
Escalada local de privilégios via condição de corrida TOCTOU no PackageKit.
CVSS 3.x : 7.8 HIGH
CWE : CWE-367 (Time-of-Check Time-of-Use)
Fix : PackageKit 1.3.5+
Affects : PackageKit 1.0.2 – 1.3.4
Escalação local de privilégios via condição de corrida TOCTOU no manipulador de transações D-Bus do PackageKit.
Qualquer usuário local sem privilégios pode instalar pacotes arbitrários como root — sem autenticação além de ter uma sessão local.
Apenas para testes autorizados em laboratório (HTB, CTF, engajamento com escopo por escrito).
python3 exploit.py
Só isso. Cria /tmp/.suid_bash e, em seguida, faz exec para um shell root.
Caminho SUID personalizado ou timeout:
SUID_BIN=/dev/shm/.sh TIMEOUT=90 python3 exploit.py
apt install python3-gi dpkg-dev
O exploit compila dois pacotes .deb em tempo de execução — nenhum compilador é necessário.
Três bugs em src/pk-transaction.c se combinam:
InstallFiles() sobrescreve cached_transaction_flags e cached_full_paths sem verificação de estado. Uma segunda chamada D-Bus na mesma transação os substitui cegamente — mesmo enquanto a transação já está em execução.
pk_transaction_set_state() rejeita transições regressivas ilegais (RUNNING→READY, etc.) sem reverter as flags sobrescritas do Bug 1. As flags corrompidas persistem.
pk_transaction_run() lê cached_transaction_flags no momento do despacho (idle do GLib), não no momento da autorização. O backend vê as flags do atacante.
Definir PK_TRANSACTION_FLAG_SIMULATE (0x4) ignora completamente a verificação de autorização do polkit. A chamada 1 é autorizada sem interação do usuário.
Atacante packagekitd
│ │
│ CreateTransaction() │ state = NEW
│─────────────────────────────────►│
│ │
│ InstallFiles(SIMULATE, dummy) │ polkit ignorado (SIMULATE)
│─────────────────────────────────►│ state = READY, idle na fila
│ │
│ InstallFiles(NONE, payload) │ flags+paths sobrescritos
│─────────────────────────────────►│ set_state(WAITING_FOR_AUTH) rejeitado
│ │ state permanece READY (BUG 2)
│ │
│ [idle dispara]│
│ │ pk_transaction_run() lê NONE (BUG 3)
│ │ → dpkg instala payload como root
│ │ → postinst: chmod +s /tmp/.suid_bash
│ │
│ execv("/tmp/.suid_bash -p") │
│─────────────────────────────────►│ euid=0(root)
Ambas as chamadas chegam antes do idle do GLib disparar — não há corrida a vencer, é determinístico.
Ubuntu Desktop 18.04–26.04, Debian Trixie, Fedora 43, RockyLinux 10.1 e qualquer distribuição que distribua PackageKit 1.0.2–1.3.4 com o daemon ativo.
systemctl mask packagekittransaction->state != PK_TRANSACTION_STATE_NEW → rejeitar