
Prueba de concepto del exploit para CVE-2026-41651, una escalada de privilegios local TOCTOU en PackageKit, con análisis técnico, lógica de detección y guía de remediación.
Clasificación: Artefacto de Evaluación de Purple Team
Solo uso autorizado. Este documento y el script de prueba adjunto están destinados exclusivamente a la validación de seguridad interna en sistemas donde se haya obtenido autorización por escrito.

| Campo | Valor |
|---|---|
| ID CVE | CVE-2026-41651 |
| CWE | CWE-367 — Condición de Carrera Time-of-check Time-of-use (TOCTOU) |
| Componente | PackageKit (packagekitd, servicio D-Bus) |
| Afectado | PackageKit ≤ 1.3.4 |
| Corregido en | PackageKit 1.3.5 |
| Impacto | Escalada de Privilegios Local → root |
| Vector de Ataque | Local / D-Bus (sesión de usuario sin privilegios) |
| Divulgado | 2026-04-22 (Deutsche Telekom Red Team) |
| Aviso | GHSA-f55j-vvr9-69xv |
| Commit de corrección | 76cfb675fb31acc3ad5595d4380bfff56d2a8697 |
PackageKit es una capa de abstracción D-Bus para la gestión de paquetes del sistema, presente por defecto en escritorios basados en GNOME en Debian, Ubuntu, Fedora, RHEL, SUSE y Arch Linux. Debido a que media operaciones privilegiadas de paquetes en nombre de usuarios sin privilegios, una falla en su flujo de autorización tiene impacto de root a nivel de sistema.
pk-transaction.c (anterior a 1.3.5) no aplicaba una protección de estado en la re-invocación de métodos de acción. Un cliente D-Bus podía llamar a InstallFiles (u otros métodos de acción) múltiples veces en el mismo objeto de transacción después de que ya había transitado fuera de PK_TRANSACTION_STATE_NEW.
Atacante (sin privilegios)
│
├─① CreateTransaction() → PackageKit devuelve la ruta del objeto de transacción (tid)
│
├─② InstallFiles(tid, FLAG_SIMULATE=4, [dummy.pkg])
│ PackageKit pone en cola una verificación de autorización polkit para dummy.pkg.
│ Aún no ocurre ninguna instalación — SIMULATE significa solo prueba en seco.
│
├─③ InstallFiles(tid, FLAG_NONE=0, [payload.pkg]) ← ventana TOCTOU
│ Re-invoca en el mismo tid antes de que se resuelva la autorización.
│ Las versiones vulnerables sobrescriben los parámetros en cola con payload.pkg.
│
└─④ polkit concede la autorización (usuario aprobó o auto-autorizado)
packagekitd instala payload.pkg como root.
script postinst/post de payload: install -m 4755 /bin/bash /tmp/.suid_bash
El atacante ejecuta /tmp/.suid_bash -p → shell root.
Los pasos ② y ③ se envían como llamadas D-Bus asíncronas no bloqueantes en la misma conexión y se vacían en una sola escritura. Los dos mensajes llegan a packagekitd antes de que pueda procesar ② y avanzar la máquina de estados, dejando abierta la ventana TOCTOU. La corrección en 1.3.5 añade una verificación de estado explícita que devuelve PK_TRANSACTION_ERROR_INVALID_STATE en cualquier re-invocación después de PK_TRANSACTION_STATE_NEW.
Archivo: cve-2026-41651-purpleteam.py
Lenguaje: Python 3
Dependencias: python3-gi (introspección GObject / enlaces GLib/Gio)
Demuestra la explotabilidad de CVE-2026-41651 en un sistema de prueba preparado con los siguientes fines:
| Fase | Acción |
|---|---|
| Configuración | Sondea /proc/mounts para seleccionar un directorio de descarga con SUID/capaz de ejecución, establece umask(022), aplica chmod explícito en todos los artefactos de compilación y luego compila un paquete dummy y uno payload en /tmp |
| Explotación | Abre una conexión D-Bus del sistema, crea una transacción PackageKit y dispara la carrera de dos llamadas |
| Payload | El script post-instalación del paquete copia /bin/bash a <drop_dir>/.suid_bash con modo 04755, propietario root. El directorio de descarga se resuelve en tiempo de ejecución (ver Soporte para Entornos Endurecidos) |
| Escalada | Sondea el binario SUID (tiempo de espera de 90 s) y luego ejecuta execl en él con -p para obtener un shell root |
| Limpieza | Elimina los archivos temporales .deb/.rpm al salir (éxito o fallo) |
Los sistemas endurecidos por defecto pueden bloquear la explotación en dos puntos independientes. El script maneja ambos automáticamente.
027 / 077)Un umask heredado por el proceso de 027 o 077 hace que mkdir() produzca directorios 750 o 700. Tanto dpkg-deb como rpmbuild deben recorrer todo el árbol de compilación — si algún directorio no es legible, la compilación falla silenciosamente.
Corrección aplicada: os.umask(0o022) se llama al inicio antes de que se cree cualquier archivo o directorio. Además, cada directorio y archivo en el árbol de compilación recibe un chmod explícito inmediatamente después de su creación (0o755 para directorios y scripts, 0o644 para archivos de datos), de modo que los permisos correctos estén garantizados independientemente del umask heredado.
nosuid / noexec en /tmp| Bandera | Efecto en la explotación |
|---|---|
nosuid | El kernel elimina silenciosamente el bit SUID de cualquier archivo almacenado en ese sistema de archivos — el bash copiado nunca se convierte en root |
noexec | El binario SUID no puede ejecutarse en absoluto |