Les bug de format, plus communéments appelés formats strings, sont connus depuis un petit bout de temps (aux environs de 1996), mais ont commencé à être populaires à la mi 2000. En fait, les formats strings et les buffers overflow sont des failles similaires puisqu'elles sont dues à des erreurs de programmation (ou plutôt de la programmation faite par des programmeurs feignants).
Les formats strings surgissent dans des erreurs de programmation subtiles, dans la famille des fonctions d'affichage comme printf(); et sprintf();.
Un attaquant peut donc tirer profit de ceci en passant les chaines de caractères soigneusement sélectionnées et contenant des commandes arbitraires qui peuvent être exécutées par l'ordinateur de cible. Cela peut, comme vous l'imaginez, engendrer d'énormes risques pour le système ciblé, surtout si le système tourne en privilège de root. De toute façon, la plupart des attaques sont focalisées sur des programmes utilisés par le root pour escalader les privilèges plus facilement et rapidement.
Les formats strings sont très utiles s'ils sont utilisés correctement puisqu'ils permettent d'afficher, sur l'écran de l'utilisateur, des variables du script. En effet, printf() scanne la chaine de caractère passée en cherchant un %: s'il y en a un, l'argument est affiché (revoyez vos cours de C si vous ne voyez pas de quoi je parle =) ).
Elle peux servir lorsque le programmeur utilise une chaine entrée par un utilisateur, voire par l'utilisateur qui a lancé le programme, c'est à dire l'utilisateur qui est sur la machine cible. Nous allons voir un exemple récapitulatif plus loin.
Vous pouvez aussi, rapellez-vous, utiliser une option de printf() qui peut être intéressante et qui risque de causer un buffers overflow: %n. En effet, cette option permet d'écrire dans la mémoire allouée au programme avant d'afficher le contenu de la variable. Imaginez donc que vous utilisiez %n plus une chaine de plus de 255 caractères, et que la variable soit un simple unsigned char. Vous provoquerez alors un BoF via un format string.
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[2048] = {0};
// nous avons ici un char signé, et rien ne vérifie pour un buffer overflow ;-)
strncpy(buf, argv[1], sizeof(buf) - 1);
printf(buf);
putchar('\n');
return(0);
}
Compilons et exécutons:
[n-0-x@localhost c]$ gcc -Wall -o format_string.o format_string.c
[n-0-x@localhost c]$
[n-0-x@localhost c]$ ./format_string.o %i
-1073744148
[n-0-x@localhost c]$ ./format_string.o %n
[n-0-x@localhost c]$ ./format_string.o %s
LESSKEY=/etc/.less
[n-0-x@localhost c]$ ./format_string.o DDDD%x%n
Segmentation fault
[n-0-X@localhost c]$ ./format_string.o %d
-1073744148
[n-0-x@localhost c]$ ./format_string.o DDDD%x%x
DDDDbffff6ec0
[n-0-x@localhost c]$ ./format_string.o DDDD%x%n
Segmentation fault
[n-0-x@localhost c]$