Les vulnérabilités de type " Format string "
Date : 26 Juillet 2005
Les vulnérabilités de type " Format string "
Récemment, un nouveau type de vulnérabilité impactant les systèmes UNIX a été identifié. Depuis sa découverte en juillet, cette vulnérabilité, appelée "format string vulnerability", a pris une ampleur comparable aux vulnérabilités de type "débordement de pile", avec lesquelles elle est quelquefois confondue.
Principe de la vulnérabilité
La fonction "printf()"est probablement l'une des plus utilisées en C. Elle permet simplement d'afficher un message formaté et s'utilise avec la syntaxe :
printf( format, var1, var2, ... ,varn )
Lorsqu’elle s’exécute, la fonction "printf()" interprète le premier paramètre comme la description d’un format d’impression (contenant un texte fixe et des marqueurs comme "%s", "%d", etc…), et remplace les marqueurs rencontrés dans ce format par la valeur des paramètres suivants passés à la fonction. Dans sa forme la plus simple, l'affichage d'une chaîne de caractères se fait ainsi en utilisant l'instruction :
printf("%s", chaine)
Cependant cette instruction peut éventuellement être simplifiée en :
printf(chaine)
Les programmes utilisant cette forme simplifiée peuvent cependant avoir un comportement bizarre, si la variable "chaîne" contient par accident un marqueur comme "%s". Dans ce cas en effet "printf()" cherche à remplacer ce marqueur par la valeur du paramètre suivant dans ses paramètres d'appel. Comme ce paramètre n'existe pas, la fonction "printf()"utilise, par erreur, la valeur immédiatement suivante trouvée dans la pile d'exécution.
Tout programme utilisant une instruction de la forme "printf(c)" peut donc être détourné de son fonctionnement nominal si la variable "c" est fournie par l'utilisateur.
En choisissant de façon judicieuse la chaîne "c" l'utilisateur peut en fait agir de façon significative sur le fonctionnement du programme.
Remarque : Il est important de noter qu’il ne s’agit pas ici du même problème que le débordement de buffer qui peut être causé par une instruction "sprintf()" si la chaîne résultat est mal dimensionnée.
Lecture de la mémoire
Si la chaîne "c" contient une suite de marqueurs de type "%h", le contenu de la pile d'exécution du programme peut par exemple être visualisé (sur une taille correspondant au nombre de marqueurs "%h" employés). En utilisant une chaîne "c" plus sophistiquée, il est même possible de consulter le contenu d'une adresse quelconque de l'espace d'adressage du processus.
Ecriture dans la mémoire
Il existe un marqueur "%n" peu connu permettant dans un "printf()" d'affecter à l'une des variables passées en paramètre la longueur du message produit par "printf()". Alors que jusqu'à présent les marqueurs utilisés permettaient simplement l'affichage de valeurs, le marqueur "%n" permet donc cette fois l'écriture d'une valeur. Tout comme dans le cas de la lecture d'une zone mémoire arbitraire, l'utilisation habile du marqueur "%n" permet en fait l'écriture d'une valeur arbitraire à un emplacement quelconque de l'espace d'adressage du processus.
Détournement d'un programme
La vulnérabilité "format string" permet donc de lire ou d'écrire dans une zone arbitraire d'un processus en cours d'exécution (un peu comme les instructions "peek" et "poke" dont les anciens programmeurs en basic se souviendront sans doute !).
Les utilisations possibles sont nombreuses :
- lancement d'un code arbitraire en écrasant l'adresse de retour d'une fonction dans la pile (attaque comparable à un débordement de pile),
- changement d'une valeur en mémoire, comme par exemple un "uid" sauvé qui sera ensuite utilisé dans la suite normale de l'exécution du programme,
- changement de la valeur d'une chaîne de caractères, comme par exemple le nom d'une commande qui est ensuite invoquée par le programme,
- modification de l'adresse de fonctions appelées (par exemple pointeur vers la fonction "exit()"),
- etc...
Dans tous les cas, le but recherché sera de détourner le fonctionnement d'une commande ou d'un processus ayant de forts privilèges (programme "suid" root ou démons lancés sous l'identité root).
Les programmes vulnérables
Depuis sa découverte, cette vulnérabilité a donné lieu à un nombre important d'attaques, dont :
- la vulnérabilité WuFtpd 2.6.1 (CERT-IST/AV-2000.149)
- Vulnérabilité PHP (CERT-IST/AV-2000.254)
- Vulnérabilité traceroute (CERT-IST/AV-2000.233)
- Vulnérabilité " usermode " (CERT-IST/AV-2000.245)
- Lpr (CERT-IST/AV-2000.240)
- Scohelp (CERT-IST/AV-2000.262)
- LPRng (CERT-IST/AV-2000.229)
- Klogd (CERT-IST/AV-2000.223)
- Rpc.statd (CERT-IST/AV-2000.165)
- IRIX telnetd (CERT-IST/AV-2000.186)
- Fonction de localisation (CERT-IST/AV-2000.207)
- chpass / pw_error (CERT-IST/AV-2000.238)
En fait, la plupart des vulnérabilités UNIX actuellement publiées par le CERT-IST sont liées à ce problème. Ceci est d'autant plus vrai que :
- certaines fonctions de la librairie C standard de certains UNIX sont vulnérables ("pw_error()" par exemple sur OpenBSD),
- les fonctions de localisation (fonctions chargées d'afficher les messages dans la langue du pays) de certains UNIX sont vulnérables.
Ce dernier cas est problématique à corriger. En effet pour produire un message adapté au pays considéré, certains UNIX utilisent des fichiers de localisation incluant le format qui sera utilisé lors du "printf()". La modification du fichier de localisation (ou l'utilisation d'un fichier de localisation "personnel") permet donc de modifier le format utilisé et de détourner tous les programmes s'appuyant sur ces fonctions de localisation.
Le problème est donc réellement d'ampleur. Lorsque le CERT-IST a pris conscience de ce fait, des courriers officiels ont été émis vers les structures d'alerte des constructeurs directement visés par ces attaques. L'objet de ces courriers n'est pas de les informer de l'existence de la vulnérabilité (les constructeurs sont supposés déjà avoir connaissance du problème), mais plutôt d'insister sur le fait que leurs clients (et les clients du CERT-IST) souhaitent que tous les efforts soient mis en œuvre pour résoudre ce problème. Bien que nos courrier aient été accueillis favorablement, il n'a à ce jour pas été diffusé par ces constructeurs de correctif traitant les "problèmes de fond".
Des solutions ?
Tout comme dans le cas des attaques par débordement de pile, cette vulnérabilité va nécessiter un travail important d'analyse et de correction du code existant, de façon à supprimer toutes les utilisations dangereuses de la fonction "printf()". Cette fonction n'est cependant pas la seule à être incriminée. De façon plus générale, toutes les fonctions admettant un nombre variable d'arguments (cf "vararg()") sont également potentiellement vulnérables et doivent être analysées.
Bien évidemment, ce processus de revue du code existant sera long, et il est naturel de prévoir que d'ici là un nombre significatif de programmes d'exploitation de cette vulnérabilité seront diffusés pour attaquer différentes commandes privilégiées. Durant cette période il est donc particulièrement important d'être attentif à ces vulnérabilités et d'appliquer au plus tôt les correctifs publiés.
Informations complémentaires
[1] "Format String Attacks"
Une explication détaillée du problème "printf(txt)".
http://www.guardent.com/docs/FormatString.PDF
[2] "[Paper] Format bugs"
Première article Bugtraq décrivant de façon détaillée la vulnérabilité.
http://www.securityfocus.com/archive/1/70552
[3] "Exploiting Libc Locale Subsystem Format String vulnerability on Solaris/SPARC"
Explication pas à pas pour réaliser une attaque "Format String" sur Solaris. http://www.securiteam.com/unixfocus/Exploiting_Libc_Locale_Subsystem_Format_String_vulnerability_on_Solaris_SPARC.html