aws-kms-decrypt-via-key-admin

Admin of a key (PutKeyPolicy/CreateGrant) can self-grant kms:Decrypt then use it.

derived aws emits CanDecrypt

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanAdminister {'key': None}

where

node_type(?key) == EncryptionKey DescribeKey.KeyUsage == ENCRYPT_DECRYPT DescribeKey.KeyManager == CUSTOMER (customer-managed; AWS-managed key policies are immutable)

emit

target typeEncryptionKey
source?principal
target?key
permissionskms:PutKeyPolicy kms:CreateGrant kms:Decrypt
conditionsscp_or_org_policy resource_policy condition_expression
state logicif 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.'
move · open · esc close