aws-kms-decrypt-via-key-admin
Admin of a key (PutKeyPolicy/CreateGrant) can self-grant kms:Decrypt then use it.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?key) ==
EncryptionKey
DescribeKey.KeyUsage == ENCRYPT_DECRYPT
DescribeKey.KeyManager == CUSTOMER (customer-managed; AWS-managed key policies are immutable)
emit
| target type | EncryptionKey |
|---|---|
| source | ?principal |
| target | ?key |
| permissions | kms:PutKeyPolicy kms:CreateGrant kms:Decrypt |
| conditions | scp_or_org_policy resource_policy condition_expression |
| state logic | if PutKeyPolicy is confirmed unconstrained (no residual key-policy Deny the admin cannot remove) AND CreateGrant is not narrowed by kms:GranteePrincipal/kms:GrantConstraintType/kms:GrantIsForAWSResource: ACTIVE (attacker rewrites policy/creates a grant to itself, then decrypts); elif the existing key policy keeps a Deny the admin cannot lift or CreateGrant is grant-constrained: CONDITIONAL(resource_policy); else BLOCKED(scp_or_org_policy) when an SCP/boundary denies PutKeyPolicy/CreateGrant |
Narrative
{principal.name} administers {key.name} (kms:PutKeyPolicy / kms:CreateGrant) and can self-grant kms:Decrypt, then decrypt any data protected by it.
Raw rule rules/derived/aws/kms.yaml
id: aws-kms-decrypt-via-key-admin
emits: CanDecrypt
description: Admin of a key (PutKeyPolicy/CreateGrant) can self-grant kms:Decrypt then use it.
match:
- - principal: null
- CanAdminister
- key: null
where:
- node_type(?key) == EncryptionKey
- DescribeKey.KeyUsage == ENCRYPT_DECRYPT
- DescribeKey.KeyManager == CUSTOMER (customer-managed; AWS-managed key policies are immutable)
emit:
target_type: EncryptionKey
source: ?principal
target: ?key
permissions:
- kms:PutKeyPolicy
- kms:CreateGrant
- kms:Decrypt
conditions:
- scp_or_org_policy
- resource_policy
- condition_expression
state_logic: 'if PutKeyPolicy is confirmed unconstrained (no residual key-policy Deny the admin cannot
remove) AND CreateGrant is not narrowed by kms:GranteePrincipal/kms:GrantConstraintType/kms:GrantIsForAWSResource:
ACTIVE (attacker rewrites policy/creates a grant to itself, then decrypts); elif the existing key
policy keeps a Deny the admin cannot lift or CreateGrant is grant-constrained: CONDITIONAL(resource_policy);
else BLOCKED(scp_or_org_policy) when an SCP/boundary denies PutKeyPolicy/CreateGrant'
confidence: min(contributing_confidences) * 0.9
derived_from:
- <CanAdminister edge_id>
false_positive_note: "Only for CUSTOMER-managed keys \u2014 AWS-managed (aws/*) key policies cannot\
\ be edited and CreateGrant is service-restricted. Self-grant is doubly gated: even with kms:PutKeyPolicy\
\ the default key-policy requirement that at least one principal retain access can leave a residual\
\ Deny the admin cannot remove, and kms:CreateGrant is constrained by kms:GranteePrincipal / kms:GrantConstraintType\
\ / kms:GrantIsForAWSResource. Keep ACTIVE only when PutKeyPolicy is confirmed unconstrained -> otherwise\
\ CONDITIONAL(resource_policy). An SCP or permission- boundary denying kms:PutKeyPolicy/kms:CreateGrant,\
\ or a Deny the admin cannot remove, makes this BLOCKED."
narrative: '{principal.name} administers {key.name} (kms:PutKeyPolicy / kms:CreateGrant) and can self-grant
kms:Decrypt, then decrypt any data protected by it.'