Daily Cloud Blog • Cloud Exploit Series
Abusing AWS IAM Trust Policies for Privilege Escalation
Cloud Exploit Series • Daily Cloud Blog
Difficulty: Intermediate • Cloud: AWS • Focus: IAM / STS
What you’ll learn: How a single misconfigured role trust policy can let a low-priv identity assume a stronger role —
and exactly how to detect and prevent it.
Ethical Use Notice: This is for authorized testing, defensive validation, and education only.
Do not use against systems you do not own or explicitly have permission to assess.
In this post
- Executive overview
- Target architecture
- Attack assumptions
- Exploit walkthrough (lab validation)
- Detection strategy
- Mitigation strategy
- Terraform secure baseline
- Key takeaways
Executive overview
In AWS, permissions are only half the story. A role can have a perfect permission policy, but if its trust policy
(who is allowed to assume it) is too broad, you’ve created a privilege-escalation path.
- Impact: Low-priv identity can become a high-priv role (data access, lateral movement, persistence).
- Root cause: Overly permissive
sts:AssumeRoletrust relationships (often “temporary” shortcuts that become permanent). - Fix: Tight trust principals + conditions (OrgID, ExternalId, tags), plus guardrails (SCP) and detection (CloudTrail).
Target architecture
Typical enterprise pattern: teams deploy workloads with “service roles” and create “break-glass” or “admin automation” roles.
Someone adds a broad trust statement to “make integrations work”… and it becomes an escalation bridge.
[Low-Priv User/Role] --(sts:AssumeRole)--> [Target Role (Powerful)]
| |
| [S3, KMS, Secrets, EC2, IAM...]
|
CloudTrail logs everything (if you’re watching)
The key point: Trust policy decides who can assume a role. Permission policy decides what that role can do once assumed.
Attack assumptions
- Attacker has some AWS access (stolen keys, compromised workload, or an overly permissive internal account).
- A target role exists with higher privilege than the attacker currently has.
- The target role trust policy allows assumption by an overly broad principal (account-wide, wildcard, or weak conditions).
Exploit walkthrough (lab validation)
Step 1 — Identify role assumption paths
In defensive assessments, you’re looking for roles where the trust relationship is broader than intended.
Common smells:
- Trust principal set to an entire account root:
"AWS": "arn:aws:iam::111122223333:root" - Trusting multiple accounts without guard conditions
- Trusting a role naming pattern without restricting tags / paths
- Allowing assumption by identities that shouldn’t be able to “jump” privilege tiers
Misconfiguration example (too broad trust)
This trust policy allows any principal in Account 111122223333 to assume the role.
That’s often not what you want — especially for high-priv roles.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:root" },
"Action": "sts:AssumeRole"
}
]
}
Step 2 — Validate in an authorized lab
If your current identity has permission to call sts:AssumeRole (either explicitly or through broad IAM),
and the trust policy permits it, you can obtain temporary credentials for the target role.
In an assessment, this validates the escalation path.
# Authorized lab validation
aws sts get-caller-identity
# Attempt to assume the target role (example)
aws sts assume-role \
--role-arn arn:aws:iam::444455556666:role/TargetPowerRole \
--role-session-name dcb-cloud-exploit-series-01
# Use returned temporary credentials (set env vars) and re-check identity
aws sts get-caller-identity
Step 3 — Observe the privilege delta
The risk becomes real when the assumed role can do something your original identity could not.
Examples include:
- Reading secrets (
secretsmanager:GetSecretValue) - Decrypting data keys (
kms:Decrypt) - Enumerating / modifying IAM (
iam:*actions) - Modifying logging or guardrails (e.g., CloudTrail, Config)
Architect’s perspective: This misconfig usually happens when teams want cross-account automation quickly.
The trust relationship gets broadened, then never tightened.
Detection strategy
The good news: role assumption is noisy (in a good way). You can detect it reliably if you’re collecting CloudTrail properly.
CloudTrail events to watch
AssumeRole(STS) — primary signalAssumeRoleWithWebIdentity— common in EKS/IRSA and OIDC patternsAssumeRoleWithSAML— federated enterprise access
High-value detections
- AssumeRole into privileged roles outside normal admin workflow
- AssumeRole from unusual source IPs / user agents
- AssumeRole followed by IAM changes, CloudTrail changes, or KMS decrypt spikes
- Cross-account AssumeRole not tied to known automation principals
# Detection pseudo-logic (SIEM / analytics)
# IF eventSource=sts.amazonaws.com AND eventName=AssumeRole
# THEN enrich with:
# - roleArn
# - sourceIPAddress
# - userIdentity.arn (who assumed)
# - sessionContext attributes
# ALERT when:
# - roleArn in "PrivilegedRoles" list AND caller not in "ApprovedPrincipals"
# - cross-account assume not in allowlist
Mitigation strategy
1) Restrict the trusted principal
Don’t trust the whole account root unless you truly mean it.
Prefer trusting specific roles used for automation, with clear naming, paths, and ownership.
2) Add conditions that enforce intent
- ExternalId for third-party or cross-account integrations
- aws:PrincipalOrgID to restrict to your AWS Organization
- aws:PrincipalArn allowlist (with care)
- sts:RoleSessionName patterns for automation hygiene
- Tags (ABAC) to require specific principal tags
3) Guardrail it with SCPs
In AWS Organizations, add SCPs that prevent creation of overly broad trust policies for roles in sensitive OUs/accounts.
This prevents the problem from reappearing.
4) Least privilege on who can call STS
Treat sts:AssumeRole as a privileged action.
Most identities should be able to assume only a minimal set of roles, not “any role anywhere.”
Terraform secure baseline
Below is a hardened trust policy example. It demonstrates three protections:
(1) trust a specific role principal, (2) restrict to your Org, (3) enforce a session name pattern.
Adjust to your environment.
data "aws_iam_policy_document" "assume_role_trust" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::111122223333:role/AutomationAssumerRole"]
}
condition {
test = "StringEquals"
variable = "aws:PrincipalOrgID"
values = ["o-xxxxxxxxxx"]
}
condition {
test = "StringLike"
variable = "sts:RoleSessionName"
values = ["dcb-*"]
}
}
}
resource "aws_iam_role" "target_power_role" {
name = "TargetPowerRole"
assume_role_policy = data.aws_iam_policy_document.assume_role_trust.json
}
Operational tip: Put privileged roles behind a break-glass workflow (ticket + approval + MFA),
and keep a short list of approved principals that can assume them.
Key takeaways
- Trust policy defines who can become a role; permission policy defines what that role can do.
- Account-root trust is a common “shortcut” that becomes a long-term escalation path.
- Detect with CloudTrail (
AssumeRole) + alert on privileged roles + unusual principals. - Prevent with restricted principals, strong conditions (OrgID/ExternalId/tags), and SCP guardrails.
Next in the Cloud Exploit Series:
EC2 Metadata Service Abuse (IMDSv1 vs IMDSv2)
Want More Offensive Cloud Research?
Subscribe to Daily Cloud Blog for deep-dive cloud attack path analysis,
secure Terraform patterns, and enterprise-ready defensive strategies.


Leave a comment