aws-secretsmanager-resource-policy-self-grant
PutResourcePolicy self-grants GetSecretValue on a secret that is credentials for a more-privileged identity - escalation.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanModifyPolicy {'secret': None}
{'secret': None} CredentialsFor {'identity': None}
where
node_class(?secret) ==
Secret
node_class(?identity) == Identity
?principal has EFFECTIVE secretsmanager:PutResourcePolicy on ?secret
privilege(?identity) > privilege(?principal)
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | ?principal |
| target | ?identity |
| permissions | secretsmanager:PutResourcePolicy secretsmanager:GetSecretValue kms:Decrypt |
| conditions | iam_permission key_permission |
| state logic | if ?secret is wrapped by the AWS-managed key (self-granted GetSecretValue then decrypts transparently same-account): ACTIVE; if ?secret is CMK-wrapped: CONDITIONAL(key_permission) — the self-grant only rewrites the SECRET resource policy, NOT the KMS key policy, so a customer CMK still blocks plaintext unless the principal separately holds kms:Decrypt. BLOCKED if an SCP denies secretsmanager:PutResourcePolicy. |
Narrative
{principal.name} can rewrite the resource policy of {secret.name} (secretsmanager:PutResourcePolicy) to self-grant GetSecretValue; the secret is credentials for the more-privileged {identity.name}, so this escalates to {identity.name} (CMK secrets still need kms:Decrypt).
Raw rule rules/derived/aws/secretsmanager.yaml
id: aws-secretsmanager-resource-policy-self-grant
emits: CanEscalateTo
description: "PutResourcePolicy self-grants GetSecretValue on a secret that is credentials for a more-privileged\
\ identity \u2014 escalation."
match:
- - principal: null
- CanModifyPolicy
- secret: null
- - secret: null
- CredentialsFor
- identity: null
where:
- node_class(?secret) == Secret
- node_class(?identity) == Identity
- ?principal has EFFECTIVE secretsmanager:PutResourcePolicy on ?secret
- privilege(?identity) > privilege(?principal)
emit:
source_type: Identity
target_type: Identity
source: ?principal
target: ?identity
permissions:
- secretsmanager:PutResourcePolicy
- secretsmanager:GetSecretValue
- kms:Decrypt
conditions:
- iam_permission
- key_permission
state_logic: "if ?secret is wrapped by the AWS-managed key (self-granted GetSecretValue then decrypts\
\ transparently same-account): ACTIVE; if ?secret is CMK-wrapped: CONDITIONAL(key_permission) \u2014\
\ the self-grant only rewrites the SECRET resource policy, NOT the KMS key policy, so a customer CMK\
\ still blocks plaintext unless the principal separately holds kms:Decrypt. BLOCKED if an SCP denies\
\ secretsmanager:PutResourcePolicy."
confidence: min(contributing_confidences) * 0.9
derived_from:
- <CanModifyPolicy(PutResourcePolicy) edge_id>
- <CredentialsFor edge_id>
false_positive_note: 'The self-grant only touches the SECRET''s resource policy, not the wrapping CMK''s
key policy: self-granting GetSecretValue does NOT bypass kms:Decrypt on a customer CMK -> CONDITIONAL(key_permission),
never assume ACTIVE for a CMK secret. AWS BlockPublicPolicy blocks a broadly public policy unless
explicitly disabled. Emit ONLY when the secret is evidenced CredentialsFor a STRICTLY more-privileged
identity; a self-grant on a secret with no CredentialsFor, or for a lateral identity, is access not
escalation. Dedup against the generic read-secret-yields-identity -> impersonate-is-escalation collapse
by (type,source,target,scope).'
narrative: '{principal.name} can rewrite the resource policy of {secret.name} (secretsmanager:PutResourcePolicy)
to self-grant GetSecretValue; the secret is credentials for the more-privileged {identity.name}, so
this escalates to {identity.name} (CMK secrets still need kms:Decrypt).'