Vulnerability 2: Escaping via Exposed Docker Daemon
Unix Sockets 101 (One Size Fits All)
When mentioning "sockets", you would likely think of "sockets" in networking. Well, the concept here is almost the same. Sockets are used to move data between two places. Unix sockets use the filesystem to transfer data rather than networking interfaces. This is known as Inter-process Communication (IPC) and is essential in operating systems because being able to send data between processes is extremely important.
Unix sockets are substantially quicker at transferring data than TCP/IP sockets (Percona., 2020). This is why database technologies such as Redis boast such outstanding performance. Unix sockets also use file system permissions. This is important to remember for the next heading.
How Does Docker Use Sockets
When interacting with the Docker Engine (i.e. running commands such as docker run
), this will be done using a socket (usually, this is done using a Unix socket unless you execute the commands to a remote Docker host). Recall that Unix sockets use filesystem permissions. This is why you must be a member of the Docker group (or root!) to run Docker commands, as you will need the permissions to access the socket owned by Docker.
Finding the Docker Socket in a Container
Remember, containers interact with the host operating system using the Docker Engine (and, therefore, have access to the Docker socket!) This socket (named docker.sock) will be mounted in the container. The location of this varies by the operating system the container is running, so you would want to find
it. However, in this example, the container runs Ubuntu 18.04, meaning the docker.sock is located in /var/run.
This location can vary based on the operating system and can even be manually set by the developer at runtime of the container.
Exploiting the Docker Socket in a Container
First, let's confirm we can execute docker commands. You will either need to be root on the container or have the "docker" group permissions as a lower-privileged user.
We will use Docker to create a new container and mount the host's filesystem into this new container. Then we are going to access the new container and look at the host's filesystem.
Our final command will look like this: docker run -v /:/mnt --rm -it alpine chroot /mnt sh
, which does the following:
We will need to upload a docker image. For this room, I have provided this on the VM. It is called "alpine". The "alpine" distribution is not a necessity, but it is extremely lightweight and will blend in a lot better. To avoid detection, it is best to use an image that is already present in the system, otherwise, you will have to upload this yourself.
We will use
docker run
to start the new container and mount the host's file system (/) to (/mnt) in the new container:docker run -v /:/mnt
We will tell the container to run interactively (so that we can execute commands in the new container):
-it
Now, we will use the already provided alpine image:
alpine
We will use
chroot
to change the root directory of the container to be /mnt (where we are mounting the files from the host operating system):chroot /mnt
Now, we will tell the container to run
sh
to gain a shell and execute commands in the container:sh
You may need to "Ctrl + C" to cancel the exploit once or twice for this vulnerability to work, but, as you can see below, we have successfully mounted the host operating system's filesystem into the new alpine container.
Verify Success
After executing the command, we should see that we have been placed into a new container. Remember, we mounted the host's filesystem to /mnt (and then used chroot
to make the container's /mnt become /)
So, let's see the contents of / by doing ls /
Last updated