
Auditeur de fonctions libc à l'exécution qui détecte les conditions de concurrence d'accès aux fichiers et les vulnérabilités de liens symboliques en interceptant les appels système du système de fichiers via LD_PRELOAD, enregistrant les chemins potentiels d'escalade de privilèges.
PathAuditor est un outil conçu pour trouver des vulnérabilités liées à l'accès aux fichiers en auditant les fonctions libc.
L'idée est approximativement la suivante :
Nous utilisons LD_PRELOAD pour intercepter tous les appels de bibliothèque liés au système de fichiers et journaliser toute violation rencontrée dans syslog.
Ce n'est pas un produit officiellement supporté par Google.
Regardons un exemple du type de vulnérabilité que cet outil peut détecter. CVE-2019-3461 était un bogue dans tmpreaper, un outil qui parcourt /tmp et supprime les fichiers anciens. Il est généralement exécuté comme une tâche cron en tant que root. Comme il ne veut pas supprimer les fichiers en dehors de tmp, il utilisait le code suivant pour vérifier si un répertoire est un point de montage :
if (S_ISDIR (sb.st_mode)) {
char *dst;
if ((dst = malloc(strlen(ent->d_name) + 3)) == NULL)
message (LOG_FATAL, "malloc failed.\n");
strcpy(dst, ent->d_name);
strcat(dst, "/X");
rename(ent->d_name, dst);
if (errno == EXDEV) {
free(dst);
message (LOG_VERBOSE,
"File on different device skipped: `%s/%s'\n",
dirname, ent->d_name);
continue;
}
// [...]
En résumé, ce code appelle rename("/tmp/foo", "/tmp/foo/x") qui retournera EXDEV si "/tmp/foo" est un point de montage.
PathAuditor signalerait cet appel comme une vulnérabilité potentielle si "/tmp/foo" appartient à un utilisateur autre que root.
Pour comprendre pourquoi, nous devons penser à ce qui se passe dans le noyau lorsque l'appel système rename est exécuté (simplifié) :
"/tmp/foo" pour le premier argument."/tmp/foo/x" pour le second argument.Il y a une condition de course ici car "/tmp/foo" sera résolu deux fois. S'il est contrôlé par l'utilisateur, celui-ci peut le remplacer par un fichier différent à tout moment.
En particulier, nous voulons que "/tmp/foo" soit d'abord un répertoire pour passer la vérification if(S_ISDIR) dans le code de tmpreaper. Nous le remplaçons ensuite par un fichier juste avant que le code n'entre dans l'appel système. Lorsque le noyau résout le premier argument, il verra un fichier avec un contenu contrôlé par l'utilisateur.
Maintenant, nous le remplaçons à nouveau, cette fois par un lien symbolique vers un répertoire arbitraire sur le même système de fichiers. Le noyau résoudra le chemin une seconde fois, suivra le lien symbolique et déplacera le fichier contrôlé vers un répertoire choisi.
La restriction du même système de fichiers est due au fait que rename ne fonctionne pas entre les systèmes de fichiers. Mais sur certaines distributions Linux, /tmp est juste un dossier sur le rootfs par défaut et vous pourriez utiliser ce bogue pour déplacer un fichier vers /etc/cron, qui sera exécuté en tant que root.
Pour l'essayer, vous devez construire libpath_auditor.so avec bazel et le charger dans un binaire en utilisant LD_PRELOAD. Toute violation sera journalisée dans syslog, assurez-vous donc qu'il est en cours d'exécution.
bazel build //pathauditor/libc:libpath_auditor.so
LD_PRELOAD=/path/to/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
tail /var/log/syslog
Il est également possible de l'exécuter sur tous les processus du système en l'ajoutant à /etc/ld.so.preload. Cependant, sachez que cela n'est recommandé que sur des systèmes de test car cela peut entraîner une instabilité.
Pour démarrer rapidement, vous pouvez essayer le conteneur docker fourni avec ce projet :
docker build -t pathauditor-example .
docker run -it pathauditor-example
# LD_PRELOAD=/pathauditor/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
# cat /var/log/syslog