
Escalada local de privilegios mediante una condición de carrera TOCTOU en el de 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
Escalada local de privilegios mediante una condición de carrera TOCTOU en el manejador de transacciones D-Bus de PackageKit.
Cualquier usuario local sin privilegios puede instalar paquetes arbitrarios como root, sin más autenticación que disponer de una sesión local.
Solo laboratorio / pruebas autorizadas (HTB, CTF, engagement con alcance por escrito).
python3 exploit.py
Eso es todo. Suelta /tmp/.suid_bash y luego hace exec a una shell root.
Ruta SUID personalizada o tiempo de espera:
SUID_BIN=/dev/shm/.sh TIMEOUT=90 python3 exploit.py
apt install python3-gi dpkg-dev
El exploit construye dos paquetes .deb en tiempo de ejecución; no se necesita compilador.
Tres fallos en src/pk-transaction.c que se encadenan:
InstallFiles() sobrescribe cached_transaction_flags y cached_full_paths sin comprobar el estado. Una segunda llamada D-Bus sobre la misma transacción los reemplaza a ciegas, incluso mientras la transacción ya se está ejecutando.
pk_transaction_set_state() rechaza transiciones regresivas ilegales (RUNNING→READY, etc.) sin revertir los flags sobrescritos del Fallo 1. Los flags corruptos persisten.
pk_transaction_run() lee cached_transaction_flags en el momento del despacho (idle de GLib), no en el momento de la autorización. El backend ve los flags del atacante.
Establecer PK_TRANSACTION_FLAG_SIMULATE (0x4) omite por completo la comprobación de autorización de polkit. La llamada 1 se autoriza sin interacción del usuario.
Atacante packagekitd
│ │
│ CreateTransaction() │ state = NEW
│─────────────────────────────────►│
│ │
│ InstallFiles(SIMULATE, dummy) │ polkit omitido (SIMULATE)
│─────────────────────────────────►│ state = READY, idle encolado
│ │
│ InstallFiles(NONE, payload) │ flags + rutas sobrescritos
│─────────────────────────────────►│ set_state(WAITING_FOR_AUTH) rechazado
│ │ state sigue en READY (FALLO 2)
│ │
│ [dispara idle]│
│ │ pk_transaction_run() lee NONE (FALLO 3)
│ │ → dpkg instala el payload como root
│ │ → postinst: chmod +s /tmp/.suid_bash
│ │
│ execv("/tmp/.suid_bash -p") │
│─────────────────────────────────►│ euid=0(root)
Ambas llamadas llegan antes de que dispare el idle de GLib: no hay carrera que ganar, es determinista.
Ubuntu Desktop 18.04–26.04, Debian Trixie, Fedora 43, RockyLinux 10.1 y cualquier distribución que incluya PackageKit 1.0.2–1.3.4 con el daemon activo.
systemctl mask packagekittransaction->state != PK_TRANSACTION_STATE_NEW → rechazar