Docker

Use containers to run applications in a reproducible and isolated environment

Docker allows you to build and run containers that hold an application in an isolated environment, similar to a Virtual Machine. This is done with Linux namespaces, however, making it much more lightweight than VMs.

It should normally be impossible to escape to the host system from inside a container. While it may still be possible to reach into the internal network over regular network protocols, access to the host's filesystem and processes is restricted. There are, however, some misconfigurations that be exploited by attackers to get more access than expected, often targeting the host from inside the container.

Escaping docker with many different misconfigurations

Below are some more tricks that I've personally found useful.

Docker Access

In some cases, you may be able to access the docker.sock socket or TCP ports 2375/2376 for the Docker API. This API does all the interaction with the host system. Through this, it is possible to start new containers with extra privileges and access through mounts.

Connecting

If you find yourself as a low-privilege user on a machine with Docker installed, and you have the docker group, you should be able to successfully run the following command. Check out Privilege Escalation to abuse this to get root access.

The -H option with the unix:// protocol allows you to connect to a mounted docker.sock socket somewhere on the filesystem if it is available:

For remote Docker APIs, a host can be specified with -H on your local docker installation to run commands on another instance. Below is an example of an unencrypted connection to port 2375:

When TLS is required, just use port 2376 and --tls:

As recommended, TLS servers may require a client certificate for authentication. If you have this, specify it with the --tlscert and --tlskey options:

Privilege Escalation

Interactive from docker run by mounting host:

Reverse shell using docker-compose by mounting host:

Docker in Docker (DinD)

Using the docker:dindarrow-up-right image, it is possible to run a nested Docker engine inside another Docker container. This allows applications that require starting docker containers to communicate with an isolated environment without requiring to talk with the host system directly.

A simple working setup of this can be seen below:

chevron-rightTesting setuphashtag

Set up the following compose file with docker-compose up:

Then, use the command below to enter a shell in the unprivileged shell container:

From here, use commands like docker version to interact with the dind container.

Making this work requires the --privileged flag to be set for the dind container, giving it more access to the host system than regular containers. We will abuse this, but first, we can use the techniques from Privilege Escalation to start a new container on the remote Docker API (DinD). Note that we also provide --privileged here to maintain the access:

When inside we should have access as if we are on the dind container itself. Because it is privileged, we can mount disks like /dev/sda to read and write to them. These disks come from the real host:

The above may generate some "Invalid argument" messages, but all possible disks should now be mounted in /mnt. Look for the host system here and do anything with the newfound access to its files.

Filesystem Protections

If you gain access to a container via some vulnerability, it might be hardened with some protections on the filesystem. A common one is "read-only file system" when writing anywhere.

Regular directories under / will not be writable, but there is often one exception: /dev/shm. This special directory is stored in RAM instead of disk and is thus always writable. While it sounds perfect, it is commonly protected with a noexec flag disallowing any ELF binaries from executing inside this directory. Running ./pspy, for example, won't work.

It is still possible to execute any bash code, however, and some clever people have abused this. By writing bash code that injects some ELF binary's logic into another process, it is possible to run a binary stored in /dev/shm without actually executing it. The repository below implements this:

Run any ELF binary from a noexec location by injecting it as shellcode

After uploading this shell script to the target together with your binary, run it with any arguments:

Distroless RCE

In an ever more restricted scenario, your container will not even have common shells like sh or ash. It might only have the programming language required to run the application installed. While a regular reverse shell is impossible here, it is often possible to use the programming language to load shellcode similarly to DDexec. By running binaries such as busybox, many commands can be brought back to enumerate the system in this restricted environment.

Check out the following video for more information about exploiting this:

"DEF CON 31 - Exploring Linux Memory Manipulation for Stealth and Evasion - Polop, Gutierrez"arrow-up-right

Last updated