Skip to content

Esta herramienta no está afiliada a The Linux Foundation, la Cloud Native Computing Foundation (CNCF) ni el proyecto Kubernetes, ni está respaldada ni patrocinada por ellos. Kubernetes y K8s son marcas registradas de The Linux Foundation. EKS, GKE, AKS y otros nombres son marcas comerciales de sus respectivos propietarios.

Detectar kubectl exec, attach y port-forward en audit logs

Cómo aparecen kubectl exec, attach, cp, port-forward y nodes/proxy en los audit logs de Kubernetes, qué revela el comando y cómo distinguir admin de abuso.

Publicado el 6 min de lectura

TL;DR. kubectl exec y kubectl attach son solicitudes a los subrecursos pods/exec y pods/attach; kubectl port-forward llega a pods/portforward. Filtra por objectRef.subresource, acepta tanto create como get como verbos, espera el código de respuesta 101 y lee el comando en los parámetros command= de requestURI. Un exec hecho por una cuenta de servicio es raro y debe tratarse como severidad alta. El acceso a nodes/proxy llega directamente al kubelet y es peor. Nada de lo que se teclea dentro de la sesión se registra.

Ejecutar un comando en un contenedor es la técnica ATT&CK T1609, Container Administration Command. También es lo que más hace un administrador legítimo durante una caída. El objetivo no es marcar cada exec, sino que cada exec tenga explicación.

Cómo se ve un exec en el audit log

kubectl exec -it api-6c9f7d8b5-q2w8x -n shop -- sh produce un evento como este (recortado, nombres ficticios):

{
  "verb": "create",
  "stage": "ResponseStarted",
  "requestURI": "/api/v1/namespaces/shop/pods/api-6c9f7d8b5-q2w8x/exec?command=sh&container=api&stdin=true&stdout=true&tty=true",
  "user": { "username": "alice@example.com" },
  "objectRef": { "resource": "pods", "namespace": "shop", "name": "api-6c9f7d8b5-q2w8x", "subresource": "exec" },
  "responseStatus": { "code": 101 }
}

Lo que conviene saber:

  • Verbo. El endpoint exec se ha autorizado y auditado tradicionalmente como create. Desde Kubernetes 1.31, kubectl usa WebSockets por defecto para el streaming, y un upgrade de WebSocket empieza como un GET HTTP, así que según las versiones puedes ver get. Busca ambos.
  • Stage. El exec es una solicitud larga. El evento ResponseStarted se escribe al abrirse la sesión; ResponseComplete, solo al cerrarse. Si la política no omite nada, tendrás los dos.
  • Código de respuesta. 101 Switching Protocols significa que la sesión se estableció. Un 403 significa que alguien lo intentó y RBAC dijo que no, lo cual ya es interesante.
  • Comando. Cada argumento es un parámetro command= distinto, codificado en URL: command=chroot&command=%2Fhost&command=sh es chroot /host sh. Como está en la URI, se registra incluso en nivel Metadata.

Consulta la anatomía de un evento de auditoría para el resto de campos.

Attach, cp, debug y port-forward

Acción del clienteRastro de auditoríaQué mirar
kubectl attachpods/attachSin comando: se conecta al proceso principal del contenedor
kubectl cppods/exec con command=tarLa dirección (tar cf = copia desde el pod, tar -x… = copia hacia el pod) y las rutas
kubectl debug (contenedor efímero)patch sobre pods/ephemeralcontainers y luego pods/attachLa imagen y si comparte el espacio de procesos del objetivo
kubectl port-forwardpods/portforward con ports=Qué puerto: 5432, 6379 o 3306 son bases de datos
kubectl proxy / proxy de API hacia un nodonodes/proxyAcceso directo a la API del kubelet

kubectl cp merece una revisión aparte: copiar un archivo hacia un pod es la forma de dejar herramientas sin descargar una imagen nueva, y copiar desde un pod es exfiltración a través del API server.

kubectl port-forward abre un túnel desde la máquina del cliente hacia la red de pods. Se salta los ingress controllers y, para el tráfico del cliente, las network policies que solo filtran tráfico entre pods. Un port-forward hacia un pod de base de datos hecho por una identidad que nunca lo hace es una pista sólida.

nodes/proxy: exec sin evento de exec

El subrecurso nodes/proxy permite hablar con la API del kubelet a través del API server. El kubelet puede ejecutar comandos en cualquier contenedor de ese nodo y leer sus logs. El API server audita la solicitud a nodes/proxy, pero no como un pods/exec, así que las detecciones basadas en exec no la ven. Las buenas prácticas de RBAC de Kubernetes incluyen el acceso al subrecurso proxy de los nodos entre las vías de escalada de privilegios.

Peor aún: quien pueda alcanzar el puerto del kubelet de un nodo con credenciales válidas habla con él directamente; esas llamadas nunca llegan al API server y nunca aparecen en el audit log. Ahí el control son los ajustes de autenticación y autorización del kubelet.

Distinguir trabajo de administración de abuso

El analizador de la página principal divide el exec en cuatro reglas:

ReglaSeveridadLógica
Comando interactivo en un contenedor (exec / attach)Mediapods/exec o pods/attach permitido, por una identidad humana que no es de sistema
Una cuenta de servicio ejecutó un comando en un contenedorAltaLo mismo, por una identidad system:serviceaccount:
Port-forward hacia un podMediapods/portforward permitido, identidad que no es de sistema
API del kubelet alcanzada a través de nodes/proxyAltanodes/proxy permitido, identidad que no es de sistema

¿Por qué esta división? Los workloads casi nunca hacen exec en otros pods. Cuando una cuenta de servicio lo hace, suele ser un token sacado de un pod y reutilizado por una persona, a menudo con un user agent kubectl/ desde una IP inesperada, algo que detectan otras dos reglas (consulta robo de tokens de cuenta de servicio). Los sistemas de CI y algunos operadores hacen exec de forma legítima; añádelos explícitamente a la lista de permitidos tras comprobarlo.

Para identidades humanas, haz el triaje de cada sesión con cuatro preguntas:

  1. ¿Quién y desde dónde? ¿Admin conocido, rango de IPs conocido, user agent normal?
  2. ¿Cuándo? ¿Durante una ventana de cambios o un incidente, o a una hora rara?
  3. ¿Dónde? ¿Qué namespace y qué pod; es un pod privilegiado o un componente de kube-system?
  4. ¿Qué? El comando: sh/bash interactivo, o algo como cat /var/run/secrets/kubernetes.io/serviceaccount/token, curl … | sh, chroot /host, nsenter?

Un chroot /host o un nsenter --target 1 dentro de un pod con un hostPath de / o hostPID es un escape de contenedor; consulta pods privilegiados y escape de contenedores.

Consultas de búsqueda

Líneas JSON en bruto con jq:

jq -c 'select(.objectRef.subresource == "exec" or .objectRef.subresource == "attach"
              or .objectRef.subresource == "portforward")
       | {t: .requestReceivedTimestamp, user: .user.username, ip: .sourceIPs,
          ns: .objectRef.namespace, pod: .objectRef.name,
          sub: .objectRef.subresource, uri: .requestURI, code: .responseStatus.code}' audit.log

EKS, CloudWatch Logs Insights:

fields @timestamp, user.username, sourceIPs.0, objectRef.namespace, objectRef.name, requestURI
| filter @logStream like "kube-apiserver-audit"
| filter objectRef.subresource in ["exec", "attach", "portforward"]
| sort @timestamp desc

AKS, Log Analytics (tabla específica del recurso):

AKSAudit
| where RequestUri has "/exec" or RequestUri has "/attach" or RequestUri has "/portforward"
| project TimeGenerated, User, SourceIps, UserAgent, RequestUri, ResponseStatus

En GKE, filtra Cloud Logging por protoPayload.methodName:"pods.exec" (y pods.attach, pods.portforward).

Reduce la superficie de ataque

  • Restringe pods/exec, pods/attach y pods/portforward a un pequeño grupo de emergencia; son recursos RBAC separados, así que puedes conceder get sobre pods sin ellos.
  • No concedas nodes/proxy ni a personas ni a workloads.
  • Alerta en cada exec de una cuenta de servicio y en cada exec dentro de kube-system.
  • Conserva el audit log: las sesiones exec son cortas y puede que el pod ya no exista mañana.

FAQ

¿Cómo se detecta kubectl exec en los audit logs de Kubernetes?

Busca eventos cuyo objectRef.subresource sea exec (o attach) sobre el recurso pods. Según las versiones del cliente y del servidor, el verbo es create o get, y el código de respuesta es 101 cuando el upgrade de streaming tiene éxito. El comando está en requestURI como parámetros command= repetidos.

¿Se puede ver lo que se tecleó dentro de una shell de kubectl exec?

No. El audit log registra la solicitud que abrió la sesión y su comando inicial, no los bytes intercambiados después. Las pulsaciones y los procesos dentro del contenedor requieren herramientas de runtime como Falco o un EDR en el nodo.

¿Aparece kubectl cp en los audit logs?

Sí, como un exec. kubectl cp ejecuta tar dentro del contenedor a través de pods/exec, así que el evento de auditoría muestra command=tar con sus argumentos en requestURI.

Artículos relacionados

Artículos relacionados

Detecta criptominería en clústeres de Kubernetes con los audit logs: imágenes y argumentos de mineros, registries inusuales, persistencia con CronJob.
Detecta la preparación de un escape de contenedor en los audit logs de Kubernetes: pods privilegiados, hostPID, hostPath de / y DaemonSets en kube-system.
Encuentra escaladas de privilegios RBAC de Kubernetes en los audit logs: bindings a cluster-admin, verbos escalate, bind e impersonate, acceso anónimo.

Esta herramienta no está afiliada a The Linux Foundation, la Cloud Native Computing Foundation (CNCF) ni el proyecto Kubernetes, ni está respaldada ni patrocinada por ellos. Kubernetes y K8s son marcas registradas de The Linux Foundation. EKS, GKE, AKS y otros nombres son marcas comerciales de sus respectivos propietarios.