Skip to content
HackIndex logo

HackIndex

Docker API Service Detection

2 min read Apr 4, 2026

The Docker daemon exposes a REST API on port 2375 (plaintext) and port 2376 (TLS). When either port is reachable without authentication, any client has full control over the Docker engine — running containers, images, volumes, and the host filesystem via container mounts. Unauthenticated Docker API exposure is a critical misconfiguration that leads directly to host compromise. Port 2375 is the most dangerous — it requires no credentials and no TLS. Port 2376 should require a valid client certificate but is often misconfigured to accept any connection.

Port Detection

┌──(kali㉿kali)-[~]
└─$ nmap -sV -p 2375,2376 $TARGET_IP
2375/tcp open  docker  Docker 20.10.21
2376/tcp open  docker  Docker 20.10.21 (TLS)

Confirm API Access — Version Endpoint

The /version endpoint requires no authentication and immediately confirms whether the API is reachable:

┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:2375/version | python3 -m json.tool
{
  "Version": "20.10.21",
  "ApiVersion": "1.41",
  "Os": "linux",
  "Arch": "amd64",
  "KernelVersion": "5.15.0-91-generic",
  "GoVersion": "go1.18.1"
}

A JSON response confirms the API is accessible without credentials. The kernel version is immediately useful for local privilege escalation research if you land in a container. Check TLS port the same way:

┌──(kali㉿kali)-[~]
└─$ curl -sk https://$TARGET_IP:2376/version | python3 -m json.tool

Enumerate Running Containers

┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:2375/containers/json | python3 -m json.tool | grep -iE '"Id"|"Names"|"Image"|"Status"|"Ports"'
"Id": "a1b2c3d4e5f6...",
"Names": ["/webapp"],
"Image": "nginx:latest",
"Status": "Up 3 days",
"Id": "b2c3d4e5f6a7...",
"Names": ["/db"],
"Image": "mysql:8.0",
"Status": "Up 3 days"
┌──(kali㉿kali)-[~]
└─$ # List all containers including stopped ones
┌──(kali㉿kali)-[~]
└─$ curl -sk 'http://$TARGET_IP:2375/containers/json?all=true' | python3 -m json.tool | grep -iE '"Names"|"Image"|"Status"'

List All Images

┌──(kali㉿kali)-[~]
└─$ curl -sk http://$TARGET_IP:2375/images/json | python3 -m json.tool | grep -iE '"RepoTags"|"Size"'
"RepoTags": ["nginx:latest"],
"RepoTags": ["mysql:8.0"],
"RepoTags": ["webapp:production"]

Use Docker Client Directly

Setting DOCKER_HOST lets you use the standard docker CLI against the remote API — much faster than crafting curl requests:

┌──(kali㉿kali)-[~]
└─$ export DOCKER_HOST=tcp://$TARGET_IP:2375
┌──(kali㉿kali)-[~]
└─$ docker version
┌──(kali㉿kali)-[~]
└─$ docker ps
┌──(kali㉿kali)-[~]
└─$ docker images
Server: Docker Engine
 CONTAINER ID   IMAGE          STATUS
 a1b2c3d4e5f6   nginx:latest   Up 3 days
 b2c3d4e5f6a7   mysql:8.0      Up 3 days

The docker CLI now communicates with the remote daemon. All docker commands run against the target. Move to unauthenticated access assessment to confirm the full attack surface, then to container escape for host compromise.

References