4 patterns
of cross-account IAM trust policy misconfiguration: overly broad account trust, wildcard principal, missing sts:ExternalId condition for third-party roles, and external account trust not scoped to an AWS Organization
62%
of cloud security incidents involve an IAM misconfiguration that allowed lateral movement or privilege escalation -- cross-account role trust is among the most frequently exploited categories
3 commands
required to enumerate all cross-account trust relationships in an AWS organization: aws iam list-roles, aws iam get-role, and aws organizations list-accounts
sts:ExternalId
is the condition key that prevents confused deputy attacks against third-party IAM roles -- absent in the majority of cross-account trust policies created before 2022

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

Every AWS account accumulates cross-account trust relationships over time. A third-party security scanner was granted assume-role access to audit the environment. A shared services account was given access to deploy to production. A vendor was onboarded with access to the logging account. A data pipeline role was created to read from an S3 bucket in another account.

Each of these trust relationships was legitimate when it was created. Many of them are still legitimate. Some of them have been forgotten. A few of them were never as scoped as they should have been, and a small number are genuinely misconfigured in ways that create lateral movement paths across account boundaries.

The difference between "overly broad" and "misconfigured" matters: an overly broad trust policy grants assumption access to more principals than intended (an entire account rather than a specific role). A misconfigured trust policy has a structural problem that makes it exploitable in ways the original creator did not intend -- a missing condition that allows the confused deputy attack, a wildcard principal, or a trust to an external account that is no longer used but still present.

This guide covers how to enumerate every cross-account trust relationship in your AWS environment, which patterns to flag immediately, and how to remediate without breaking legitimate access.

Enumerating All Cross-Account Trust Relationships

The enumeration produces a complete list of IAM roles with trust policies that allow principals in other AWS accounts to assume them. Run this across every account in your AWS Organization.

Step 1: List all IAM roles and their trust policies. The AWS CLI command aws iam list-roles --query 'Roles[*].{RoleName: RoleName, Arn: Arn, TrustPolicy: AssumeRolePolicyDocument}' returns all roles in the account. Pipe the output through a filter that extracts any trust policy principal containing an AWS account ID other than the current account: aws iam list-roles | jq '.Roles[] | select(.AssumeRolePolicyDocument.Statement[].Principal.AWS? | strings | test("[0-9]{12}"))'. This filters to roles with external account principals.

Step 2: Cross-reference with the organization account list. Get all account IDs in your AWS Organization: aws organizations list-accounts --query 'Accounts[*].Id'. Compare the account IDs in your trust policies against this list. Any account ID in a trust policy that is NOT in your organization's account list is an external account -- a vendor, a managed service provider, an old account that has been closed, or an account that was never formally part of your environment.

Step 3: Use IAM Access Analyzer for automated external access detection. Enable IAM Access Analyzer in every account (it is free for external access findings). Access Analyzer automatically identifies IAM roles with trust policies that grant access to principals outside the AWS Organization. The findings appear as "external access" in the console and are queryable via aws accessanalyzer list-findings --analyzer-arn <analyzer-arn> --filter '{"resourceType": {"eq": ["AWS::IAM::Role"]}}'. This catches external account trust, wildcard principals, and trust to AWS services from outside your organization.

Step 4: Map assumption chains. For each cross-account trust relationship identified, determine the chain: which principal in Account A can assume Role B in Account C, and what permissions does Role B carry? Assumption chains longer than two hops (Account A assumes Role in Account B which then assumes Role in Account C) require explicit documentation and are often misconfigured -- the intermediate role may be more permissive than intended.

The Four Misconfiguration Patterns to Flag Immediately

Pattern 1: Full account trust without principal scoping. A trust policy that grants assumption access to arn:aws:iam::123456789012:root allows any principal in account 123456789012 to assume the role -- every IAM user, every IAM role, every service. The intended pattern was likely to grant access to a specific IAM role or user in that account. Flag any trust policy principal that ends in :root and is not an AWS service principal.

Pattern 2: Wildcard or overly broad condition. A trust policy with "AWS": "*" as the principal grants assumption to any principal in the universe with a matching condition. Even with conditions like StringEquals: {"aws:PrincipalAccount": "123456789012"}, the wildcard principal plus a loose condition is a pattern that is easy to misconfigure. Flag all "AWS": "*" principals.

Pattern 3: Missing sts:ExternalId for third-party trust. Third-party IAM roles (for vendors, security scanners, cloud management platforms) should include a sts:ExternalId condition that the third party provides at assumption time. This prevents the confused deputy attack: if Vendor A's account is compromised, without the ExternalId condition, the compromised account can assume your role. The ExternalId ensures the requester knows a shared secret, limiting the attack surface. Flag any trust policy that names an external AWS account (not in your organization) and does not include a sts:ExternalId condition.

Pattern 4: Trust to accounts outside the organization without justification. Any trust to an AWS account ID that is not in your AWS Organization account list requires a documented justification. If no justification exists and nobody on the team can explain why the trust exists, treat it as potentially compromised and revoke it. Accounts change ownership, get acquired, or are repurposed -- an account that was your vendor's in 2021 may not be the same entity in 2026.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

How to Scope a Trust Policy Correctly

The correct scope for a cross-account trust policy is the minimum specific principal that needs assumption access. In practice, this usually means a specific IAM role ARN rather than an account root.

Replace :root trust with specific role ARN trust. If the intent was to allow a specific deployment role in Account A to assume a role in Account B, the trust policy should name arn:aws:iam::ACCOUNT_A:role/DeploymentRole rather than arn:aws:iam::ACCOUNT_A:root. The more specific principal requires that the caller is operating as that exact role, not any principal in the account.

Add aws:PrincipalOrgID conditions for internal trust. For trust policies that should only be assumable from within your AWS Organization, add a condition: StringEquals: {"aws:PrincipalOrgID": "o-ORGANIZATION_ID"}. This ensures that even if an account ID is compromised or transferred, only principals that are currently members of your organization can assume the role.

Add sts:ExternalId for all third-party trust policies. Coordinate with the third-party vendor to establish an ExternalId value. The vendor provides this value when calling AssumeRole; your trust policy checks for it. Use a long random value (UUID or equivalent) that is unique per vendor. Store it in your secrets management system. This value should be rotated if the vendor relationship ends or if the vendor reports a compromise.

Use aws:SourceAccount for service-linked trust. For trust policies granting access to AWS services (like config, cloudtrail, or inspector), use the aws:SourceAccount or aws:SourceArn condition to prevent the confused deputy attack from the service side. This ensures the service is acting on behalf of your account rather than potentially being redirected to act for another account's resources.

Auditing Assumption Chains: Multi-Hop Lateral Movement

A single-hop trust (Account A user assumes Role in Account B) is straightforward to audit. Multi-hop chains (Account A role assumes Account B role which assumes Account C role with admin permissions) are harder to detect and represent a more significant lateral movement risk.

The multi-hop risk: a low-permission principal in Account A can reach high permissions in Account C through an intermediate role in Account B that was provisioned with broader permissions than intended. The permissions at each hop compound in the direction of greatest access.

Enumerate multi-hop chains: for each cross-account role identified in the enumeration step, check whether that role itself has a trust policy allowing assumption from another external account (creating a two-hop chain) and what permissions the role carries (are they high enough that two-hop access is a meaningful risk escalation?).

Tools that automate multi-hop analysis: PMapper (Principal Mapper, open source) analyzes IAM policies across an AWS account and produces a graph of privilege escalation paths, including cross-account assumption chains. Run PMapper in a read-only mode against each account in the organization and review the resulting privilege escalation paths. Focus on paths that lead to AdministratorAccess or policies with *:* effect Allow.

Flag any multi-hop chain where:

  • The first hop is from outside your organization (external account, public internet)
  • Any hop in the chain involves a role with admin-equivalent permissions
  • The chain traverses more than two accounts before reaching the destination role

Remediating Without Breaking Legitimate Access

Trust policy changes break legitimate access immediately -- unlike IAM permission changes, which can often be staged. The remediation sequence minimizes disruption.

Phase 1: Classify each finding. For each flagged trust policy pattern, categorize it as: (a) misconfigured and unused (no CloudTrail AssumeRole events in the last 90 days -- safe to remediate immediately), (b) misconfigured and actively used (CloudTrail shows recent successful assumption -- requires coordination before remediation), or (c) misconfigured with an unknown usage pattern (no CloudTrail data older than 90 days is available -- investigate before remediating).

Check assumption activity: aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole --start-time $(date -d '90 days ago' -Iseconds) | jq '.Events[] | select(.Resources[].ResourceName | contains("ROLE_NAME"))'. This shows every AssumeRole API call for the role in the last 90 days, with the caller's identity.

Phase 2: Remediate unused cross-account trusts first. For trust relationships with zero AssumeRole events in 90 days: remove the external account from the trust policy or delete the role if it is fully unused. This carries zero operational risk.

Phase 3: Notify before remediating active trusts. For trust relationships with recent usage: identify the team or vendor that uses the trust, notify them of the pending change with a 14-day notice period, schedule the remediation change for a low-traffic period, implement the more restrictive trust policy (specific principal ARN instead of account root, ExternalId condition added), and confirm with the consuming team that their access continues to work.

Phase 4: Implement organizational SCPs as a guardrail. Add an SCP to your AWS Organization that denies the creation of trust policies with "Principal": {"AWS": "*"} or with account root principals without a PrincipalOrgID condition. This prevents new misconfigured trust policies from being created without going through an exception process.

Ongoing Monitoring for New Cross-Account Trust Creation

Trust policy remediation without ongoing monitoring produces a clean state that degrades over time as new cross-account trusts are created through the normal AWS provisioning process.

CloudTrail event monitoring. The CloudTrail event CreateRole and UpdateAssumeRolePolicy capture all trust policy creation and modification events. Create a CloudWatch Metric Filter and alarm (or a SIEM detection rule) that fires when either event occurs and the request parameters contain an account ID that is not in your organization's known account list. This provides near-real-time detection of new external account trust creation.

IAM Access Analyzer continuous monitoring. Access Analyzer runs continuously and generates new findings for any newly created external access. Configure an EventBridge rule to fire when a new Access Analyzer finding is generated with resource type AWS::IAM::Role and access type External. Route the event to your security ticket queue for triage.

Quarterly trust policy re-audit. Run the full enumeration process quarterly, not only as a one-time exercise. New roles accumulate, vendors change, and organizational account structure changes over time. The quarterly audit catches trust policies that were created through infrastructure-as-code provisioning that bypassed the real-time detection, and confirms that remediated trust policies have not been reverted.

The bottom line

Cross-account IAM trust policies are one of the most reliable lateral movement paths in AWS environments because they are legitimate AWS infrastructure features that are easy to misconfigure and difficult to notice without targeted auditing. The four misconfiguration patterns -- full account trust instead of specific role ARN trust, wildcard principals, missing ExternalId conditions for third-party trust, and external account trust without organizational scoping -- are individually remediable and collectively represent the majority of cross-account IAM risk in most AWS Organizations. Enumerate all trust relationships, classify each finding by active usage, remediate unused trusts immediately and active trusts with coordination, and implement CloudTrail monitoring and Access Analyzer detection to catch new misconfigurations before they are exploited.

Frequently asked questions

What is a cross-account IAM trust policy in AWS?

An IAM trust policy is the resource-based policy attached to an IAM role that defines which principals can assume that role via the sts:AssumeRole API. A cross-account trust policy grants assumption access to principals in AWS accounts other than the account where the role resides -- for example, allowing a role in Account B to be assumed by a user or role in Account A. This is legitimate infrastructure for shared services, deployments, and vendor integrations, but misconfigured cross-account trust policies create lateral movement paths that allow unintended principals to gain access to resources in the target account.

How do I find all cross-account IAM roles in my AWS environment?

Use three methods in combination: the AWS CLI command `aws iam list-roles` piped through a filter for trust policy principals containing external account IDs, IAM Access Analyzer (enable it in every account -- it automatically identifies roles with trust policies granting access to principals outside your AWS Organization), and AWS Organizations account list comparison (cross-reference trust policy account IDs against your organization's known account list to identify external account trust). Run this enumeration across every account in the organization, not just the primary account.

What is the confused deputy attack in AWS IAM?

The confused deputy attack exploits the trust relationship between your AWS account and a third-party service's AWS account. Without an sts:ExternalId condition, a compromised third-party account can call sts:AssumeRole against your account's roles by impersonating the service you trust. The ExternalId condition requires the caller to provide a shared secret at assumption time, which the third party cannot know if they are not the legitimate service. Add an ExternalId condition with a unique UUID to every trust policy that names an external AWS account, and rotate the ExternalId if the vendor relationship ends or the vendor reports a compromise.

Why is granting trust to `:root` in an AWS IAM trust policy a security risk?

A trust policy principal of `arn:aws:iam::123456789012:root` grants assumption access to any principal in account 123456789012 -- every IAM user, every IAM role, every service. The intent is usually to grant access to a specific role or user in that account, but the `:root` principal scoping opens the trust to the entire account. Replace `:root` principals with specific IAM role ARNs (e.g., `arn:aws:iam::123456789012:role/DeploymentRole`) to limit assumption access to only the intended principal.

How do I audit for multi-hop cross-account IAM assumption chains?

Multi-hop chains occur when Role B (which can be assumed from Account A) itself has a trust policy allowing assumption from Account C, creating a path where Account A -> Role B -> Role C chains privilege escalation. Use PMapper (Principal Mapper, open source) to enumerate IAM privilege escalation paths across an account, including cross-account assumption chains. Run PMapper in read-only mode against each account and review paths that lead to AdministratorAccess or wildcard permissions. Flag chains longer than two hops, chains with an external account (outside your organization) at the first hop, and chains that pass through any role with admin-equivalent permissions.

How do I remediate a cross-account trust policy without breaking access for the team using it?

Use a phased approach: first, check CloudTrail AssumeRole events for the last 90 days to determine whether the trust relationship is actively used. For unused trusts (zero events in 90 days): remove or replace the trust policy immediately with no operational risk. For active trusts: identify the team or vendor using the trust, notify them with a 14-day notice period before making the change, implement the more restrictive trust policy (specific ARN instead of account root, ExternalId condition added), and confirm with the consuming team that access still works. Never remediate an active trust policy without prior notification to the consumer.

What AWS SCP can prevent misconfigured cross-account trust policies from being created?

An SCP that denies the iam:CreateRole and iam:UpdateAssumeRolePolicy actions when the request includes a trust policy with `"Principal": {"AWS": "*"}` or an account root principal without a PrincipalOrgID condition prevents the most common misconfiguration patterns at creation time. Apply the SCP at the organization root or at the OU level for accounts that should never create external account trust without security team approval. Combine the SCP with a CloudTrail alert for CreateRole and UpdateAssumeRolePolicy events that include external account IDs, providing real-time detection for trust policies that do not trigger the SCP deny.

Sources & references

  1. AWS: IAM Trust Policy Evaluation Logic
  2. AWS: IAM Access Analyzer External Access Findings
  3. CISA: AWS Security Best Practices
  4. Rhino Security Labs: AWS Cross-Account Privilege Escalation
  5. AWS: Service Control Policies for Trust Policy Guardrails

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.