flawopen.com/Teardowns/cve-2024-21626-runc-container-breakout-leaky-vessels
CVE-2024-21626: runc의 Leaky Vessels 탈출 수정 방법
CVE-2024-21626은 runc 내부 파일 디스크립터 누출과 chdir 이후 경계 검사 누락이 결합된 문제입니다. 실행 경로에 따라 컨테이너가 호스트 네임스페이스의 작업 디렉터리에서 시작하거나 관련 exec 경로로 호스트 파일에 접근할 수 있었습니다.[1], [2], [3]
runc은 컨테이너를 위한 격리된 방을 준비합니다. 내부 핸들이 유출되면 선택된 작업 디렉터리가 그 방 밖을 가리킬 수 있었습니다. 마지막 경계 검사와 내부 핸들 닫기가 이 경로를 차단합니다.[1], [2]
핵심 개념 및 용어
runc- 컨테이너를 만들고 시작하는 OCI 런타임입니다.[1]
내부 파일 디스크립터- 열린 파일이나 디렉터리를 가리키는 운영체제 핸들입니다. 자식 프로세스 실행 시 디스크립터가 열린 채 남으면 프로세스 경계를 넘어 접근이 이어질 수 있습니다.[1], [3]
컨테이너 탈출- 컨테이너 프로세스가 격리 경계 밖의 호스트 리소스에 접근하는 경로입니다.[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 보호가 이 별도 경로를 막습니다. 유지 관리자는 실용적인 임시 방안을 제시하지 않았으므로 업데이트해야 합니다. |
단계별 공격 실행 흐름
영향과 조건
권고문은 네 가지 변형에서 호스트 파일 시스템 접근과 호스트 제어가 가능하다고 설명합니다. 이미지 공격은 권한 있는 사용자가 신뢰할 수 없는 이미지를 실행해야 합니다. 공격 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)
}
엔지니어링 및 시스템 보안 강화 체크리스트
- ✓runc을 1.1.12 또는 공급업체가 지원하는 최신 버전으로 업데이트하고 각 노드 이미지의 실제 런타임 버전을 확인합니다.[1], [4]
- ✓신뢰할 수 없는 컨테이너 이미지를 코드 실행 요청으로 취급하고 Docker, Kubernetes, 빌드 파이프라인에서 실행 권한을 제한합니다.[1]
- ✓런타임 구현에서는 chdir 뒤 플랫폼의 네임스페이스 루트 검사로 cwd를 확인하고 오류 시 안전하게 중단합니다.[1], [2]
- ✓내부 디스크립터에 O_CLOEXEC을 설정하고 런타임 자체 디스크립터를 고려해 exec 직전 마지막 안전 지점에서 stdio 외 디스크립터를 닫습니다.[1], [3]
- ✓user namespace를 runc 패치의 대체재로 간주하지 말고, 지원되는 경우 추가 격리 계층으로 사용합니다.[1]