Anatomy of an Unauthenticated Docker Engine API Takeover Chain
Overview
Exposing an unauthenticated Docker Engine API port (TCP 2375) to the public internet is one of the quickest ways to lose control of cloud infrastructure.
My newly deployed Docker API honeypot captured automated botnet scanners executing a complete, sub-second 4-stage attack chain:
Within ~100 milliseconds of confirming API responsiveness, automated exploit scripts transition from basic banner grabbing to full interactive container takeover attempts.
1. Reconnaissance Scan
Uses zgrab/0.x to sweep public IPv4 ranges for open /v1.16/version endpoints.
2. Health Verification
Spoofs Docker-Client/20.10.18 to confirm /_ping returns 200 OK.
3. Container Creation
Issues POST /containers/create JSON payload to instantiate an unprivileged container.
4. Socket Hijacking
Issues Connection: Upgrade header to upgrade HTTP to a raw interactive TCP socket.
Automated botnets sweep public IP ranges for exposed Docker Engine APIs, leveraging sub-second protocol upgrades to establish raw interactive shell access.
Methodology
To dissect this attack progression, my analysis separates the incident into distinct operational phases:
- Reconnaissance Identification: Filter raw TCP telemetry for initial port scans and banner probes (
/version). - Ping & Daemon Verification: Isolate requests validating API availability (
/_ping). - Payload Inspection: Analyze
POST /containers/createJSON structures to extract container configurations and target images. - Interactive Upgrade Extraction: Inspect
POST /containers/attachrequests for raw TCP socket upgrade headers (Upgrade: tcp). - Timeline Correlation: Calculate exact millisecond deltas between API response and subsequent exploit execution.
- Indicator Generation: Aggregate attacker IPs, User-Agent strings, and target API endpoints into reusable IOC tables and detection logic.
Tools used
- Honeypot container logging raw HTTP REST API traffic on port 2375
- JSON telemetry parser for timestamp delta calculations and payload extraction
- Mermaid sequence diagramming for protocol flow visualization
Investigation
Reconnaissance & API Version Probing
The attack chain begins with wide-scope internet scanning to discover exposed Docker daemons.
Attacker IPs (such as 140.238.153.39 and 101.206.108.14) issue a lightweight version check using the Zgrab scanner framework:
GET /v1.16/version HTTP/1.1
Host: <HONEYPOT_IP>:2375
User-Agent: Mozilla/5.0 zgrab/0.x
Accept: */*
Accept-Encoding: gzip
Using zgrab/0.x allows attackers to rapidly sweep public IPv4 ranges and flag hosts that return standard Docker version metadata.
Responsiveness Check
Once an IP is identified as open, a second request verifies that the Docker Engine API daemon is actively responding:
HEAD /_ping HTTP/1.1
Host: <HONEYPOT_IP>:2375
User-Agent: Docker-Client/20.10.18 (linux)
GET /_ping HTTP/1.1
Host: <HONEYPOT_IP>:2375
User-Agent: Docker-Client/1.13.1 (linux)
Accept-Encoding: gzip
By disguising their User-Agent as a legitimate Linux Docker CLI client (Docker-Client/20.10.18 or Docker-Client/1.13.1), the bot confirms that /_ping returns an HTTP 200 OK response.
Container Creation Attempt
Just 111 milliseconds after receiving the ping confirmation, the attacker's automated script issues an API request to instantiate a new container:
POST /v1.24/containers/create HTTP/1.1
Host: <HONEYPOT_IP>:2375
User-Agent: Go-http-client/1.1
Content-Length: 2214
Content-Type: application/json
{
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true
}
The payload specifies container parameters designed to run privileged tasks or mount underlying host filesystems (such as /host or /var/run/docker.sock).
Interactive Shell Upgrade
Immediately after submitting the container creation request (+110 ms), the bot attempts to attach an interactive socket stream to the container:
POST /v1.24/containers/attach?stderr=1&stdout=1&stream=1 HTTP/1.1
Host: <HONEYPOT_IP>:2375
User-Agent: Go-http-client/1.1
Content-Length: 0
Connection: Upgrade
Content-Type: text/plain
Upgrade: tcp
Using the HTTP Connection: Upgrade header with Upgrade: tcp, the attacker requests the Docker daemon to switch the connection into a raw bidirectional TCP stream, providing direct interactive shell access to execute cryptojacking scripts (XMRig, kinsing) or host takeover commands.
IOCs
| Indicator Type | Value | Description |
|---|---|---|
| Attacker IP | 140.238.153.39 | Active Docker API Takeover Scanner |
| Attacker IP | 101.206.108.14 | Active Docker API Takeover Scanner |
| Scanner User-Agent | Mozilla/5.0 zgrab/0.x | Zgrab Docker API Port Scanner |
| Client User-Agent | Docker-Client/20.10.18 (linux) | Automated Docker CLI Probe |
| Client User-Agent | Docker-Client/1.13.1 (linux) | Automated Docker CLI Probe |
| Target Endpoint | POST /v1.24/containers/create | Container Creation Exploit Attempt |
| Target Endpoint | POST /v1.24/containers/attach | Raw TCP Shell Socket Upgrade Attempt |
Detection Logic
IF
Destination Port = 2375 (Docker Engine API)
AND HTTP Request = GET /v1.16/version OR HEAD /_ping
AND User-Agent MATCHES "zgrab" OR "Docker-Client"
THEN
Docker API Reconnaissance Scan Detected
IF
HTTP Request = POST /v1.24/containers/create
AND Followed within < 2 seconds by POST /v1.24/containers/attach
AND Request Header contains "Connection: Upgrade" AND "Upgrade: tcp"
THEN
Automated Unauthenticated Docker Daemon Takeover Attempt Detected
Observations & Conclusions
- Sub-Second Execution Speed: Exploitation is entirely automated — the transition from
/_pingtocontainers/createandcontainers/attachtakes under 120 milliseconds. - Standardized Toolchain: Attackers leverage
zgrabfor bulk port discovery,Go-http-clientfor REST API payloads, and spoofedDocker-Clientheaders for ping checks. - Primary Intent: Unauthenticated Docker socket exposure remains a prime vector for automated cryptojacking campaigns (
kinsing,kdevtmpf) aiming to deploy resource-intensive container workloads.
Docker TCP sockets should never be exposed unauthenticated on public interfaces (0.0.0.0:2375). Always enforce TLS mutual authentication (2376) or restrict access via SSH tunneling or firewall rules.
