Archives du blog

mercredi 27 juillet 2011

Shellcoding

Je me suis intéressé récemment à la création de shellcodes,

Un shellcode c'est une instruction en code machine confectionnée dans le but (en général) de se faire exécuter par un programme faillible dans le but de s'augmenter en privilèges, le challenge que ça représente tient en deux choses :

-Éviter un code trop grand (qui ne laisse pas de place, lors de l'exploitation d'un buffer overflow classique à l'écrasement du pointeur d'instruction par une adresse de retour)

-Le coder de manière a n'obtenir aucun null byte (\x00) puisqu'une fois exécuté ils sont interprétés comme une fin de chaîne.

Exemple sous linux avec hello :
On récupère en premier lieu le numéro du syscall qui correspond à WRITE() , cette fonction est en fait appelée nativement par des fonctions d'affichages plus connues comme puts ou printf, je me demande aussi si elles n'agissent pas comme des wrappers au final.
Ici le numéro du syscall pour WRITE() est 4.
Ensuite on va appeler EXIT() qui a pour numéro le 1.
main:
xorl %eax,%eax
xorl %ebx,%ebx        
xorl %ecx,%ecx
xorl %edx,%edx
movb $0x4,%al              //syscall pour write
movb $0x1,%bl             //STDOUT notre console
call afficher
push $0x6f6c6c65         //Hello world en little endian
push $0x00000068

afficher:
popl %ecx                     //On fait remonter la chaîne
movb $0x5,%dl            //Nombre de caractères dans notre chaîne
int $0x80                       //Interruption, exécution du syscall
xorl %eax,%eax
movb $0x1,%al            //syscall pour exit
xorl %ebx,%ebx
int $0x80                       //Interruption,exécution du syscall
Une fois compilé :

xtceb@xtceb-VirtualBox:~$ ./helloworld
hello

Ce qui donne en code machine :
helloxtceb@xtceb-VirtualBox:~$ objdump -d helloworld
helloworld:     file format elf32-i386

Disassembly of section .text:

08048054 <main>:
 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:    e8 07 00 00 00           call   804806c <afficher>
 8048065:    68 65 6c 6c 6f           push   $0x6f6c6c65
 804806a:    6a 68                    push   $0x68

0804806c <afficher>:
 804806c:    59                       pop    %ecx
 804806d:    b2 05                    mov    $0x5,%dl
 804806f:    cd 80                    int    $0x80
 8048071:    31 c0                    xor    %eax,%eax
 8048073:    b0 01                    mov    $0x1,%al
 8048075:    31 db                    xor    %ebx,%ebx
 8048077:    cd 80                    int    $0x80
Petit problème à l'appel de "afficher" on remarque que le call nous donne des zéros de terminaisons...
Il va falloir trouver une autre façon de coder notre hello :

main:
xorl %eax,%eax
xorl %ebx,%ebx
xorl %ecx,%ecx
xorl %edx,%edx
jmp message
fin:
movb $0x4,%al
movb $0x1,%bl
popl %ecx
movb $0x5,%dl
int $0x80
xorl %eax,%eax
movb $0x1,%al
xorl %ebx,%ebx
int $0x80
message:
call fin
.string "hello"
 Avec objdump :


xtceb@xtceb-VirtualBox:~$ objdump -d helloworld
helloworld:     file format elf32-i386
Disassembly of section .text:

08048054 <main>:
 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:    eb 11                    jmp    804806f <message>

0804805e <fin>:
 804805e:    b0 04                    mov    $0x4,%al
 8048060:    b3 01                    mov    $0x1,%bl
 8048062:    59                       pop    %ecx
 8048063:    b2 05                    mov    $0x5,%dl
 8048065:    cd 80                    int    $0x80
 8048067:    31 c0                    xor    %eax,%eax
 8048069:    b0 01                    mov    $0x1,%al
 804806b:    31 db                    xor    %ebx,%ebx
 804806d:    cd 80                    int    $0x80

0804806f <message>:
 804806f:    e8 ea ff ff ff           call   804805e <fin>
 8048074:    68 65 6c 6c 6f           push   $0x6f6c6c65
Hop, il nous reste plus qu'à rassembler nos petites instructions machine :

\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xeb\x11\xb0\x04\xb3\x01\x59\xb2\x05\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xea\xff\xff\xff\x68\x65\x6c\x6c\x6f

On peut dès à présent le tester pour voir si il marche :

char shellcode[]="\x31\xc0\x31\xdb\x31\xc9"
        "\x31\xd2\xeb\x11\xb0\x04"
        "\xb3\x01\x59\xb2\x05\xcd"
        "\x80\x31\xc0\xb0\x01\x31"
        "\xdb\xcd\x80\xe8\xea\xff"
        "\xff\xff\x68\x65\x6c\x6c\x6f";
int main()
{
    int (*func)();func = (int (*)()) shellcode;(int)(*func)();
}


xtceb@xtceb-VirtualBox:~$ ./shellcode
helloxtceb@xtceb-VirtualBox:~$

1 commentaire: