En cherchant des failles de sécurité, j'ai réalisé que je n'avais jamais pensé à Gettext (bibliothèque de traduction). Or la majorité des applications Linux l'utilisent. Gettext utilise un ensemble de fichier portant l'extension « .mo » (un fichier par langue) : les variables d'environement (LANGUAGE, LC_ALL et LANG) indiquant lequel choisir.

Réussite du fuzzing

J'ai tenté de fuzzer un fichier .mo et je suis rapidement arrivé à faire planter mon programme de test. J'ai alors contacté l'auteur de gettext, l'allemand Bruno Haible, qui m'a répondu en moins d'une heure ! Résumé de sa réponse : « This is known: The gettext routines (...) don't verify the integrity of .mo files. (...) Such a verification would not serve the purpose of a maximally efficient lookup of translations ». Traduction libre : « Le problème est connu, les routines gettext ne vérifient pas l'intégrité des fichiers .mo. Une telle vérification serait en contradiction avec le principe d'efficacité (vitesse) maximale de la traduction. »

Je peux comprendre ses raisons mais je reste perplexe. De plus, il ajoute « It is the duty of the distribution or system manager to ensure that the directories containing .mo files (...) are not world- nor group- writable. » que je traduis « C'est la responsabilité de la distribution [Linux] ou l'administraeur système de s'assurer que les dossiers contenant des fichiers .mo ne sont pas modifiables par les utilisateurs ».

Échec sur un programme suid

Je me suis alors mis en tête de lui prouver qu'il avait tord et que ses bugs pouvaient mettre à mal la politique de sécurité. J'ai d'abord réussi à utiliser un fichier .mo arbitraire avec une astuce : en donnant la valeur « ../../../../../tmp » à la variable d'environnement LANGUAGE, gettext va chercher le fichier mo dans « /tmp/LC_MESSAGES/ ». J'ai alors testé sur un programme suid (ie. lancé avec les droits d'administrateur) et là : ça ne marchait plus.... bizzare. J'ai creusé Internet avec Google et je suis tombé des articles montrant que des vulnérabilités avaient déjà été trouvées en 2000 et en 2003 au sujet de gettext : soucis avec la variable NLSPATH et soucis avec la variable LANG. J'avais déjà lu des informations à ce sujet en tombant sur la liste des variables d'environnement de la glibc qui énumère les variables proscrites pour un programme suid.

Raison de l'échec

Tétu comme un âne, je me suis mis à lire le code source de la glibc et je suis retombé sur NLSPATH : la constante UNSECURE_ENVVARS (définie dans sysdeps/generic/unsecvars.h) contient les variables interdites pour un programme suid. Mais nul part je ne lis LANGUAGE, LC_ALL ou LANG. D'ailleurs, je réalise que ces variables d'environnement ne sont pas supprimées pour un programme suid. Finalement, je réalise que c'est gettext qui possède une protection ! Bruno Haible avait oublié de m'en parler ;-) Dans intl/dcigettext.c, si __libc_enable_secure vaut 1 (cas d'un programme suid) les locales contenant le caractère « / » sont proscites. Et bien voilà, tout simplement !

Conclusion

Fausse alerte, gettext est troué mais ce n'est pas (trop) grave :-) D'autres personnes avaient constatées le problème avant moi.

Néanmois, la sécurité reste minimale : elle consiste à bloquer les attaques injectant un dossier dans une variable d'environnement. Et encore, uniquement pour les programmes suid. Les autres programmes sont donc vulnérables.