AWS cross-account trust attacks: how role chaining reaches crown jewels
Key takeaways
- Cross-account trust is the connective tissue of multi-account AWS, and it is also how attackers move between accounts.
- A wildcard principal in a trust policy opens the role to every AWS account in the world. It is the misconfiguration that hurts most.
- Role chaining strings AssumeRole hops together. Every hop is a legitimate call, so the lateral movement blends into normal traffic.
- Third-party roles without an external ID are exposed to the confused deputy problem.
- You break the chain by closing one trust edge. Pathbreak finds which external principals can reach your crown jewels and shows you that edge.
Multi-account AWS is the recommended way to run a large estate, and cross-account IAM roles are what tie it together. A security account reads logs from every workload account. A shared services account deploys into others. A vendor assumes a role to run its product. All of that is cross-account trust working as designed.
It is also the map an attacker follows. Land in one account, look for a role you can assume in the next, and repeat. This post covers how cross-account trust is abused, how role chaining reaches the crown jewels, and how to break the path with a single change.
How cross-account trust works
Every IAM role has two policies. The permission policy says what the role can do. The trust policy, the assume-role policy, says who is allowed to become it. To move into a role, a principal calls sts:AssumeRole and gets temporary credentials, as long as the target role's trust policy allows that principal and the caller has sts:AssumeRole permission.
The trust policy controls who can get in, so cross-account security depends on how precisely you name the principals you allow.
Where trust goes wrong
Wildcard principals in trust policies
The most dangerous and most misunderstood trust policy is a wildcard principal. A statement that allows "Principal": { "AWS": "*" } with "Action": "sts:AssumeRole" does not mean any identity in your account. It opens the role to every AWS account in the world. There is a second requirement, the calling identity must also have sts:AssumeRole permission on the role, but that is why the wildcard is a practical risk: an attacker grants that permission to themselves in their own AWS account, so they supply the second requirement for free. Teams read the wildcard as "everyone on my team" and ship it. What they published is a role any attacker can assume from an account they control. On a role with meaningful permissions, that gives away everything the role can do. If you do need a broad principal, constrain it with a Condition such as aws:PrincipalAccount or aws:PrincipalOrgID, and better still, name the exact role ARNs you intend to trust.
The confused deputy on third-party roles
When you let a SaaS vendor assume a role in your account, that vendor uses one AWS account to serve many customers. If your trust policy only names the vendor's account, another of the vendor's customers could induce it into assuming your role. The fix is an external ID, a shared secret in the condition, or an aws:SourceArn or aws:SourceAccount condition that ties the assumption to your specific relationship. A third-party role without one is a classic confused deputy exposure.
Role chaining across accounts
One hop rarely reaches the goal, so real paths chain. The attacker assumes a role in the dev account, which happens to be trusted by a role in the shared services account, which can assume a role in production, which can read the database credentials in Secrets Manager. Four legitimate sts:AssumeRole calls, and a foothold in the least important account has become access to the most important data.
What makes chaining dangerous is that each hop is authorized, nothing is exploited, and the credentials are temporary and expected. To CloudTrail it looks like roles doing their jobs. The only way to see the danger is to look at the whole chain at once and ask where it ends, rather than judging each trust relationship on its own.
AWS does add friction. Chained role sessions are capped at one hour and cannot be renewed by chaining further. That limits session lifetime, but it does not close the path.
Why account boundaries feel safer than they are
Teams treat the account boundary as a security boundary, and it is a good one for blast radius. The trouble is that cross-account roles are deliberately drilled through it, and each one is reviewed in isolation by whoever owns that account, so no one reviews the chain as a whole. The dev team's convenient trust relationship and the platform team's shared role are each reasonable, and together they form a route neither team can see from where they sit. Isolation review misses cross-account paths for the same reason it misses IAM escalation. The risk is in how the roles connect, and reviewing one role at a time never shows that.
Tools that map it
Graph-based tools help. PMapper and Cartography can model roles and trust relationships across accounts, and AWS IAM Access Analyzer reports external access to your roles, including cross-account trust you may have forgotten. These give you candidate edges. The remaining question is which of those candidate chains resolve, all hops, all conditions, all permission policies in force, to something you care about. A trust edge that leads nowhere sensitive is noise, and a four-hop chain to your production data is the one you need to fix.
How to break the chain
As with any path, cutting one edge is enough. Replace root-account principals in trust policies with exact role ARNs. Require an external ID or aws:SourceArn condition on every third-party role. Delete cross-account trust that no longer serves a purpose, dormant vendor roles are a common find. Where you can, use service control policies to stop entire classes of account from assuming sensitive roles. Then alert on AssumeRole calls into critical roles from unexpected sources.
The judgment call is which edge to cut. The right edge severs the attacker's route without touching anything legitimate, and the wrong one breaks a pipeline. That requires knowing where each chain leads.
How Pathbreak approaches it
Pathbreak builds the full trust graph across your AWS accounts and treats it as one connected space, the way an attacker does. It resolves each AssumeRole hop against the real trust policies, permission policies, and conditions, then confirms which chains reach crown jewels such as production data, backups, or organization management. It works read-only, so mapping your trust never touches it.
For every chain that resolves, Pathbreak shows the hops and points to the single trust edge that breaks it, usually one over-broad trust policy or one forgotten vendor role. You can see the chains we cover on the attack paths page, the mechanics on how it works, and how we handle least-privilege access on the Trust and Security page. For how the same reasoning applies inside one account, see our post on AWS IAM privilege escalation.
Frequently asked questions
What is a cross-account trust attack in AWS?
It is when an attacker uses a role's trust policy to move between AWS accounts. If a role in a sensitive account trusts a principal the attacker controls, they can call sts:AssumeRole and operate in that account, then repeat the move to reach further. Pathbreak maps cross-account trust across your AWS accounts and validates which of these paths work.
What is role chaining?
Role chaining is assuming one role, then using it to assume another, and another, until you reach a role with access to what matters. Each hop is a legitimate sts:AssumeRole call, so the chain looks like normal activity while it crosses trust boundaries. Pathbreak follows role chains across accounts and shows the trust edge that breaks each one.
How do you prevent cross-account trust abuse?
Name exact principals in trust policies instead of whole accounts or wildcards, require an external ID or aws:SourceArn condition for third-party roles to stop confused-deputy abuse, remove unused cross-account trust, and continuously validate which external principals can reach sensitive roles. Pathbreak names the specific trust edge to change for each validated cross-account path.
How does Pathbreak detect role chaining paths?
Pathbreak builds the full trust graph across your accounts, resolves each AssumeRole hop against the real trust and permission policies, and confirms which chains reach crown jewels. It is read-only and shows the single trust edge that severs the path.
Want to see which cross-account chains reach your crown jewels?
Request a demo