Escalada de privilegios RBAC en Kubernetes: cómo detectarla
Encuentra escaladas de privilegios RBAC de Kubernetes en los audit logs: bindings a cluster-admin, verbos escalate, bind e impersonate, acceso anónimo.
TL;DR. En el audit log, una escalada RBAC es una escritura: un create, update o patch sobre clusterrolebindings, rolebindings, clusterroles o roles. Las peligrosas enlazan cluster-admin, conceden los verbos escalate, bind o impersonate o comodines *, o nombran a system:anonymous / system:unauthenticated. Necesitas los cuerpos de las solicitudes (nivel Request) para ver el roleRef y los subjects. Mira también el campo impersonatedUser y recuerda que el IAM cloud puede dar acceso al clúster sin ningún objeto RBAC de Kubernetes.
Un binding nuevo a cluster-admin es la señal más fiable de una intrusión seria en Kubernetes: así es como un atacante convierte un token prestado en una identidad permanente. ATT&CK lo recoge como T1098.006, Additional Container Cluster Roles.
RBAC en una tabla
El RBAC tiene cuatro tipos de objetos:
| Objeto | Ámbito | Concede |
|---|---|---|
Role | Namespace | Reglas (verbos sobre recursos) dentro de un namespace |
ClusterRole | Clúster | Reglas sobre recursos de clúster, o reutilizables entre namespaces |
RoleBinding | Namespace | Un Role o ClusterRole a sujetos, dentro de un namespace |
ClusterRoleBinding | Clúster | Un ClusterRole a sujetos, en todas partes |
El roleRef del binding nombra el rol; subjects lista usuarios, grupos o cuentas de servicio. Ambos van en el cuerpo de la solicitud, así que una política que registra las escrituras RBAC en Metadata te dice «se creó un ClusterRoleBinding llamado node-health-admin» y nada sobre lo que concede.
Los verbos de prevención de escalada
Kubernetes bloquea la escalada ingenua: la documentación de RBAC explica que solo puedes crear o modificar un rol con permisos que ya tienes, y solo enlazar un rol cuyos permisos ya tienes. Tres verbos levantan esos controles:
escalatesobre roles o clusterroles: escribir un rol con más permisos de los que tienes.bindsobre roles o clusterroles: enlazar un rol que no tienes, incluido cluster-admin.impersonatesobre usuarios, grupos o cuentas de servicio: actuar como otro.
Las buenas prácticas de RBAC incluyen los tres entre los riesgos de escalada de privilegios, junto con los comodines *, que además cubren cualquier tipo de recurso que se añada en el futuro. Un rol que concede cualquiera de ellos a una persona o workload es un camino hacia cluster-admin.
Detecciones
El analizador de audit logs revisa las escrituras RBAC con estas reglas:
| Regla | Severidad | Lógica |
|---|---|---|
| Binding a cluster-admin creado | Crítica | (Cluster)RoleBinding con roleRef.name: cluster-admin |
| Acceso concedido a usuarios anónimos / no autenticados | Crítica | Binding con sujeto system:anonymous o system:unauthenticated |
| El rol otorga escalate / bind / impersonate o comodines | Alta | (Cluster)Role escrito con esos verbos o con verbos/recursos * |
| Solicitud realizada suplantando otra identidad | Media | impersonatedUser presente |
| Solicitud anónima permitida | Alta | system:anonymous permitido más allá de los endpoints de salud, versión y descubrimiento OIDC |
| Role binding a nivel de clúster creado | Baja | Cualquier ClusterRoleBinding escrito por una identidad que no es de sistema |
La regla de severidad baja existe por el hueco de registro mencionado antes: cuando falta el cuerpo, un ClusterRoleBinding nuevo sigue mereciendo un vistazo, y la anotación authorization.k8s.io/reason de las solicitudes posteriores lo nombrará cuando se use.
Leer un binding malicioso
Un ejemplo ficticio, registrado en nivel RequestResponse:
{
"verb": "create",
"user": { "username": "system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard" },
"sourceIPs": ["203.0.113.45"],
"userAgent": "kubectl/v1.30.2 (linux/amd64) kubernetes/3968350",
"objectRef": { "resource": "clusterrolebindings", "name": "node-health-admin",
"apiGroup": "rbac.authorization.k8s.io" },
"requestObject": {
"kind": "ClusterRoleBinding",
"metadata": { "name": "node-health-admin" },
"roleRef": { "apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "cluster-admin" },
"subjects": [ { "kind": "ServiceAccount", "name": "node-health", "namespace": "kube-system" } ]
},
"responseStatus": { "code": 201 }
}
Destacan tres cosas: una cuenta de servicio creando objetos RBAC, desde una dirección de Internet, con kubectl; un nombre elegido para parecer un componente del sistema; y una cuenta de servicio nueva en kube-system como sujeto. El siguiente paso del atacante suele ser una TokenRequest para esa cuenta de servicio, que le da una credencial independiente de la que robó al principio. El recorrido ficticio sigue exactamente esta secuencia.
Impersonation
Quien tenga el permiso impersonate puede enviar las cabeceras Impersonate-User, Impersonate-Group (y relacionadas); la solicitud se autoriza entonces como la identidad suplantada. El evento de auditoría conserva ambas: user es quien llamó de verdad e impersonatedUser quien dijo ser. Informa siempre de las dos y revisa quién tiene impersonate: en la mayoría de los clústeres esa lista debería ser muy corta.
kubectl --as=<user> y --as-group usan estas cabeceras; algunos proxies de acceso y dashboards también, por eso el analizador marca la impersonation con severidad media y deja el juicio en tus manos.
Acceso anónimo
Salvo que se desactive, las solicitudes sin credenciales se autentican como system:anonymous en el grupo system:unauthenticated, como describe la documentación de autenticación. Los endpoints de salud y descubrimiento suelen estar abiertos. Un binding que conceda algo más a estos sujetos significa que cualquiera que pueda alcanzar el API server tiene esos derechos. Consulta la entrada del glosario system:anonymous para ver los ajustes que lo controlan.
Vías de escalada sin escritura RBAC
No toda escalada aparece como un binding:
- IAM cloud. En EKS, las access entries y el ConfigMap
aws-authasocian principales de IAM a identidades de Kubernetes; asociar una política de acceso de administrador a una access entry ocurre en la API de AWS (CloudTrail), no en el audit log de Kubernetes. GKE concede acceso al clúster mediante roles de IAM de Google Cloud, y AKS mediante asignaciones de roles de Azure RBAC cuando está activado Azure RBAC para Kubernetes. Revisa estos cambios en los logs del plano de control cloud. - Agregación de ClusterRoles. Un ClusterRole con etiquetas que coinciden con una
aggregationRuleve sus reglas fusionadas en el rol agregado. Añadir un rol así amplía en silencio todos los bindings del rol agregado. - Creación de workloads. Quien puede crear pods en un namespace puede montar cualquier cuenta de servicio de ese namespace y actuar como ella. Consulta secrets y robo de tokens.
- Certificados. El permiso para aprobar CertificateSigningRequests permite emitir certificados de cliente para identidades arbitrarias.
- Webhooks de admisión. Controlar la configuración de los webhooks permite ver o reescribir objetos al crearse.
Consultas de búsqueda
Líneas JSON en bruto, bindings a cluster-admin:
jq -c 'select((.objectRef.resource == "clusterrolebindings" or .objectRef.resource == "rolebindings")
and (.verb == "create" or .verb == "update" or .verb == "patch")
and .requestObject.roleRef.name == "cluster-admin")
| {t: .requestReceivedTimestamp, user: .user.username, ip: .sourceIPs,
name: .objectRef.name, subjects: .requestObject.subjects}' audit.log
EKS, CloudWatch Logs Insights, todas las escrituras RBAC:
fields @timestamp, user.username, verb, objectRef.resource, objectRef.name
| filter @logStream like "kube-apiserver-audit"
| filter objectRef.apiGroup = "rbac.authorization.k8s.io"
| filter verb in ["create", "update", "patch", "delete"]
| sort @timestamp desc
Remediación
- Guarda los objetos implicados (
kubectl get clusterrolebinding <name> -o yaml) como evidencia y luego bórralos. - Borra y vuelve a crear cualquier cuenta de servicio que recibiera cluster-admin, para que los tokens emitidos para ella dejen de funcionar.
- Revisa todos los bindings a cluster-admin y a roles que pueden escribir RBAC, leer secrets o hacer exec en pods.
- Quita
escalate,bind,impersonatey los comodines de los roles que no los necesiten estrictamente. - Desactiva la autenticación anónima o limítala a los endpoints de salud.