
AWS EKS forensics: data sources and investigation tooling (Synacktiv)
Synacktiv's CSIRT publishes a detailed forensics guide for Amazon EKS, mapping the three data-source layers (Kubernetes control plane audit logs, data-plane/Container Insights logs, and AWS-level CloudTrail/GuardDuty) and giving concrete CloudWatch Logs Insights queries for detecting RBAC reconnaissance, role escalation via the 'escalate' verb, Pod Security Admission evasion, and static-pod node persistence. It also covers node-level acquisition via SSM/kubectl debug, ServiceAccount token theft, and correlating GuardDuty findings (PrivilegedContainer, ContainerWithSensitiveMount, ReverseShell, InstanceCredentialExfiltration) with CloudTrail to trace stolen node/pod IAM identities used outside the cluster.
What it is
Synacktiv CSIRT's EKS forensics guide, part of their Kubernetes Forensics series. Beyond the three-layer data-source map the summary names, the page carries verbatim CloudWatch Logs Insights queries, the enablement commands, node-level config paths, and an incident-response procedure (containment reflexes, acquisition ordering) the summary only gestures at.
Preconditions
EKS emits almost none of the relevant telemetry by default — control-plane logs are off, Container Insights and Runtime Monitoring are opt-in add-ons. Node access depends on compute mode: standard clusters run EC2 workers reachable via SSH/SSM; Auto Mode and Fargate give no direct node access, so establishing the mode is the first triage step. Runtime Monitoring (eBPF syscall visibility) does not support Fargate; without it, in-container command execution is a coverage gap.
What to do
Enable all five control-plane log types (single log group /aws/eks/[cluster]/cluster):
aws eks update-cluster-config --name <cluster> --region <region> \
--logging '{"clusterLogging":[{"types":["api","audit","authenticator","controllerManager","scheduler"],"enabled":true}]}'Detect a static-pod node-persistence — the pod is created by a system:node: identity and annotated kubernetes.io/config.source = "file" instead of "api":
fields @timestamp, user.username, objectRef.name,
`requestObject.metadata.annotations.kubernetes.io/config.source` as config_source
| filter verb = "create" and objectRef.resource = "pods"
| filter user.username like "system:node:"
| sort @timestamp descOn EKS-optimized AMIs the kubelet has no staticPodPath by default, so the attacker must first set it in /etc/kubernetes/kubelet/config.json and restart the service — an observable node artifact preceding the pod event.
Isolate stolen node-role activity in CloudTrail by excluding cluster egress:
fields @timestamp, eventName, sourceIPAddress, userAgent
| filter userIdentity.arn like "<node-role-name>"
| filter not (sourceIPAddress like /^10\.0\./) and sourceIPAddress != "<nat-gateway-eip>"
| stats count(*) as n by eventName, sourceIPAddress
| sort n descA burst of Describe*/List*/Get* from an off-cluster IP with an aws-cli user agent signals discovery from a stolen token.
Locate node-held projected ServiceAccount tokens for acquisition:
$ find /var/lib/kubelet/pods -path "*/kubernetes.io~projected/*/token" -type fResponse ordering that the summary omits:
- Do not
kubectl delete— it discards process state, the writable upper layer, and projected tokens, and a controller may reschedule the workload. Isolate at the network level (NetworkPolicies, Security Groups) while keeping liveness/readiness probes satisfied so the pod is not killed or moved. - Network isolation does not contain a stolen AWS identity — a token replays from anywhere. Rotate/revoke node-role credentials and leaked ServiceAccount tokens.
- Acquisition is a race against the pod lifecycle: live pod and node-local tokens first (volatile), centralised CloudWatch logs are durable and retrievable later.
Caveats
kubectl debug node/[node](mounts host under/host) is a last-resort access route — it schedules a workload on the node under examination and leaves freshcreate podsaudit entries.- The kubelet checkpoint API (Beta, on by default since v1.30) relies on CRIU, which EKS-optimized AMIs do not ship, so container checkpointing is not usable on EKS as of writing.
- GuardDuty is a black box (rules undisclosed, not extensible); a finding is a lead to confirm, never a verdict. A stolen pod identity has no dedicated GuardDuty finding equivalent to the node-role
InstanceCredentialExfiltration.
Organisations named here
- Container Insights
- EKS Pod Identity
- escalate verb
- IMDS
- IRSA
- static pod
Technologies named here
- CloudWatch Logs Insights
- Amazon EKS
- GuardDuty
- EKS worker nodes
- CloudTrail
- EKS Auto Mode
- kubectl
- AWS Fargate
- EKS-optimized AMIs
- amazon-cloudwatch-observability EKS add-on
- Fluent Bit
- CloudWatch agent
- AWS SSM Session Manager
- kubelet checkpoint API
- Falco
- Sysdig
- Wiz
- Traefik
- Argo CD
- AWS
- Kubernetes
- eBPF
Techniques named here
- Helm template injection via GitOps controller
- RBAC reconnaissance
- Pod Security Admission evasion attempt
- Node account self-privilege evaluation
- PrivilegeEscalation:Kubernetes/PrivilegedContainer
- Persistence:Kubernetes/ContainerWithSensitiveMount
- Execution:Runtime/ReverseShell
- Node/pod IAM identity abuse
- Static pod persistence
- ServiceAccount token theft from node filesystem
- kubectl debug node access
Related in Cloud & Infra


Orca Security: GenAI Risks in Cloud Environments
.png)
Google API keys keep working after you delete them
