> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/aws/aws_assume_role.md).

# AWS\_ASSUME\_ROLE

## Summary

|                                |                                     |
| ------------------------------ | ----------------------------------- |
| **Forestall ACL Alias**        | AWS\_ASSUME\_ROLE                   |
| **Edge Type**                  | Attack Path                         |
| **Affected Object Types**      | IAM Users, IAM Roles, AWS Services  |
| **Exploitation Certainty**     | Certain                             |
| **AWS IAM Action / Condition** | `sts:AssumeRole` on the target role |

## Description

`AWS_ASSUME_ROLE` records that a principal can call `sts:AssumeRole` against a target IAM role and receive temporary credentials scoped to that role's permissions.

The caller gets back `AccessKeyId`, `SecretAccessKey`, and `SessionToken`, valid for up to 12 hours. Those credentials carry the full permission set of the assumed role, which may far exceed what the caller normally has.

Two things must both be true for this path to work:

1. The **source identity** has `sts:AssumeRole` allowed in its own policies.
2. The **target role's trust policy** lists the source identity as a permitted principal.

Cross-account assumption is standard practice in multi-account AWS architectures. The target role in Account B lists an Account A principal in its trust policy. An attacker who controls that Account A identity can pivot to Account B without any credentials native to it.

Common condition keys for restricting this path are `aws:MultiFactorAuthPresent`, `sts:ExternalId`, `aws:SourceIp`, and `aws:PrincipalOrgID`. Missing or misconfigured conditions are where most over-permissive chains come from.

## Identification

### AWS CLI

Pull the trust policy to see who can assume the role:

```bash
aws iam get-role --role-name TargetRole \
  --query 'Role.AssumeRolePolicyDocument'
```

Simulate whether a specific identity can assume the role:

```bash
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/AnalystUser \
  --action-names sts:AssumeRole \
  --resource-arns arn:aws:iam::123456789012:role/TargetRole
```

Find all roles whose trust policies list a specific principal:

```bash
aws iam list-roles --query 'Roles[*].[RoleName,AssumeRolePolicyDocument]' \
  --output json | python3 -c "
import json, sys
roles = json.load(sys.stdin)
for name, policy in roles:
    doc = policy if isinstance(policy, dict) else json.loads(policy)
    for stmt in doc.get('Statement', []):
        p = stmt.get('Principal', {})
        if 'arn:aws:iam::123456789012:user/AnalystUser' in str(p):
            print(name)
"
```

### AWS Console

1. Open **IAM** → **Roles** → select the target role.
2. Open the **Trust relationships** tab.
3. Review the trust policy JSON. `Principal` defines who can call `AssumeRole`, and `Condition` blocks restrict when it is allowed.
4. To check if a source identity has the permission to call `sts:AssumeRole`, open that user or role and use **Policy Simulator** under **Actions**.

## Exploitation

Assume the target role:

```bash
aws sts assume-role \
  --role-arn arn:aws:iam::123456789012:role/TargetRole \
  --role-session-name AttackerSession
```

Export the returned credentials:

```bash
export AWS_ACCESS_KEY_ID=<AccessKeyId>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey>
export AWS_SESSION_TOKEN=<SessionToken>
```

Verify the assumed identity:

```bash
aws sts get-caller-identity
```

Cross-account trust policies often require an `ExternalId`. If you have or can guess the value, pass it:

```bash
aws sts assume-role \
  --role-arn arn:aws:iam::999999999999:role/CrossAccountRole \
  --role-session-name AttackerSession \
  --external-id KnownExternalId
```

## Mitigation

* Trust policies should name specific principals. Remove anything that isn't actively used.
* Use `aws:PrincipalOrgID` to keep cross-account assumptions inside your organization.
* Add `aws:MultiFactorAuthPresent: 'true'` as a condition on sensitive roles.
* Treat `sts:ExternalId` as a shared secret for third-party access. Don't expose it in docs or error messages.
* Never use `"Principal": "*"` in a trust policy. Any authenticated AWS principal can attempt the assumption.
* Set `MaxSessionDuration` to the shortest viable session length.

## Detection

Watch CloudTrail for `AssumeRole` activity:

* **Event source**: `sts.amazonaws.com`
* **Event name**: `AssumeRole`

```bash
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole \
  --query 'Events[*].{Time:EventTime,Actor:Username,Role:Resources[0].ResourceName}'
```

Signals worth investigating:

* Cross-account assumptions from an account you don't recognize.
* Session names that break your naming convention. Automated tools produce consistent patterns; attackers often don't.
* Repeated `AccessDenied` on `AssumeRole` calls, a sign of trust policy enumeration.
* Admin, deploy, or break-glass role assumptions outside business hours.

## References

* <https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html>
* <https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_permissions-to-switch.html>
* <https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_require-mfa.html>
* <https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html>
* <https://cloud.hacktricks.wiki/en/pentesting-cloud/aws-security/aws-privilege-escalation/aws-sts-privesc/index.html>
* <https://ermetic.com/blog/aws/aws-iam-privilege-escalation-methods-mitigation/>
