aws-account-root-credential-reset
match (effective permission)
{
"any_action": [
"account:PutContactInformation",
"account:PutAlternateContact",
"account:StartPrimaryEmailUpdate"
],
"resource_type": "AWS::Account::Account"
}
where
?principal has EFFECTIVE account:PutContactInformation OR account:PutAlternateContact (Security) OR account:StartPrimaryEmailUpdate on the
Account
no SCP denies the account:Put*/StartPrimaryEmailUpdate action at the Account scope (else BLOCKED)
emit
| source type | Identity |
|---|---|
| target type | HumanIdentity |
| source | <principal> |
| target | <root HumanIdentity> |
| permissions | account:PutContactInformation account:PutAlternateContact account:StartPrimaryEmailUpdate |
| conditions | scp_or_org_policy condition_expression |
| state logic | CONDITIONAL(reset-flow) by default: writing contact/primary-email REDIRECTS the root recovery channel, but completing the reset is an out-of-band step (console 'Forgot password' email, and for StartPrimaryEmailUpdate an OTP delivered to the NEW attacker-controlled address consumed via account:AcceptPrimaryEmailUpdate). Never ACTIVE on the write permission alone. BLOCKED(scp_or_org_policy) if an SCP denies the account:Put*/email actions. |
Narrative
{principal.name} can change the recovery contact/primary email of the account (account:PutContactInformation / PutAlternateContact / StartPrimaryEmailUpdate) and complete the root-user password reset, taking over the {target.name} root credentials.
Raw rule rules/explicit/aws-account.yaml
id: aws-account-root-credential-reset
emits: CanResetCredential
applies_to:
- aws
match_effective_permission:
any_action:
- account:PutContactInformation
- account:PutAlternateContact
- account:StartPrimaryEmailUpdate
resource_type: AWS::Account::Account
where:
- ?principal has EFFECTIVE account:PutContactInformation OR account:PutAlternateContact (Security) OR
account:StartPrimaryEmailUpdate on the Account
- no SCP denies the account:Put*/StartPrimaryEmailUpdate action at the Account scope (else BLOCKED)
emit:
source_type: Identity
target_type: HumanIdentity
source: <principal>
target: <root HumanIdentity>
api_source: iam:SimulatePrincipalPolicy | policy evaluation
permissions:
- account:PutContactInformation
- account:PutAlternateContact
- account:StartPrimaryEmailUpdate
conditions:
- scp_or_org_policy
- condition_expression
state_logic: 'CONDITIONAL(reset-flow) by default: writing contact/primary-email REDIRECTS the root recovery
channel, but completing the reset is an out-of-band step (console ''Forgot password'' email, and for
StartPrimaryEmailUpdate an OTP delivered to the NEW attacker-controlled address consumed via account:AcceptPrimaryEmailUpdate).
Never ACTIVE on the write permission alone. BLOCKED(scp_or_org_policy) if an SCP denies the account:Put*/email
actions.'
false_positive_note: 'The write permission is necessary but not sufficient: the actual root password
reset requires driving the email/OTP recovery flow. Keep CONDITIONAL(reset-flow); do NOT mark ACTIVE.
StartPrimaryEmailUpdate additionally needs AcceptPrimaryEmailUpdate with the OTP sent to the new address.
In centrally-managed orgs member self-service for account contacts is often SCP-denied -> BLOCKED.
Root MFA does not block the password reset itself but blocks final sign-in; note but do not silently
upgrade past it.'
narrative: '{principal.name} can change the recovery contact/primary email of the account (account:PutContactInformation
/ PutAlternateContact / StartPrimaryEmailUpdate) and complete the root-user password reset, taking
over the {target.name} root credentials.'