← All articles
Cloud Security 10 min read

IAM Role Chaining: How Attackers Use AssumeRole for Lateral Movement

A technical deep-dive into how attackers chain IAM role assumptions across accounts and services to escalate privileges — and the CloudTrail signatures and preventive controls that stop them.

CloudDefender Team ·
IAM Privilege Escalation PathsPATH A: Role ChainingDev Rolelimited permsCI Rolests:AssumeRoleProd Admincross-accountPATH B: PassRole EscalationAttackeriam:PassRoleLambda fnattacker controlsAdmin Roleattached to fnPATH C: Policy Overwriteiam:CreatePolicyVersion on existing policyOverwrites body: Effect Allow, Action *, Resource * — instant adminDetection: CloudTrail SignaturesRole chaining:AssumeRole where userIdentity.type =AssumedRole (role assuming a role)Cross-account assumptions:requestParameters.roleArn account IDdiffers from recipientAccountIdPassRole escalation:iam:PassRole + CreateFunction/RunInstancesin same session within short windowPolicy overwrite:CreatePolicyVersion with setAsDefault=trueon managed policy used by admin rolesAlert on ALL three patterns in real-timevia CloudTrail Lake or EventBridge rulesfeeding Lambda auto-remediation
Three IAM privilege escalation paths — each exploiting a legitimate AWS mechanism — and the CloudTrail events that expose them.

IAM privilege escalation is one of the most consequential techniques in cloud attacker playbooks. Unlike network-based lateral movement, which triggers firewall alerts and anomalous connection logs, IAM-based escalation uses fully legitimate AWS APIs — the same sts:AssumeRole and iam:CreatePolicyVersion calls that developers and automation pipelines make hundreds of times a day. Detection requires understanding what normal looks like and recognizing the specific patterns that indicate abuse.

What role chaining is

When an IAM principal calls sts:AssumeRole, AWS returns a set of temporary credentials scoped to the target role. Those temporary credentials can themselves call sts:AssumeRole on a third role — this is role chaining. There is no AWS-enforced limit on chain depth, and the chain can cross account boundaries if the target role’s trust policy permits.

An attacker who compromises a low-privilege role in a development account can walk this chain: dev role → shared services role → production admin role, provided each role’s trust policy allows the previous one to assume it. The final set of credentials carries the permissions of the last role in the chain, not the initial identity.

The CloudTrail signature for role chaining is distinct from a human or service directly assuming a role. When a role assumes another role, the userIdentity.type field in the CloudTrail event is AssumedRole rather than IAMUser or Root, and the userIdentity.sessionContext.sessionIssuer field traces back to the originating role. Monitoring for AssumeRole events where the caller is already an AssumedRole session — particularly where the source and target account IDs differ — surfaces this pattern.

PassRole privilege escalation

iam:PassRole is a permission that allows an IAM identity to attach an IAM role to an AWS service resource: a Lambda function, an EC2 instance, an ECS task definition, a Glue job, or dozens of other compute services. Its intended use is to grant a developer the ability to specify which execution role a function or instance will use.

The escalation path: an attacker with iam:PassRole and lambda:CreateFunction (or ec2:RunInstances, or ecs:RegisterTaskDefinition) can create a resource that runs with a role they do not themselves possess. They pass a highly privileged role — AdministratorAccess, for example — to the new resource, then invoke it with code that exfiltrates its own credentials or performs privileged actions on the attacker’s behalf.

This escalation requires no direct assumption of the target role. The attacker never calls sts:AssumeRole on the admin role — they simply get a compute resource to run with it. IAM Access Analyzer does not flag this by default because iam:PassRole is a standard permission.

Detect it in CloudTrail by alerting on sessions where iam:PassRole and a resource creation API (lambda:CreateFunction, ec2:RunInstances) occur within a short window, where the passed role ARN has broader permissions than the calling identity’s own policies.

The confused deputy problem

When a third party (such as a SaaS vendor or another AWS account) needs cross-account access to your AWS resources, the standard pattern is to create an IAM role with a trust policy allowing the third party’s account to assume it. The confused deputy vulnerability arises when that trust policy does not require an ExternalId condition.

Without ExternalId, any identity in the trusted account — not just the specific service or principal you intended — can assume your role. If the vendor’s account is ever compromised, or if the vendor’s own permissions are misconfigured, an attacker in that account can assume your role without any additional verification.

Require ExternalId on all cross-account role trust policies. Generate a unique, unguessable ID per customer relationship and include it as a condition:

"Condition": {
  "StringEquals": { "sts:ExternalId": "your-unique-external-id" }
}

IAM Access Analyzer surfaces roles that are accessible from outside your AWS Organization — review these findings regularly and confirm that every external trust relationship is intentional and includes ExternalId.

Policy modification escalation paths

Several IAM permissions allow an attacker to modify existing policies in ways that grant themselves or others elevated access:

iam:CreatePolicyVersion allows creating a new version of a managed policy and setting it as the default. An attacker with this permission on a policy that is attached to a privileged role can overwrite its statements with "Action": "*", "Resource": "*" — effectively granting themselves administrator access through the role.

iam:AttachRolePolicy allows attaching any managed policy to any role (subject to resource constraints in the attacker’s own policy). An attacker with unconstrained iam:AttachRolePolicy can attach arn:aws:iam::aws:policy/AdministratorAccess to a role they already possess.

iam:PutRolePolicy allows writing an inline policy directly to a role — functionally equivalent to AttachRolePolicy for escalation purposes.

All three permissions should be treated as equivalent to iam:* in terms of risk. Grant them only to identity governance automation, with strict resource constraints limiting which policies or roles they can modify, and with aws:RequestedRegion and aws:CalledVia conditions where applicable.

Detection and response

The CloudTrail events to alert on:

Route these events through EventBridge to a Lambda function that suspends the calling identity’s active sessions via iam:CreateServiceLinkedRole revocation and pages the security team.

Preventive controls

SCPs can restrict sts:AssumeRole to a known set of role ARNs using aws:PrincipalArn conditions, preventing ad-hoc role assumptions from principals outside the approved set. Permission boundaries constrain the maximum effective permissions of roles even if their attached policies are modified — an attacker who overwrites a policy cannot exceed the boundary. IAM Access Analyzer continuously evaluates resource-based policies and trust relationships for external access and surfaces findings that a human review would miss.

Takeaway

IAM privilege escalation paths are well-documented, reliably reproducible, and present in most AWS environments that have grown organically without a defined identity governance model. The techniques — role chaining, PassRole, policy overwrite — all use legitimate AWS APIs, which means prevention must come from policy constraints and detection must come from behavioral analysis of CloudTrail. Neither is optional if you are running privileged workloads in AWS.

CloudDefender

Defend your cloud. Continuously.

CloudDefender Suite gives security teams continuous posture management, threat detection, and compliance automation across AWS, Azure, and GCP — with zero false-positive fatigue.

Try CloudDefender →