
CTF-style Docker lab pour CVE-2026-41651 (Pack2TheRoot) : élévation locale de privilèges via PackageKit permissive-polkit
Un laboratoire Dockerisé d'élévation de privilèges locaux de type CTF, conçu autour de l'avis de sécurité Pack2TheRoot (CVE-2026-41651), une faille vieille de 12 ans dans le démon PackageKit, divulguée par l'équipe Red Team de Deutsche Telekom le 22 avril 2026.
Le laboratoire comprend trois conteneurs : un défi CTF avec polkit permissif, une cible de comparaison durcie, et une reproduction complète de l'exploit TOCTOU qui compile PackageKit vulnérable depuis les sources et exécute l'exploit réel de la cause racine de CVE-2026-41651.
| Conteneur | Port | Rôle | Résultat de l'exploit |
|---|
pack2theroot-vuln | tcp/2222 (ssh) | PackageKit ≤ 1.3.4 + règle polkit permissive | Root via RPM malveillant |
pack2theroot-patched | tcp/2223 (ssh) | Identique, mais polkit durci | L'exploit échoue |
pack2theroot-exploit | — | PackageKit 1.3.4 vulnérable compilé depuis les sources | Root via course D-Bus TOCTOU |
En plus :
docs/CHALLENGE.md)docs/HINTS.md)docs/SOLUTION.md)docs/diagrams/)| Champ | Valeur |
|---|---|
| CVE | CVE-2026-41651 |
| Nom | Pack2TheRoot |
| Classe | Élévation de privilèges locale (TOCTOU / CWE-367) |
| Composant | Démon PackageKit (pk-transaction.c) |
| Plage vulnérable | >= 1.0.2 et <= 1.3.4 (12 ans de versions) |
| Corrigé dans | PackageKit 1.3.5 (commit 76cfb675, publié le 2026-04-22) |
| CVSS 3.1 | 8.8 (ÉLEVÉE) — AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Pré-conditions | Shell local non privilégié sur un hôte exécutant PackageKit |
| Découvreur | Deutsche Telekom Security GmbH — Red Team |
pk-transaction.c| Bug | Emplacement | Effet |
|---|---|---|
| BUG 1 | InstallFiles() (~ligne 4036) | Écrase cached_transaction_flags et cached_full_paths sans condition — aucun garde-fou d'état |
| BUG 2 | pk_transaction_set_state() (~lignes 876–881) | Ignore silencieusement les transitions d'état rétrogrades au lieu de générer une erreur — les drapeaux sont déjà empoisonnés |
| BUG 3 | pk_transaction_run() (~lignes 2273–2277) | Lit cached_transaction_flags au moment de la répartition, et non au moment de l'autorisation |
| Contournement | Drapeau SIMULATE (~lignes 2893–2900) | PK_TRANSACTION_FLAG_SIMULATE (0x4) contourne entièrement polkit |
L'exploit envoie deux appels D-Bus asynchrones InstallFiles() sur la même transaction : d'abord avec SIMULATE (contourne polkit, met en file un rappel GLib idle), puis immédiatement avec NONE + le paquet malveillant (écrase les drapeaux/chemins en cache avant que l'idle ne se déclenche). L'ordonnancement par priorité de GLib garantit que les deux messages D-Bus sont répartis avant le rappel idle — ce qui rend l'opération déterministe, et non une course temporelle.
IOC connu (issu de l'avis de sécurité) :
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 # mot de passe : labuser
# puis suivez docs/CHALLENGE.md
Le drapeau se trouve dans /root/flag.txt (mode 0400 root:root). Format :
PACK2THEROOT{...}
# Construction et exécution — le mode automatique exécute l'exploit immédiatement :
docker compose run --rm exploit
# Ou mode manuel :
docker compose run --rm --entrypoint /entrypoint.sh exploit
# Puis à l'intérieur du conteneur :
su - victim -c /exploit/cve-2026-41651
L'exploit construit deux paquets .deb en C (aucun outil externe requis),
envoie la course D-Bus à deux appels, et crée un bash SUID dans
/tmp/.suid_bash. Voir docs/SOLUTION.md pour la
procédure technique complète.
Arrêt :
docker compose down -v
pack2theroot-lab/
├── README.md
├── docker-compose.yml
├── docs/
│ ├── CHALLENGE.md # brief du joueur
│ ├── HINTS.md # indices progressifs (les deux chemins)
│ ├── SOLUTION.md # solution complète + drapeau
│ ├── references.md # sources primaires et secondaires
│ ├── threat-model.md # hypothèses attaquant/défenseur
│ └── diagrams/ # schémas SVG du flux de l'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 # compile PackageKit 1.3.4 vulnérable depuis les sources
│ ├── entrypoint.sh # démarre dbus + polkitd + packagekitd
│ ├── Makefile
│ └── src/
│ └── cve-2026-41651.c # l'exploit TOCTOU (PoC public)
├── vulnerable/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ └── policy/
│ └── 10-pack2theroot-lab-misconfig.rules # défi CTF
├── patched/
│ ├── Dockerfile
│ ├── entrypoint.sh
│ ├── local/ # déposez ici un RPM de rétroportage 1.3.5 (facultatif)
│ └── policy/
│ └── 10-pack2theroot-lab-hardened.rules # le correctif
└── scripts/
├── exploit-check.sh # vérifie version, polkit, D-Bus, IOC
├── check-version.sh # verdict VULNÉRABLE vs CORRIGÉ
├── check-preconditions.sh # vérification de cohérence pkcon / polkit / D-Bus
├── monitor-ioc.sh # suit le journal pour l'IOC de l'avis
├── compare-env.sh # diff entre les deux conteneurs
└── apply-polkit-mitigation.sh # durcissement provisoire pour les hôtes non corrigés
Le répertoire docs/diagrams/ contient des schémas SVG
montrant le flux de l'exploit. Ouvrez-les dans n'importe quel navigateur :
| Schéma | Description |
|---|---|
01-docker-build.svg | Construction Docker en deux étapes — compilation de PackageKit vulnérable |
02-container-startup.svg | Lancement du conteneur — démarrage de dbus, polkitd, packagekitd |
03-exploit-execution.svg | Sortie de l'exploit étape par étape avec phases annotées |
04-dbus-race-sequence.svg | Schéma de séquence D-Bus — comment BUG 1/2/3 s'enchaînent avec la priorité GLib |
05-attack-overview.svg | Flux d'attaque de haut niveau : attaquant vs système avec chronologie |
06-auto-mode-full-run.svg | Terminal complet du mode --auto — du démarrage du conteneur au shell root |
Voir docs/CHALLENGE.md. En résumé :
labuser (mot de passe labuser).rpm-build, rpmdevtools sont installés).pkcon install-local --allow-untrusted.%post s'exécute en root. Lisez /root/flag.txt.Bloqué ? docs/HINTS.md contient des indices de plus en plus
explicites. N'ouvrez docs/SOLUTION.md qu'après avoir
résolu le défi ou si vous avez réellement besoin de la solution.
À l'intérieur de l'un ou l'autre conteneur, en tant que n'importe quel utilisateur :
/opt/lab/check-version.sh # verdict VULNÉRABLE ou CORRIGÉ
/opt/lab/check-preconditions.sh # auto-vérification d'exposition
/opt/lab/exploit-check.sh # vérification version + polkit + D-Bus + IOC
/opt/lab/monitor-ioc.sh # suit le journal pour l'IOC
Depuis l'hôte, côte à côte :
./scripts/compare-env.sh
Appliquez l'atténuation polkit provisoire à un conteneur vulnérable :
docker exec pack2theroot-vuln /opt/lab/apply-polkit-mitigation.sh
# Puis réessayez l'exploit — il devrait maintenant échouer.
L'image patched gère honnêtement le statut de la version de PackageKit :
/etc/pack2theroot-banner) indiquant
si la version installée est le vrai correctif ou un espace réservé de la distribution.Pour construire une image corrigée complète en version une fois que votre distribution
publie le rétroportage, déposez le(s) RPM dans patched/local/ et reconstruisez :
docker compose build --build-arg USE_LOCAL_RPM=1 patched
--privileged pour exécuter la pile complète
dbus/polkitd/packagekitd. Ne l'exécutez que dans des environnements isolés.labuser) est volontairement faible. Ne le réutilisez jamais.Sources primaires :
CVE-2026-41651 :
https://nvd.nist.gov/vuln/detail/CVE-2026-4165176cfb675 :
https://github.com/PackageKit/PackageKit/commit/76cfb675fb31acc3ad5595d4380bfff56d2a8697Voir docs/references.md pour la couverture secondaire
et les suiveurs de distribution.
MIT — voir LICENSE.