클러스터: 쿠버네티스의 로컬 볼륨 취약점
편집 및 추가 설명: 트리샤 하워드(Tricia Howard)
핵심 요약
Akamai 보안 연구원 토머 펠레드(Tomer Peled)는 최근 쿠버네티스에서 심각한 수준의 취약점을 을 발견했으며 이 취약점은 CVSS 점수 7.2, CVE-2023-5528 로 지정되었습니다.
이 취약점은 쿠버네티스 클러스터 내의 모든 Windows 엔드포인트에서 시스템 권한으로 원격 코드 실행을 허용합니다. 이를 악용하려면 공격자는 클러스터에 악성 YAML 파일을 적용해야 합니다.
이 취약점은 클러스터의 모든 Windows 노드에 대한 전체 장악으로 이어질 수 있습니다.
쿠버네티스(버전 1.28.4 이전)의 기본 설치에서 악용될 수 있으며, 온프레미스 배포와 Azure Kubernetes Service 모두를 대상으로 테스트되었습니다.
- 이 블로그 게시물에서는 이 취약점을 차단하기 위한 개념 증명 YAML 파일과 OPA(Open Source Agent) 룰을 제공합니다.
서론
쿠버네티스 및 컨테이너 는 일반적으로 보안 업계에서 주도적인 역할을 하고 있기 때문에 Akamai를 포함한 전 세계 연구자들의 관심의 대상이 되고 있습니다. Akamai의 리서치 여정은 악성 YAML 파일을 클러스터에 적용해 악용될 수 있는 명령 인젝션 취약점인 CVE-2023-3676 (CVSS 8.8)에서 시작되었습니다. 쿠버네티스 프레임워크는 컨테이너 네트워크 인터페이스 설정부터 포드 관리, 심지어 비밀 처리에 이르기까지 기본적으로 모든 것에 YAML 파일을 사용하기 때문에, 이 취약점을 악용하면 치명적인 결과를 초래할 수 있습니다.
이 취약점과 함께 안전하지 않은 함수 호출과 사용자 입력 위생화 부족이라는 동일한 근본 원인을 공유하는 다른 두 가지 취약점도 발견되었습니다.
볼륨 있는 포드를 생성하는 YAML 파일에서 subPath 매개변수를 위생화하지 않으면 악성 인젝션의 기회가 열립니다. 처음 발견한 이 문제에 이어, 리서치의 마지막 단계에서 또 다른 명령어 인젝션 취약점으로 이어질 수 있는 코드 내 잠재적인 부분을 발견했습니다. 몇 번의 시도 끝에 “kubelet" 서비스(시스템 권한)로 명령을 실행하는 것과 비슷한 결과를 얻을 수 있었습니다. 이것이 바로 오늘 CVE-2023-5528와의 여정을 시작하는 지점입니다.
이 블로그 게시물에서는 취약점의 세부 사항과 이를 허용하는 쿠버네티스 소스 코드의 문제를 살펴보고 쿠버네티스 팀의 패치와 그 효과를 분석해 보겠습니다. 가능한 한 빨리 패치를 적용하는 것이 좋지만, 영향을 받는 노드를 찾는 방법과 이러한 종류의 동작을 탐지하고 차단하는 데 도움이 되는 OPA(Open Policy Agent) 룰을 적용하는 방법에 대한 짧은 가이드도 포함했습니다.
이 게시물은 쿠버네티스 자체 및 사이드카 프로젝트(예: 인그레스등)의 여러 코드 영역에서 입력 위생화가 부족하기 때문에, 쿠버네티스 설정 YAML을 검증하는 작업이 얼마나 중요한지 다시 한번 잘 보여줍니다.
취약점 세부 정보
이 취약점 자체의 세부 사항을 살펴보기 전에 먼저 쿠버네티스내의 몇 가지 주요 구성요소를 이해해야 합니다.
쿠버네티스 볼륨이란 무엇인가요?
쿠버네티스 볼륨 은 포드 간 데이터 공유를 지원하거나 포드의 수명 주기 외부에 영구적으로 저장하는 것을 목표로 하는 기능입니다. 개발자가 사용할 수 있는 볼륨 종류에는 여러 가지가 있습니다. 예를 들어, CVE-2023-3676에 대한 이전 리서치에서는 hostPath 볼륨을 사용했습니다. 이번 취약점에서는 쿠버네티스 내의 이와 다른 종류인 로컬 볼륨에 초점을 맞추고 있습니다. 로컬 볼륨은 사용자가 포드 내부에 디스크 파티션을 마운트할 수 있도록 설계된 반면, hostPath 볼륨은 사용자가 노드(호스트)의 디렉터리를 포드에 마운트할 수 있도록 설계되었습니다.
로컬 볼륨을 포함하는 포드를 생성하는 동안, kubelet 서비스는 (결국) ‘MountSensitive()‘ 함수에 도달하게 됩니다. 그 안에는 노드의 볼륨 위치와 포드 내부의 위치 간에 심볼릭 링크를 만드는 ‘exec.command‘에 대한 cmd 라인 호출이 있습니다(그림 1).
사용 편의성을 위해 작업에서 몇몇 버전의 명령 연결(그림 2)을 사용하는 터미널이 상당히 많습니다. "&&" 토큰을 사용해 터미널이 두 개 이상의 명령을 차례로 실행하는 Windows의 명령 프롬프트 (cmd) 도 이에 해당합니다.
C:\Users\user>echo "by using &&" && echo "we can execute multiple commands in the same command line"
"by using &&"
"we can execute multiple commands in the same command line"
C:\Users\user>
그림 2: cmd의 명령 연결
cmd 실행에서 매개변수 중 하나를 제어할 수 있다는 것은 명령 인젝션을 사용할 수 있음을 의미합니다. 하지만 몇 가지 전제 조건이 있는데, 사용자가 로컬 볼륨을 사용하려면 persistentVolume을 지정하거나 만들어야 합니다.
persistentVolumes란 무엇일까요?
persistentVolumes 은 클러스터 관리자가 포드의 수명 기간 동안 지속될 스토리지 공간을 미리 프로비저닝하기 위해 생성할 수 있는 스토리지 리소스입니다(그림 3). 일단 persistentVolume 이 생성되면, 사용자는 persistentVolumeClaim을 사용해 스토리지 공간을 요청할 수 있습니다.
여기에 인젝션이 배치될 수 있습니다. 공격자는 persistentVolume YAML 파일 내부의 "local.path" 매개변수 값을 변경해 마운팅 프로세스 중에 실행될 악성 명령을 추가할 수 있습니다.
그림 4에서는 노드에서 계산기를 여는 양성 ‘&calc.exe&&‘를 사용했지만 이 프로세스는 훨씬 더 악의적인 결과에 사용될 수 있습니다.
그림 5는 악성 계산기 명령이 삽입된 후 표적 노드에서 악용이 성공적으로 이뤄지는 모습을 보여줍니다.
패치 분석
인젝션의 기회를 제거하기 위해 쿠버네티스 팀은 cmd 호출을 삭제하고 동일한 작업을 수행하는 기본 GO 함수인 ‘os.Symlink()‘로 대체하기로 결정했습니다(그림 6).
이제 GO ‘os’ 라이브러리는 처음에 의도했던 대로 심볼릭 링크 연산만 수행합니다.
모든 쿠버네티스가 취약한가요?
사용자가 이 취약점의 영향을 받으려면 쿠버네티스가 1.28.4 이전 버전이어야 합니다. 아직 패치를 하지 않았다면 이 패치를 우선적으로 적용하는 것이 좋습니다. 클러스터 내에 Windows 노드가 있는 기업의 경우 이 부분에 취약점이 있기 때문에 더욱 그렇습니다.
다행히도 이것은 업계 표준이 아닌 것 같습니다. 관리자는 클러스터 컨트롤러에서 그림 7에 표시된 명령을 실행해 기업 클러스터에 Windows 노드가 포함되어 있는지 쉽게 테스트할 수 있습니다.
root@controller:~/$ kubectl get nodes -o wide --show-labels | grep “os=windows”
akswin000000 Ready agent 4d17h v1.26.6 agentpool=win,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=windows…
akswin000001 Ready agent 4d17h v1.26.6 agentpool=win,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=windows…
root@controller:~/$
그림 7: 클러스터의 Windows 노드를 표시하는 명령어
취약점 여부는 쉽게 확인할 수 있습니다. 'os=windows‘ 부분을 주목하세요. Windows 노드가 없는 경우, 이 명령은 출력이 없으므로 취약하지 않습니다.
방어
사용 가능한 유일한 방어 방법은 쿠버네티스를 1.28.3 이후 버전으로 패치하는 것입니다.
Akamai는 일부 기업과 네트워크의 경우 즉각적인 패치가 가능하지 않다는 것을 알고 있습니다. 이에 패치 미적용 시 리스크에 대응할 수 있도록 이러한 종류의 동작을 탐지하고 차단하는 데 도움이 되는 OPA 룰을 제공했습니다.
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "PersistentVolume"
path := input.request.object.spec.local.path
contains(path, "&")
msg := sprintf("malicious path: %v was found", [path])
}
OPA는 사용자가 노드에 들어오고 나가는 트래픽에 대한 데이터를 수신하고 수신된 데이터에 대해 정책 기반 조치를 취할 수 있는 오픈 소스 에이전트입니다.
이 취약점은 Windows 노드에만 영향을 미친다는 점에 유의하세요. 쿠버네티스 클러스터에 윈도우 노드가 없는 경우, 이 특정 취약점을 서둘러 패치할 필요는 없습니다. 하지만 시간이 되면 패치를 적용하는 것이 중요합니다.
이 문제는 소스 코드에 있어 위협이 계속 활성화되고 악용될 가능성이 높기 때문에, 클러스터에 Windows 노드가 없더라도 패치를 적용할 것을 강력히 권장합니다.
결론
이 취약점은 보안에서 공유 책임 모델이 중요한 이유를 보여주는 좋은 예입니다. 쿠버네티스 소스 코드에 입력 위생화 기능이 없다는 점을 인지하면 심각한 보안 영향을 피하기 위한 외부 예방 조치를 취할 수 있습니다.
2023년에만 7개의 서로 다른 명령어 주입 취약점이 발견되었으며, 코드의 다른 영역에서도 더 많은 취약점이 발견될 가능성이 있습니다. 방어팀과 소속 기업은 이러한 증가 트렌드에 더욱 주의를 기울여야 하며, 숨겨진 위협이 포함되어 있을 수 있기 때문에 YAML 파일 콘텐츠를 모니터링해야 합니다. 이 게시물에서 제공한 OPA 룰은 이러한 노력에 도움이 될 수 있습니다.
Akamai 보안 인텔리전스 그룹은 이 위협과 이와 유사한 위협을 지속적으로 모니터링하고 그 결과를 발표할 예정입니다. 이 취약점 및 기타 보안 리서치에 대한 최신 정보를 확인하려면 X(기존의 Twitter)에서 Akamai 보안 연구팀을 팔로우하시기 바랍니다.
Akamai는 매우 신속하게 대응하고 원활하게 소통해 주신 쿠버네티스 팀에 감사의 말씀을 드립니다.
공개 타임라인
2023/11/01 - 쿠버네티스 팀에 취약점 공개
2023/11/11 - 쿠버네티스 팀의 CVE 지정
2023/11/14 - 쿠버네티스, CVE 수정 사항 배포
2024/03/14 - 블로그 게시물 게시