
# Laboratorio Docker estilo CTF para CVE-2026-41651 (Pack2TheRoot): escalada de privilegios local mediante PackageKit permissive-polkit
Un laboratorio de escalada de privilegios local estilo CTF, dockerizado y construido en torno al aviso Pack2TheRoot (CVE-2026-41651), una vulnerabilidad de 12 años en el demonio PackageKit divulgada por el Red Team de Deutsche Telekom el 22 de abril de 2026.
El laboratorio incluye tres contenedores: un desafío CTF con polkit permisivo, un objetivo de comparación endurecido y una reproducción completa del exploit TOCTOU que compila el PackageKit vulnerable desde el código fuente y ejecuta el exploit real de la causa raíz de CVE-2026-41651.
| Contenedor | Puerto | Rol | Resultado del exploit |
|---|---|---|---|
pack2theroot-vuln | tcp/2222 (ssh) | PackageKit ≤ 1.3.4 + regla polkit permisiva | Root mediante RPM manipulado |
pack2theroot-patched | tcp/2223 (ssh) | Igual, pero con polkit endurecido | El exploit falla |
pack2theroot-exploit | — | PackageKit 1.3.4 vulnerable compilado desde el código fuente | Root mediante carrera TOCTOU en D-Bus |
Además:
docs/CHALLENGE.md)docs/HINTS.md)docs/SOLUTION.md)docs/diagrams/)| Campo | Valor |
|---|---|
| CVE | CVE-2026-41651 |
| Nombre | Pack2TheRoot |
| Clase | Escalada de privilegios local (TOCTOU / CWE-367) |
| Componente | Demonio PackageKit (pk-transaction.c) |
| Rango vulnerable | >= 1.0.2 y <= 1.3.4 (12 años de versiones) |
| Corregido en | PackageKit 1.3.5 (commit 76cfb675, publicado el 2026-04-22) |
| CVSS 3.1 | 8.8 (ALTA) — AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Precondiciones | Shell local sin privilegios en un host que ejecute PackageKit |
| Descubridor | Deutsche Telekom Security GmbH — Red Team |
pk-transaction.c| Error | Ubicación | Efecto |
|---|---|---|
| ERROR 1 | InstallFiles() (~línea 4036) | Sobrescribe cached_transaction_flags y cached_full_paths de forma incondicional — sin guarda de estado |
| ERROR 2 | pk_transaction_set_state() (~líneas 876–881) | Descarta silenciosamente las transiciones de estado hacia atrás en lugar de generar un error — las banderas ya están envenenadas |
| ERROR 3 | pk_transaction_run() (~líneas 2273–2277) | Lee cached_transaction_flags en el momento del despacho, no en el de la autorización |
| Bypass | Bandera SIMULATE (~líneas 2893–2900) | PK_TRANSACTION_FLAG_SIMULATE (0x4) omite polkit por completo |
El exploit envía dos llamadas asíncronas InstallFiles() por D-Bus en la misma transacción: primero con SIMULATE (omite polkit, pone en cola una devolución de llamada inactiva de GLib), e inmediatamente después con NONE + el paquete malicioso (sobrescribe las banderas/rutas en caché antes de que se ejecute la devolución inactiva). La prioridad de ordenación de GLib garantiza que ambos mensajes D-Bus se despachen antes de la devolución de llamada inactiva, lo que hace que esto sea determinista y no una carrera de tiempos.
IOC conocido (del aviso):
journalctl --no-pager -u packagekit | grep -iE 'emitted_finished|pk_transaction_finished_emit|pk-transaction\.c:514'
git clone https://github.com/dinosn/pack2theroot-lab.git
cd pack2theroot-lab
docker compose build
docker compose up -d
ssh labuser@localhost -p 2222 # contraseña: labuser
# luego trabaja con docs/CHALLENGE.md
La bandera está en /root/flag.txt (modo 0400 root:root). Formato:
PACK2THEROOT{...}
# Compilar y ejecutar — el modo automático ejecuta el exploit de inmediato:
docker compose run --rm exploit
# O modo manual:
docker compose run --rm --entrypoint /entrypoint.sh exploit
# Luego, dentro del contenedor:
su - victim -c /exploit/cve-2026-41651
El exploit compila dos paquetes .deb en C (sin necesidad de herramientas externas), envía la carrera de dos llamadas por D-Bus y crea un bash SUID en /tmp/.suid_bash. Consulta docs/SOLUTION.md para la guía técnica completa.
Desmontaje:
docker compose down -v
pack2theroot-lab/
├── README.md
├── docker-compose.yml
├── docs/
│ ├── CHALLENGE.md # resumen para el jugador
│ ├── HINTS.md # pistas progresivas (ambas rutas)
│ ├── SOLUTION.md # guía completa + bandera
│ ├── references.md # fuentes primarias y secundarias
│ ├── threat-model.md # supuestos atacante/defensor
│ └── diagrams/ # diagramas SVG del flujo del exploit
│ ├── 01-docker-build.svg
│ ├── 02-container-startup.svg
│ ├── 03-exploit-execution.svg
│ ├── 04-dbus-race-sequence.svg
│ ├── 05-attack-overview.svg
│ └── 06-auto-mode-full-run.svg
├── exploit/
│ ├── Dockerfile # compila PackageKit 1.3.4 vulnerable desde el código fuente
│ ├── entrypoint.sh # inicia dbus + polkitd + packagekitd
│ ├── Makefile
│ └── src/
│ └── cve-2026-41651.c # el exploit TOCTOU (PoC público)
├── vulnerable/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── policy/
│ └── 10-pack2theroot-lab-misconfig.rules # desafío CTF
├── patched/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ ├── local/ # coloca aquí un RPM de backport 1.3.5 (opcional)
│ └── policy/
│ └── 10-pack2theroot-lab-hardened.rules # la corrección
└── scripts/
├── exploit-check.sh # comprueba versión, polkit, D-Bus, IOC
├── check-version.sh # veredicto VULNERABLE vs PARCHADO
├── check-preconditions.sh # comprobación de pkcon / polkit / D-Bus
├── monitor-ioc.sh # sigue el journal en busca del IOC del aviso
├── compare-env.sh # compara ambos contenedores
└── apply-polkit-mitigation.sh # endurecimiento provisional para hosts sin parchear
El directorio docs/diagrams/ contiene diagramas SVG que muestran el flujo del exploit. Ábrelos en cualquier navegador:
| Diagrama | Descripción |
|---|---|
01-docker-build.svg | Compilación Docker en dos etapas — compilación del PackageKit vulnerable |
02-container-startup.svg | Arranque del contenedor — inicio de dbus, polkitd, packagekitd |
03-exploit-execution.svg | Salida del exploit paso a paso con fases anotadas |
04-dbus-race-sequence.svg | Diagrama de secuencia D-Bus — cómo se encadenan ERROR 1/2/3 con la prioridad de GLib |
05-attack-overview.svg | Flujo de ataque de alto nivel: atacante vs sistema con cronología |
06-auto-mode-full-run.svg | Terminal completa del modo --auto — desde el inicio del contenedor hasta la shell root |
Consulta docs/CHALLENGE.md. En resumen:
labuser (contraseña labuser).rpm-build, rpmdevtools están instalados).pkcon install-local --allow-untrusted.%post se ejecuta como root. Lee /root/flag.txt.¿Atascado? docs/HINTS.md contiene pistas cada vez más explícitas. Solo abre docs/SOLUTION.md después de haberlo resuelto o de necesitar realmente el informe.
Dentro de cualquiera de los contenedores, como cualquier usuario:
/opt/lab/check-version.sh # veredicto VULNERABLE o PARCHADO
/opt/lab/check-preconditions.sh # autocomprobación de exposición
/opt/lab/exploit-check.sh # comprobación de versión + polkit + D-Bus + IOC
/opt/lab/monitor-ioc.sh # sigue el journal en busca del IOC
Desde el host, lado a lado:
./scripts/compare-env.sh
Aplica la mitigación provisional de polkit a un contenedor vulnerable:
docker exec pack2theroot-vuln /opt/lab/apply-polkit-mitigation.sh
# Luego reintenta el exploit — ahora debería fallar.
La imagen patched gestiona el estado de la versión de PackageKit de forma honesta:
/etc/pack2theroot-banner) que indica si la versión instalada es el parche real o un marcador de posición de la distribución.Para compilar una imagen parcheada completa en cuanto tu distribución publique el backport, coloca los RPM en patched/local/ y recompila:
docker compose build --build-arg USE_LOCAL_RPM=1 patched
--privileged para ejecutar la pila completa de dbus/polkitd/packagekitd. Ejecútalo solo en entornos aislados.labuser) es intencionalmente débil. Nunca la reutilices.Fuentes primarias:
CVE-2026-41651:
https://nvd.nist.gov/vuln/detail/CVE-2026-4165176cfb675:
https://github.com/PackageKit/PackageKit/commit/76cfb675fb31acc3ad5595d4380bfff56d2a8697Consulta docs/references.md para cobertura secundaria y rastreadores de distribución.
MIT — consulta LICENSE.