dirty-pipe

2 개의 포스트

Escaping containers using the Dirty Pipe vulnerability | Datadog Security Labs (새 탭에서 열림)

리눅스 커널의 Dirty Pipe(CVE-2022-0847) 취약점은 권한이 없는 프로세스가 읽기 전용 파일에 데이터를 쓸 수 있게 하여, 컨테이너 환경에서 호스트의 권한을 탈취하는 '컨테이너 탈출'을 가능하게 한다. 이 글은 Kubernetes 환경에서 runC 바이너리를 덮어쓰는 방식을 통해, 공격자가 격리된 컨테이너를 벗어나 호스트 수준의 관리자 권한을 획득하는 과정을 상세히 설명한다. 이는 과거 runC 취약점 패치가 성능 최적화를 위해 커널 페이지 캐시를 공유한다는 점을 역이용한 결과로, 현대적 컨테이너 런타임 구조 내의 보안 허점을 시사한다. ### 컨테이너 런타임과 OCI 명세의 이해 * Kubernetes는 컨테이너 실행을 위해 containerd나 CRI-O 같은 고수준 런타임을 사용하며, 이들은 내부적으로 runC와 같은 저수준 OCI(Open Container Interface) 런타임을 호출한다. * runC는 리눅스의 네임스페이스와 제어 그룹(cgroups)을 설정하여 프로세스를 논리적으로 격리하며, 최종적으로 `execve` 시스템 콜을 통해 사용자가 지정한 엔트리포인트를 실행한다. * 컨테이너 프로세스가 생성되는 시점에 `/proc/self/exe` 파일 기술자(File Descriptor)를 통해 호스트의 runC 바이너리에 접근할 수 있는 경로가 일시적으로 열리게 된다. ### runC 취약점의 역사적 맥락 * 과거 CVE-2019-5736 취약점은 컨테이너 내부에서 호스트의 runC 바이너리를 직접 수정하여 루트 권한을 획득하는 방식을 사용했다. * 이를 방어하기 위해 runC 개발팀은 바이너리를 복제(clone)하여 실행하거나, 호스트의 runC 바이너리를 읽기 전용으로 마운트하여 컨테이너 내부에 제공하는 패치를 적용했다. * 하지만 Dirty Pipe 취약점은 커널 페이지 캐시를 조작하여 읽기 전용 파일조차 수정할 수 있게 하므로, 성능 향상을 위해 도입된 '읽기 전용 공유 방식'이 오히려 새로운 공격 경로가 되었다. ### Dirty Pipe를 이용한 컨테이너 탈출 메커니즘 * 공격자는 권한이 없는 컨테이너 내부에서 스크립트를 실행하여 호스트의 runC가 다시 실행되기를 기다린다(예: 관리자의 `kubectl exec` 호출). * runC가 실행되는 순간, 공격 프로세스는 `/proc/<runC-pid>/exe` 경로를 통해 Dirty Pipe 취약점을 가동한다. * 이 취약점은 커널 페이지 캐시 수준에서 메모리를 덮어쓰기 때문에, 호스트의 물리적 디스크에 저장된 runC 파일은 건드리지 않으면서도 현재 실행 중인 runC 프로세스를 악성 바이너리로 교체할 수 있다. ### 공격 증명(PoC) 및 실행 과정 * 공격 스크립트는 루프를 돌며 `ps` 명령어로 `/proc/self/exe`를 참조하는 runC 프로세스의 PID를 지속적으로 감시한다. * 대상 PID가 발견되면 Dirty Pipe 익스플로잇 코드를 실행하여, 해당 프로세스가 참조하는 바이너리 데이터를 호스트 권한으로 실행될 악성 ELF 파일로 덮어쓴다. * 조작된 runC는 호스트 시스템에서 루트 권한으로 실행되며, 공격자가 의도한 명령(예: 호스트의 `/tmp/hacked` 파일 생성 등)을 수행한 뒤 호스트 전체를 장악할 수 있게 한다. ### 보안 결론 및 대응 방안 * 본 취약점은 컨테이너 격리 기술 자체가 아닌 리눅스 커널의 메모리 관리 결함에서 비롯된 것이므로, 가장 확실한 해결책은 Dirty Pipe 보안 패치가 적용된 최신 커널 버전으로 노드를 업데이트하는 것이다. * 컨테이너 환경에서는 `/proc` 파일 시스템에 대한 비정상적인 접근을 모니터링하고, 불필요한 고권한(Privileged) 컨테이너 사용을 지양하는 보안 정책이 병행되어야 한다. * 시스템 재부팅이나 캐시 초기화 시 조작된 페이지 캐시가 사라져 공격 흔적이 휘발될 수 있으므로, 실시간 침입 탐지 시스템을 통한 조기 대응이 중요하다.

Dirty Pipe 취약점을 이용한 컨 (새 탭에서 열림)

리눅스 커널에서 발견된 Dirty Pipe 취약점은 권한이 없는 프로세스가 읽기 권한만 가진 파일에 데이터를 쓸 수 있게 허용하며, 이를 통해 컨테이너 환경에서 호스트 시스템의 루트 권한을 탈취할 수 있는 심각한 위협을 초래합니다. 특히 Kubernetes 환경에서 널리 쓰이는 컨테이너 런타임인 runC의 실행 바이너리를 페이지 캐시 수준에서 변조함으로써, 격리된 컨테이너를 탈출하여 호스트 시스템을 완전히 장악하는 시나리오가 가능합니다. 본 글에서는 이 취약점의 기술적 배경과 함께 실제 컨테이너 탈출이 이루어지는 공격 메커니즘을 상세히 설명합니다. **컨테이너 런타임과 runC의 구조적 취약성** - Kubernetes는 containerd나 CRI-O 같은 런타임을 통해 컨테이너를 관리하며, 실제 프로세스 생성은 OCI 규격을 준수하는 하위 레벨 런타임인 runC가 담당합니다. - runC는 컨테이너 내부 프로세스를 실행할 때 자신을 포크(fork)한 뒤 `execve` 시스템 콜을 호출하는데, 이때 `/proc/self/exe` 경로를 통해 호스트에 있는 runC 이진 파일에 대한 파일 서술자(File Descriptor)를 열어두게 됩니다. - 과거 CVE-2019-5736 취약점에 대한 대응으로 runC를 읽기 전용으로 마운트하는 방어책이 도입되었으나, Dirty Pipe는 커널의 페이지 캐시를 직접 수정하므로 이러한 파일 시스템 수준의 권한 제한을 무력화합니다. **Dirty Pipe를 이용한 컨테이너 탈출 과정** - 공격자는 먼저 취약한 웹 애플리케이션 등을 통해 권한이 제한된 일반 컨테이너에 침투한 뒤, 호스트의 runC 바이너리가 실행되기를 대기합니다. - 관리자가 `kubectl exec`와 같은 명령을 수행하여 컨테이너 내부에서 runC가 구동되는 순간, 공격 프로세스는 `/proc/<runC-pid>/exe`를 통해 호스트의 runC 실행 파일에 접근합니다. - Dirty Pipe 공격 프리미티브를 활용하여 페이지 캐시에 로드된 runC 바이너리 내용을 공격자의 악성 ELF 코드로 덮어씁니다. - 이렇게 변조된 runC는 호스트의 루트 권한으로 실행되므로, 공격자는 호스트 시스템에서 임의의 명령(예: 호스트 이름 확인, 루트 권한 쉘 실행 등)을 수행하며 컨테이너 격리를 완전히 무너뜨립니다. **메모리 기반 공격의 비영구적 특성** - Dirty Pipe를 통한 바이너리 변조는 디스크의 실제 파일을 직접 수정하는 것이 아니라 커널의 페이지 캐시 내에서 발생합니다. - 따라서 공격으로 인한 변조는 시스템이 재부팅되거나 커널 캐시가 드롭(drop)되기 전까지만 유지되는 비영구적 특성을 가집니다. - 하지만 단 한 번의 실행만으로도 호스트에 백도어를 설치하거나 권한을 상승시키기에 충분하므로 그 위험성은 매우 높습니다. Dirty Pipe 취약점은 리눅스 커널 수준의 결함이므로 이를 근본적으로 해결하기 위해서는 최신 보안 패치가 적용된 커널로 신속히 업데이트해야 합니다. 또한 컨테이너 환경에서는 최소 권한 원칙을 철저히 준수하고, 런타임 보안 모니터링 도구를 도입하여 `/proc` 파일 시스템에 대한 의심스러운 접근이나 시스템 이진 파일의 비정상적인 동작을 실시간으로 감지하고 차단하는 방어 전략이 필요합니다.