Workload Identities Have a Home Address — So Watch Where They Get Used
Cloud hands every workload a privileged identity by default. Unlike other machine identities it's bound to one host — use from elsewhere is theft.
Every EC2 instance, every Kubernetes pod, every Lambda function, every Azure VM in your environment was handed a privileged identity the moment it booted — automatically, by the cloud provider, often with more access than it needs. You didn’t approve most of them. You probably can’t list them. And they are, collectively, the most powerful and least-watched credentials in your environment.
These are workload identities: IAM roles attached to compute, managed identities, service principals, IRSA tokens, OIDC-federated pipeline credentials. They’re the engine of cloud automation, and they’re a lateral-movement surface that moves through the control plane — through CloudTrail, not through your network — which is exactly the place most SOCs ingest logs but never actually build detections.
But workload identities have one property that makes them the most detectable identity you own, and almost nobody exploits it: unlike a human or a SaaS token, a workload identity has a home address. An EC2 instance-role credential is supposed to be used from exactly one place — that instance. Use it from anywhere else, and you’re not looking at an anomaly you have to reason about. You’re looking at theft.
The Identity Nobody Provisioned On Purpose
In the NHI post I covered why non-human identities outnumber humans by 45 to 1 and up to 144 to 1 in cloud-native environments. Workload identities are the cloud-native core of that population, and the numbers specific to them are worse than the average.
42% of machine identities carry privileged access, 77% of machine accounts are unmanaged — no owner, no review, no lifecycle — and 50% of organizations suffered a breach tied to a compromised machine identity in the past year [1]. Attacks targeting machine credentials rose more than 65% year over year (CrowdStrike 2025). This is not a slow-burn governance concern. It is the fastest-growing initial-access and lateral-movement surface in cloud.
The reason it’s underwatched is the reason it exists: it’s invisible by design. A workload identity is provisioned automatically so developers don’t have to manage keys. That same automation means no human ever decided this workload should have this access, no one is accountable for it, and — because the credential lives on the machine and is assumed silently — nothing about its day-to-day use looks like a security event. It’s a privileged identity with no MFA, no human behind it, and no provisioning decision to audit. As standing privilege, it’s the identity-creep problem in its purest cloud form: 42% privileged, 77% unmanaged, accumulating by default.
How They Get Taken
Three patterns dominate, and all three end the same way: an attacker holding a valid workload credential and using it to move.
IMDS theft via SSRF. The instance metadata service hands an instance its IAM credentials over a link-local HTTP endpoint. If an application on that instance has a server-side request forgery bug, an attacker can make the app fetch its own credentials and hand them over. This is the dominant path. CVE-2025-51591 [2] — an SSRF in Pandoc — was exploited from August 2025 to do exactly this against AWS IMDS, with the same actors probing GCP via unrelated SSRF bugs. The lineage runs back to UNC2903 abusing an Adminer SSRF in 2021. The structural problem: as of late 2024, only 32% of EC2 instances had moved to IMDSv2, which neutralizes GET-based SSRF. The other 68% are one SSRF bug away from leaking a privileged credential.
OIDC federation abuse. Keyless authentication — a GitHub Actions pipeline assuming an AWS role via OIDC — is more secure than long-lived keys, but its security rests entirely on how tightly the cloud validates the token’s subject claim. A trust policy that matches repo:my-org/main-repo:ref:refs/heads/main is safe. One that wildcards to repo:my-org/* is a confused deputy: an attacker who can create a repo or branch under that org matches the policy and walks away with temporary AWS credentials, sometimes at administrative scope (demonstrated by Datadog Security Labs [3]). AWS began blocking the creation of wildcarded trust policies in June 2025 — but every role created before that is still live and still vulnerable.
Role chaining. Once an attacker holds one workload credential, AssumeRole is a lateral-movement primitive. They assume into a second role, then a third, traversing the IAM graph entirely within the control plane. No network traffic crosses a boundary you’re watching. The movement is a sequence of CloudTrail events — which is only lateral movement you can see if you’re detecting on CloudTrail at the role-assumption layer.
”Isn’t This Just a Misconfiguration Problem?”
The obvious objection: this is hygiene. Enforce IMDSv2, scope your OIDC trust policies, least-privilege the roles, and there’s nothing left to detect. Detection is patching over sloppy configuration.
Three problems with betting purely on that. First, prevention is empirically losing the race, the same way it loses on identity creep: IMDSv2 has been available for years and adoption is 32%; AWS auto-blocks new bad OIDC policies but every pre-existing role stays exposed; 77% of machine accounts are unmanaged. The configuration debt is enormous and it’s not being paid down fast enough to rely on.
Second, and more fundamental: the headline attack here is not a misconfiguration. An SSRF bug in your application is a code vulnerability, and when it’s exploited, a perfectly configured IMDSv2 instance still hands over a valid credential to the application — because the application is who’s asking. Least privilege limits the blast radius; it does not stop the theft. The moment your app gets popped, the attacker inherits whatever that workload can do, and no amount of trust-policy hygiene changes that.
Third, the credential is valid. That’s the whole point. Prevention assumes you can keep the credential from being stolen. Detection assumes it will be, and asks the only question that still works after the theft: is this credential being used from somewhere it has no business being? That question has an answer even when every preventive control did its job and the app still got compromised.
The Detection Gift: A Workload Identity Has a Home
In the NHI post, I argued that machine identities have no location, so geo-based detection — impossible travel, login geography — returns null against them. Workload identities are the exception that proves the rule, and the exception is a gift.
An EC2 instance-role credential is bound to one instance. An IRSA token belongs to one pod in one cluster. A managed identity lives on one VM. These credentials are not supposed to roam — they have a physical home, and the cloud audit log records where every call using them came from. That turns the hardest problem in NHI detection (what is “normal” for an identity with no human behind it) into the easiest: normal is here, and anywhere else is theft.
This is not theoretical. The public detection rule “Malicious Usage Of IMDS Credentials Outside Of AWS Infrastructure” (Sigma) [4] keys on exactly this: an assumed-role instance identity taking an action from a source that isn’t AWS-internal. AWS GuardDuty’s UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS finding [5] does the same — instance credentials seen from an IP outside AWS. Elastic’s “AssumeRoleWithWebIdentity from Kubernetes SA and External ASN” [6] applies the identical logic to IRSA: a Kubernetes service account reaching STS from an autonomous system that isn’t Amazon’s is a stolen workload token. The thesis is encoded in shipping rules.
Here’s the shape, modeled on the public IMDS-outside-AWS pattern:
# Detection rationale: an EC2 instance-role credential is bound to its instance
# and should only ever call AWS APIs from inside AWS infrastructure (or SSM).
# An assumed-role *instance* identity making calls from a source that is not
# AWS-internal means the temporary credential has left the instance — i.e. it
# was exfiltrated via SSRF/IMDS and is being replayed from attacker infra.
# This keys on the credential's HOME, not on any known-bad value.
title: Instance-Role Credential Used Outside AWS Infrastructure
status: stable
logsource:
product: aws
service: cloudtrail
detection:
selection:
# the calling identity is an EC2 instance role (assumed-role/aws:...)
userIdentity.arn|re: '.+:assumed-role/aws:.+'
filter_internal:
- sourceIPAddress: 'AWS Internal'
- eventSource: 'ssm.amazonaws.com'
condition: selection and not filter_internal
falsepositives:
- Legitimate tooling that proxies instance-role calls through an external
egress (rare; allowlist the specific egress and the role)
- VPC endpoint or PrivateLink paths that surface non-internal source IPs
level: high
tags:
- attack.defense_evasion
- attack.t1078.004
- attack.t1552.005
The honest limit is the false-positive edge: NAT gateways, egress proxies, and some VPC-endpoint paths can surface a source that isn’t cleanly “AWS Internal.” That’s a real tuning problem — but it’s a bounded one. You’re allowlisting a handful of known egress paths per account, not chasing an open-ended baseline. And the stronger version of this detection binds tighter still: record the source an instance role is first seen from after it assumes its role, and alert on any subsequent use from a different source (a stateful session-binding approach that public tooling like SAM-DetectEC2CredExfil implements with a session store). That needs an enrichment layer rather than a stateless rule — the same build-the-state-first pattern I described for dormant-privilege detection in the identity-creep post — but it raises the signal further because it doesn’t depend on “outside AWS” at all. It depends on “not where this credential lives.”
What to Detect Beyond “Outside AWS”
The home-address rule catches exfiltration. The full workload-identity surface needs a few more, and the public rule corpus already has them — the work is deploying and tuning them, not inventing them. As with the detection funnel, the gap is between what’s been written and what’s actually running.
- OIDC token exchange from an unexpected subject or ASN.
AssumeRoleWithWebIdentitysucceeding for a federated subject that’s new, or from a non-cloud autonomous system, is the federation-abuse signal. Elastic ships both the external-ASN IRSA rule and a “Lateral Movement from Kubernetes SA via AssumeRoleWithWebIdentity” rule [6]; on the Azure side, “Service Principal Federated Credential Authentication by Unusual Client” catches the first-time federated sign-in. - AssumeRole chaining anomaly. A role assuming another role it has never assumed before, especially across accounts, is the lateral-movement primitive. Baseline the role-to-role graph and alert on new edges.
- IMDS access from anomalous process context. Elastic’s “Unusual Instance Metadata Service (IMDS) API Request” [6] watches for
curl,wget, or interpreter processes hitting the metadata endpoint — the on-host signal of SSRF or hands-on-keyboard credential theft, before the credential even leaves. - The workload doing what it never does. A role that has only ever called three S3 actions suddenly enumerating IAM or touching a new region is the same per-principal scope-expansion baseline I argued for against OAuth token theft — deviation from what this credential normally does.
The Real Gap Is the Control Plane
None of these detections fire if you’re not detecting on the control plane. And this is the actual gap: most organizations ingest CloudTrail, Azure Activity, and GCP audit logs — into a bucket, for compliance, for incident forensics — without building detections on the identity-assumption events inside them. The logs are collected. The alerts don’t exist.
That’s the workload-identity blind spot in one sentence: the lateral movement happens in a log you already have and don’t watch. AssumeRole, AssumeRoleWithWebIdentity, GetCallerIdentity from a stolen credential testing whether it works — these are not exotic signals. They are CloudTrail events sitting in storage, with public detection rules already written for them, waiting for someone to treat the control plane as a primary detection surface instead of an audit archive.
This is where the identity series lands. Across non-human identities, accumulated privilege, OAuth grants, and now workloads, the same pattern repeats: the attack moved to the identity layer, the telemetry exists, and the detections are written — but they’re not deployed, because the SOC is still watching endpoints and networks while the adversary operates in the IAM graph.
Closing
Workload identities are the rare case where the defender has the advantage and mostly hasn’t taken it. Every other machine identity forces you to learn a behavioral baseline from scratch. A workload identity tells you where it lives the first time it’s used — and from then on, every call from somewhere else is a credential that walked out the door.
Two questions tell you whether you’ve claimed that advantage. When an instance-role credential is used from outside its instance, does anything fire? And are you detecting on AssumeRole in CloudTrail at all, or just storing it?
The detections are public. The telemetry is already in your account. The only thing missing is the decision to watch the one identity that was kind enough to tell you where it belongs.
Resources
- 2025 State of Machine Identity Security Report — CyberArk (42% of machine identities privileged, 77% unmanaged, 50% of organizations breached via a compromised machine identity).
- CVE-2025-51591 — NVD (server-side request forgery in Pandoc, exploited against the AWS instance metadata service).
- No Keys Attached: Exploring GitHub-to-AWS Keyless Authentication Flaws — Datadog Security Labs (wildcarded OIDC trust policies letting untrusted GitHub Actions assume AWS roles).
- Malicious Usage Of IMDS Credentials Outside Of AWS Infrastructure — SigmaHQ (CloudTrail rule for instance-role credentials used outside AWS).
- GuardDuty IAM Finding Types — InstanceCredentialExfiltration.OutsideAWS — AWS (instance credentials observed from an IP address outside AWS).
- Elastic Detection Rules — Elastic (external-ASN IRSA, Kubernetes SA lateral movement, unusual IMDS request, and Azure federated-credential rules).