Bypass ASLR&NX – Return Oriented programming

Nous allons continuer notre série d’article sur l’exploitation de binaire par la pratique. Dans l’article precedent nous avons utilisé la ret2libc pour contrer la protection NX qui rend la stack non exécutable.
Aujourd’hui nous allons non seulement exploiter un binaire 64bits contrairement aux fois précédentes, mais aussi laisser la protection ASLR qui rend les adresses aléatoire activée. Mais comme nous désactiverons l’option PIE les adresses du code seront fixe.

Tout d’abord le changement est qu’avec un binaire 64 bits nous ne pourrons pas faire la technique classique de ret2libc car cette fois les arguments passés à la fonction le sont à travers les registres et non sur la stack.
De plus, nous laisserons l’ASLR activé sur notre machine, il n’aurait donc pas été possible d’utiliser la technique de ret2libc car nous ne pouvons pas connaître l’adresse de system ( qui changera a chaque exécution ).
Pour contourner les protections que nous avons laissé activées nous allons utiliser la technique de Return Oriented Programming, dites « ROP« .

Pour contourner les deux protections NX ( stack non exécutable ) et ALSR, il nous faut donc exécuter du code qui ne se trouve pas dans la pile car celle ci n’est pas exécutable et utiliser des adresse qui ne sont pas aléatoires.
Nous allons donc utiliser le code du programme en lui même, en récupérant l’adresse de « gadget », c’est a dire de « morceau de code » que nous enchaînerons les uns avec les autres pour faire en sorte que le programme exécute les actions voulues.

Un gadget est un bout de la code contenant des instructions suivies par une instruction RET. Lorsque nous écrasons l’adresse qui sera dans RIP (équivalent du eip mais en 64bits) nous redirigeons l’exécution sur le premier gadget : par exemple « xor rax,rax; ret ».
Une fois le xor effectué le programme passera a l’instruction RET qui est l’équivalent d’un « pop rip », ce « pop rip » va prendre la prochaine valeur sur la stack (qui sera donc l’adresse que nous avons mis après l’adresse du premier gadget) et redirigera l’exécution vers cette adresse, le code du deuxième gadget sera donc exécuté après le premier, et ainsi de suite jusqu’à enchaîner assez de gadgets pour recréer un code similaire à un shellcode pour par exemple exécuter un shell.

Passons à la pratique !

Maintenant que vous savez mieux ce qu’est le ROP, nous allons passer à la pratique.
Nous allons exploiter ce binaire :

 

Pour compiler ce binaire nous allons utiliser la commande suivante :
gcc vuln.c -o vuln -no-pie -static

le -no-pie empêche les adresses du code d’être aléatoires, seules les adresses des bibliothèques, de la stack et du heap seront soumis à l’ASLR et nous utilisons -static pour que notre binaire soit plus gros car il contient très peu de code source.
Un vrai programme serait assez gros pour contenir assez de code permettant de faire du ROP et nous n’aurions pas besoin d’inclure statiquement la libc.

Si vous voulez avoir les même adresse que moi et ne pas avoir a chercher les gadgets vous même,voici la version que j’ai utilisé pour faire cette article : binaire (md5 : cd6c41510519efba2c60d6c36cf94987 ).

Ce binaire est vulnérable a un buffer overflow sur la fonction scanf celle ci devrait être scanf("%64s",buffer); pour limiter le nombre de caractère envoyés au buffer.

Tout d’abord nous allons faire comme d’habitude, lancer le binaire sous gdb, lui envoyer un pattern ( crée à l’aide de « pattern create 100 » sous gdb-peda ) et l’envoyer dans le buffer… A l’aide de « pattern offset » nous trouvons que l’adresse de retour est écrasée à 72 caractères.
Il nous suffira donc d’envoyer 72 caractères « A » suivi de notre « ropchain ».

Pour retrouver des gadgets a utiliser dans notre « ropchain » nous pouvons utiliser ROPGadget  ou ropper

Notre « ropchain » consistera a lancer le binaire /bin/sh pour obtenir un shell. Nous ferons ça comme si nous programmions le shellcode en assembleur, nous devons donc appeler l’instruction syscall avec RAX a 59 pour le syscall execve, l’adresse de la chaîne /bin/sh dans RDI ainsi que deux chaînes NULL dans RSI et RDX (vous pouvez le vérifier ici : http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ ).

Nous allons donc écrire la chaîne de caractère /bin/sh a une adresse connue, celle de la section data par exemple :
la ropchain suivante :

 

Il nous reste plus qu’a mettre les bonnes valeurs dans les registres

Maintenant nous allons utiliser un gadget appelant syscall :
payload += p64(0x462975) # syscall ; ret

Il nous reste plus qu’a envoyer le padding de 72 caractères puis la ropchain pour récupérer un shell.
Voici le code complet de l’exploit :

Nous lançons l’exploit et nous avons un shell !

J’espère que cet article vous aura plus, n’hésitez pas à commenter ou me contacter si vous avez des questions, idées d’article ou autre , merci !

 

Add a Comment

Votre adresse de messagerie ne sera pas publiée.