The Bind, Escalate and Impersonate Verbs For Privilege Escalation In The Kubernetes Cluster

Yani
InfoSec Write-ups
Published in
10 min readMay 12, 2023

--

Photo by Tori Nefores on Unsplash

Kubernetes role binding plays a critical role in controlling access to resources within a Kubernetes cluster, it grants the permissions defined in a role to a user or set of users. RBAC Role or ClusterRole consists of rules that define a specific set of permissions(in the forms of verbs) including some lesser-known ones like bind, escalate, and impersonate. It's important to have awareness of these permissions and understand their implications in order to effectively manage access control. In this blog post, we'll explore the three specific permissions within RoleBindings/ClusterRoleBindings : bind, impersonate, and escalate. By understanding the risks associated with these permissions and implementing best practices, you can enhance the security of your Kubernetes environment.

RBAC Role and ClusterRole

A RBAC Role is designed to define permissions within a specific namespace, while a ClusterRole, on the other hand, is not tied to any specific namespace and provides permissions across the entire cluster. This distinction is important to understand when defining and managing access control in Kubernetes.

Here’s an example Role in the “default” namespace that can be used to grant read access to pods:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]

A ClusterRole can be used to grant the same permissions as a Role, but it can be cluster-scoped. Below is an example of a ClusterRole that grants read access to secrets within a specific namespace or across all namespaces depending on how it is bound (using RoleBinding or ClusterRoleBinding):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "watch", "list"]

In both above examples, the operations executed on available resources like Deployments, Services, and Pods in a Kubernetes cluster are called verbs. Other common examples are Create, Read, Update, or Delete (CRUD). There are also specialized verbs as we mentioned in the beginning, bind , escalate and impersonate. bind and escalate verbs on roles and clusterroles resources for RBAC, impersonate verb on users, group, and serviceaccounts, etc. for authentication.

Environment Setup

Now assume we already have an existing context named ‘admin’ in the kubectl configuration and we can use it to operate the kubernetes as a cluster administrator. Before we explore these Verbs, let’s set up a scenario in Kubernetes v1.24 where we create a “dev” service account without any associated roles.

$ kubectl create serviceaccount dev

In Kubernetes versions prior to v1.22, a token for the service account was automatically generated. However, in more recent versions, the default behavior is that newly created service accounts will not have an accompanying token.

$ kubectl describe serviceaccounts/dev
Name: dev
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Events: <none>

To create a token for the service account, we can execute the command kubectl create token. Once the token is created, we can use it to set up a configuration context and utilize it for further operations.

$ TOKEN=$(kubectl create token dev --duration=12h)
$ kubectl config set-credentials dev --token=$TOKEN
$ kubectl config set-context dev --user=dev --cluster=kubernetes

Now we have two contexts for the kubectl configuration, one is enable us to operate the cluster as “admin”, the other is as “dev” service account.

$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* admin kubernetes 313846530892193692
dev kubernetes dev

We create a role name “pod-reader” for the tests in the subsequent sections. This “pod-reader” role can “read” the pods in the current (i.e. default) namespace.

kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods

Please note that the “dev” service account does not have any roles bound to it. In the following demonstration, we will showcase the usage of the bind, escalate, and impersonate permissions.

Bind Verb

To create or update a role binding, you either need to have all the permissions specified in the referenced role or the explicit authorization to use the bind verb on the referenced role. In other words, to create or update a role binding, you need to meet either of the following conditions:

  1. You already possess all the permissions specified in the referenced role, at the same scope as the role binding. This means that you have the necessary privileges to perform the required actions defined by the role.
  2. You have been explicitly authorized to use the bind verb on the referenced role. This means that even if you don't have all the permissions in the role, you can still create or update the role binding by having the specific authority to bind the role to the user or group.

The bind privilege grants a user the ability to create/update bindings to cluster roles or roles, resulting in an elevation of the effective permissions within the cluster.

There is an diagram about how the bind permission enable "dev" service account to bypass of Kubernetes in-built protections.

Next we will proceed to demonstrate the entire process in action.

createBinding.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: create-binding
rules:
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- clusterrolebindings
- rolebindings
verbs:
- create

The cluster administrator create the ClusterRole and bind it to “dev” service account.

$ kebectl apply -f createBinding.yaml

$ kubectl create rolebinding create-binding-dev --clusterrole=create-binding --serviceaccount=default:dev

Switch kubectl configuration to “dev” context and try if “dev” service account has the permission to bind the “pod-reader” role to itself.

$ kubectl config use-context dev
Switched to context "dev".

The “dev” service account attempts to bind the “pod-reader” to itself in order to escalate its permissions.

$ kubectl create rolebinding pod-reader-dev --role=pod-reader --serviceaccount=default:dev 
error: failed to create rolebinding: rolebindings.rbac.authorization.k8s.io "pod-reader-dev" is forbidden: user "system:serviceaccount:default:dev" (groups=["system:serviceaccounts" "system:serviceaccounts:default" "system:authenticated"]) is attempting to grant RBAC permissions not currently held:
{APIGroups:[""], Resources:["pods"], Verbs:["get" "list" "watch"]}

We can observe that the operation has been rejected as the “dev” service account doesn’t have the necessary permission {APIGroups:[""], Resources:["pods"], Verbs:["get" "list" "watch"]} to successfully complete the action.

Now let’s explore how the bind permission can change the situation.

Switch kubectl configuration back to “default” context and create a ClusterRole with bind permission and bind the clusterrole to "dev" service account.

bind.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: bind
rules:
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- clusterroles
- roles
verbs:
- bind
$ kubectl config use-context default
$ kubectl apply -f bind.yaml
$ kubectl create rolebinding bind-dev --clusterrole=bind --serviceaccount=default:dev

Now let see the what happens after the bind permission has been bound to "dev" service account

$ kubectl config use-context dev
$ kubectl create rolebinding pod-reader-dev --role=pod-reader --serviceaccount=default:dev
rolebinding.rbac.authorization.k8s.io/pod-reader-dev created

The “dev” service account has successfully elevated its permissions by binding the “pod-reader” role to itself.

Clean up

Before we move to the next section, we need to run the following commands to delete the resources created in this section.

$ kubectl config use-context "admin"
$ kubectl delete rolebinding pod-reader-dev
$ kubectl delete rolebinding create-binding-dev
$ kubectl delete rolebinding bind-dev
$ kubectl delete clusterrole bind
$ kubectl delete clusterrole create-binding

Escalate Verb

To create or update a role in Kubernetes, one of the following conditions must be met:

  1. You possess all the permissions specified in the role definition, and these permissions are applicable to the same scope as the object being modified. For instance, if it’s a ClusterRole, you must have cluster-wide permissions. If it’s a Role, you need permissions within the same namespace or cluster-wide.
  2. You have been explicitly granted the authority to use the escalate verb on the "roles" or "clusterroles" resource within the "rbac.authorization.k8s.io" API group.

These conditions ensure that only authorized users with the necessary permissions or explicit escalation rights can create or update roles, maintaining proper access control within the Kubernetes cluster.

The escalate privilege empowers a user to modify roles or cluster roles to which they are bound, effectively increasing their level of access and privileges.

In this section, we will explore the escalation verb which allows "dev" service account to modify the role that is bound to itself. This modification will enable the account to elevate its permissions and gain the ability to create pods.

Firstly, as the admin, we need to assign two roles to the “dev” service account. The first role is named “role-editor” and it enables the service account to retrieve and modify roles. The second role is named “pod-reader” and it allows the service account to access information about pods.

$  kubectl config use-context "admin"
$ kubectl create rolebinding pod-reader-dev --role=pod-reader --serviceaccount=default:dev
$ kubectl create role role-editor --verb=get --verb=patch --resource=role
$ kubectl create rolebinding role-editor-dev --role=role-editor --serviceaccount=default:dev

After granted with these roles, the “dev” service account can view the roles, get pod information, but it can’t create pod.

$  kubectl config use-context "dev"
$ kubectl run nginx --image=nginx
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:dev" cannot create resource "pods" in API group "" in the namespace "default"

When the “dev” service account attempts to add the “create” permission to the existing “pod-reader” role, the action fails because it does not have the necessary permissions granted to perform this operation.

$ kubectl edit role pod-reader  # add "create" into the list of verbs and save it
error: roles.rbac.authorization.k8s.io "pod-reader" could not be patched: roles.rbac.authorization.k8s.io "pod-reader" is forbidden: user "system:serviceaccount:default:dev" (groups=["system:serviceaccounts" "system:serviceaccounts:default" "system:authenticated"]) is attempting to grant RBAC permissions not currently held:
{APIGroups:[""], Resources:["pods"], Verbs:["create"]}

Now, the escalate permission comes into the picture. If the "admin" creates the "escalate" role and assigns it to the "dev" service account due to some reason, the service account will gain the ability to escalate its permissions.

escalate.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: escalate
rules:
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- clusterroles
- roles
verbs:
- escalate
$  kubectl config use-context "admin"
$ kubectl apply -f escalate.yaml
$ kubectl create rolebinding escalate-dev --clusterrole=escalate --serviceaccount=default:dev

Let’s take a look at what the “dev” service account can do this time after being assigned the escalate permission. With the escalate permission, the "dev" service account has the ability to modify the "pod-reader" role and add the "create" verb to the list of permissions.

$  kubectl config use-context "dev"
$ kubectl edit role pod-reader # add "create" into the list of verbs and save it
role.rbac.authorization.k8s.io/pod-reader edited

The “dev” service account successfully expands its permission and is able to create a pod by utilizing the updated “pod-reader” role.

$ kubectl run nginx --image=nginx 
pod/nginx created

Clean up

We have reached the end of the section highlighting the escalate permission. To clean up and delete the resources created during this demonstration, please execute the following commands.

$ kubectl config use-context "admin"
$ kubectl delete rolebinding pod-reader-dev
$ kubectl delete role role-editor
$ kubectl delete rolebinding role-editor-dev
$ kubectl delete clusterrole escalate
$ kubectl delete rolebinding escalate-dev

Impersonate Verb

The impersonate privilege gives an user the ability to assume the identity of other users, thereby acquiring their permissions within the cluster. For example, the impersonation feature in Kubernetes allows an admin to temporarily assume the identity of another user, including their associated roles and permissions. This can be useful for debugging and troubleshooting authorization policies

To quickly assumes another identity, such as the “dev” service account impersonating the built-in admin service account, you can utilize the --as flag in the kubectl command. There is no impersonate permission associated with "dev" account initially, as a result, it is unable to perform the actions successfully.

$ kubectl get pods --all-namespaces
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:dev" cannot list resource "pods" in API group "" at the cluster scope

$ kubectl --as=system:serviceaccount:kube-system:admin get pods --all-namespaces
Error from server (Forbidden): serviceaccounts "admin" is forbidden: User "system:serviceaccount:default:dev" cannot impersonate resource "serviceaccounts" in API group "" in the namespace "kube-system"

To grant the “dev” service account the ability to impersonate other users, we need to create a custom role with the impersonate permission and then bind it to the service account. Here are the steps to follow:

impersonate.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonate
rules:
- apiGroups:
- ""
resources:
- serviceaccounts
verbs:
- impersonate

Create the ClusterRole and bind it to the “dev” service account.

$ kubectl config use-context "admin"
$ kubectl apply -f impersonate.yaml
$ kubectl create clusterrolebinding impersonate-dev --clusterrole=impersonate --serviceaccount=default:dev

Once these steps are completed, the “dev” service account will have the impersonate permission and will be able to impersonate other users/service accounts, granting it the ability to perform actions on their behalf.

$ kubectl config use-context "dev" 
$ kubectl --as=system:serviceaccount:kube-system:admin get pods --all-namespaces

The diagram in the below illustrates the complete process:

Clean up

To clean up and delete the resources created in this section, please execute the following commands.

$ kubectl config use-context "admin"
$ kubectl delete clusterrolebinding impersonate-dev
$ kubectl delete clusterrole impersonate

Conclusion

In this blog, we explored the intricacies of RBAC permissions in Kubernetes and shed light on the often overlooked bind, escalate, and impersonate permissions. By understanding and properly configuring these permissions, you can ensure a secure and controlled access environment in your Kubernetes deployments. Remember, maintaining strong RBAC practices is crucial to protecting sensitive information, preventing unauthorized access, and safeguarding your Kubernetes clusters. Stay vigilant in managing and reviewing permissions to uphold the security of your infrastructure.

--

--

Focusing on Security for Web Application, AWS and Kubernetes, etc. | CKA&CKS, AWS Security & ML Specialty | https://www.linkedin.com/in/yani-dong-041a1b120/