AWS cross-account
TL;DR: Trust policies that name external account IDs or
*principals, plus RAM-shared resources, let a foothold in account A pivot into account B throughsts:AssumeRoleor shared subnets/keys.
What it is
Cross-account access in AWS lives in a role’s trust policy (AssumeRolePolicyDocument). When that policy lists an external account, an external role, or "Principal": {"AWS": "*"} without a hardening condition, identities outside the owning account can call sts:AssumeRole and obtain temporary credentials. AWS RAM (Resource Access Manager) adds a second pivot path: shared subnets, transit gateways, KMS keys, and license-manager configs let an attacker in the consumer account influence producer-account assets.
Preconditions / where it applies
- You can enumerate target roles (e.g. via aws-iam-enum or
iam:GetRolefrom a partner account). - The trust policy lists your account, lists
*, or lists a third-party SaaS pattern you can spoof. - No
sts:ExternalIdcondition, or the ExternalId is leaked/guessable. - Or: RAM share grants you network adjacency / KMS-decrypt against another account’s resources.
Technique
Enumerate roles and grep their trust documents for cross-account principals:
1
2
aws iam list-roles --query 'Roles[].[RoleName,AssumeRolePolicyDocument]' --output json \
| jq '.[] | select(.[1].Statement[].Principal.AWS | tostring | test("arn:aws:iam::(?!OWNER_ID)"))'
From a controlled account, attempt the cross-account assume:
1
2
3
4
aws sts assume-role \
--role-arn arn:aws:iam::TARGET_ID:role/PartnerIntegration \
--role-session-name pivot \
--external-id "$LEAKED_EXTERNAL_ID"
Common patterns that work:
- Wildcard principal —
"Principal": {"AWS": "*"}with no condition. Any account assumes it. - Confused-deputy SaaS roles — vendor template includes ExternalId but customers paste the example value verbatim.
- Org-wide trust —
aws:PrincipalOrgIDset, but the attacker already has a foothold in the same org. - RAM-shared KMS keys — consumer account uses the shared key to decrypt producer-owned ciphertext (S3 objects, Secrets Manager).
- RAM-shared subnets — workloads in shared VPC subnets can reach producer-account services and metadata.
Once assumed, chain into aws-sts-assume-role role hops or look for aws-iam-eventual-consistency-persistence.
Detection and defence
- CloudTrail
AssumeRoleevents withsourceIPAddressoutside expected ranges oruserIdentity.accountIdnot on an allow-list. - Set
aws:SourceAccountandaws:SourceArnon SaaS integration roles to block confused-deputy. - Always require a high-entropy
sts:ExternalIdfor third-party trust; never the example value. - Use IAM Access Analyzer external-access findings to inventory cross-account exposure (including RAM shares).
- SCPs to deny role creation with wildcard principals (
aws:PrincipalAccountdeny when outside org).
References
- AWS — Cross-account access using IAM roles — official mechanics
- AWS — Confused deputy problem — ExternalId rationale
- HackTricks Cloud — AWS IAM privesc — assume-role chains