FlawOpen

flawopen.com/Teardowns/cve-2024-21626-runc-container-breakout-leaky-vessels

● CVE-2024-21626 · CVSS 8.6[1], [5] · 높음
보안 연구 · FlawOpen

CVE-2024-21626: runc의 Leaky Vessels 탈출 수정 방법

CVE-2024-21626은 runc 내부 파일 디스크립터 누출과 chdir 이후 경계 검사 누락이 결합된 문제입니다. 실행 경로에 따라 컨테이너가 호스트 네임스페이스의 작업 디렉터리에서 시작하거나 관련 exec 경로로 호스트 파일에 접근할 수 있었습니다.[1], [2], [3]

💡 알기 쉬운 설명 (ELI5)

runc은 컨테이너를 위한 격리된 방을 준비합니다. 내부 핸들이 유출되면 선택된 작업 디렉터리가 그 방 밖을 가리킬 수 있었습니다. 마지막 경계 검사와 내부 핸들 닫기가 이 경로를 차단합니다.[1], [2]

핵심 개념 및 용어

runc
컨테이너를 만들고 시작하는 OCI 런타임입니다.[1]
내부 파일 디스크립터
열린 파일이나 디렉터리를 가리키는 운영체제 핸들입니다. 자식 프로세스 실행 시 디스크립터가 열린 채 남으면 프로세스 경계를 넘어 접근이 이어질 수 있습니다.[1], [3]
cwd
프로세스의 작업 디렉터리입니다. 마운트 네임스페이스 루트 밖에 있으면 Linux Getwd가 ENOENT를 반환할 수 있습니다.[1], [2]
컨테이너 탈출
컨테이너 프로세스가 격리 경계 밖의 호스트 리소스에 접근하는 경로입니다.[1]

프레임워크 매핑

  • 이 가이드에서 설명하는 약점의 CWE 참조: CWE-403

근본 원인 분석 (Root Cause)

runc 1.1.12 이전에는 내부 파일 디스크립터가 runc init으로 누출될 수 있었습니다. 이는 runc init 내부의 누출이며 컨테이너 프로세스가 단순히 상속한 것이 아닙니다. 공격 1과 2에서는 설정되거나 선택된 작업 디렉터리가 호스트 마운트 네임스페이스 핸들을 통해 해석될 수 있었고, runc은 chdir 뒤 최종 cwd가 컨테이너 루트 안에 있는지 확인하지 않았습니다. 별도의 변형은 execve 경로 처리로 호스트 바이너리를 덮어썼습니다. 1.1.12는 Linux Getwd/ENOENT 동작을 이용한 cwd 검사, execve 직전 내부 디스크립터 닫기, 확인된 누출 수정, runc init 실행 전 stdio 외 디스크립터에 close-on-exec 설정이라는 네 겹의 조치를 추가했습니다.[1], [2], [3], [4]

CVE-2024-21626: 공격 변형과 runc 1.1.12의 대응 보호

권고문의 변형필요한 조건과 경계관련 수정 또는 임시 조치
공격 1 — runc run으로 악성 이미지 실행권한 있는 사용자가 신뢰할 수 없는 이미지를 실행하도록 유도해야 합니다. 구성된 작업 디렉터리가 컨테이너 마운트 네임스페이스 밖으로 해석될 수 있습니다.chdir 뒤 unix.Getwd() 검사는 cwd가 네임스페이스 루트 밖이면 실패합니다. 임시 조치로 신뢰할 수 없는 이미지의 cwd는 /만 허용합니다.
공격 2 — 컨테이너 프로세스와 runc exec컨테이너 안의 프로세스가 관리자가 runc exec --cwd로 / 이외의 cwd를 고를 것을 예측하고 실행 전에 관련 경로를 준비해야 합니다.같은 chdir 후 cwd 검사가 컨테이너 밖 실행을 차단합니다. 패치 전에는 --cwd를 /로 제한합니다.
공격 3a — 이미지 실행을 통한 호스트 바이너리 덮어쓰기공격 1의 변형으로 권한 있는 사용자가 악성 이미지를 시작해야 하는 조건이 남습니다. 이 변형은 execve 경계를 넘어 호스트 바이너리에 접근합니다.execve 직전에 runc 내부 디스크립터를 닫고 확인된 누출을 수정하며 O_CLOEXEC을 설정합니다. runc 1.1.12 또는 공급업체 수정 버전으로 업데이트합니다.
공격 3b — runc exec를 통한 호스트 바이너리 덮어쓰기공격 2의 변형으로 공격자가 영향을 줄 수 있는 실행 중 컨테이너의 악성 프로세스에서 시작합니다. 그래도 권한 있는 관리자가 해당 컨테이너에서 일반적인 runc exec 작업을 수행해야 합니다. 권고문은 이미지 경로보다 사용자 상호작용이 적다고 설명합니다.exec 전 닫기와 close-on-exec 보호가 이 별도 경로를 막습니다. 유지 관리자는 실용적인 임시 방안을 제시하지 않았으므로 업데이트해야 합니다.

단계별 공격 실행 흐름

단계 1

내부 핸들 누출

설정 중 호스트 마운트 네임스페이스를 참조하는 runc 내부 디스크립터가 runc init에서 사용 가능한 상태로 남을 수 있었습니다.[1], [3]

단계 2

도달 가능한 작업 디렉터리 경로

악성 이미지 또는 특정 조건의 runc exec 흐름은 요청된 cwd가 컨테이너 루트 밖에서 해석되게 할 수 있었습니다.[1], [2]

단계 3

경계 검사 누락

기존 코드는 chdir 뒤 최종 cwd를 확인하지 않았습니다. 관련 변형은 execve 경로 처리를 이용해 호스트 바이너리를 덮어썼습니다.[2], [3]

단계 4

영향과 조건

권고문은 네 가지 변형에서 호스트 파일 시스템 접근과 호스트 제어가 가능하다고 설명합니다. 이미지 공격은 권한 있는 사용자가 신뢰할 수 없는 이미지를 실행해야 합니다. 공격 3b도 공격자가 영향을 줄 수 있는 컨테이너에서 관리자가 일반적인 runc exec 작업을 수행해야 합니다.[1]

소스 코드 비교: 취약한 구현 vs 보안 패치

취약한 구현
// 수정 전 동작을 단순화한 표현이며 runc 원문 소스가 아닙니다
if err := os.Chdir(config.Cwd); err != nil {
    return err
}
// 누락된 검사: 최종 cwd가 네임스페이스 루트 안에 있는지 확인
보안 강화 패치
// runc 1.1.12 경계 검사(upstream 발췌)
if wd, err := unix.Getwd(); errors.Is(err, unix.ENOENT) {
    return errors.New("cwd가 컨테이너 마운트 네임스페이스 루트 밖에 있습니다")
} else if err != nil {
    return fmt.Errorf("cwd 안전성을 확인할 수 없습니다: %w", err)
} else if !filepath.IsAbs(wd) {
    return fmt.Errorf("cwd가 절대 경로가 아닙니다: %q", wd)
}

엔지니어링 및 시스템 보안 강화 체크리스트

출처