aws-kms-decrypt-via-reencrypt
ReEncryptFrom proves KMS can decrypt the source ciphertext; with an attacker-decryptable destination key, data is recoverable.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?key) ==
EncryptionKey
?principal has EFFECTIVE kms:ReEncryptFrom on ?key, permitted by identity + key policy
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'principal': None}
CanDecrypt {'destkey': None}
emit
| target type | EncryptionKey |
|---|---|
| source | ?principal |
| target | ?key |
| permissions | kms:ReEncryptFrom kms:ReEncryptTo |
| conditions | key_permission resource_policy condition_expression |
| state logic | if matched(optional[0]) (attacker controls a decryptable destination key) AND policies permit: ACTIVE; else CONDITIONAL(key_permission) — ReEncryptFrom alone never returns plaintext |
Narrative
{principal.name} can ReEncrypt ciphertext from {key.name} (kms:ReEncryptFrom); paired with a key it can decrypt, this recovers the data.
Raw rule rules/derived/aws/kms.yaml
id: aws-kms-decrypt-via-reencrypt
emits: CanDecrypt
description: ReEncryptFrom proves KMS can decrypt the source ciphertext; with an attacker-decryptable
destination key, data is recoverable.
match:
- - principal: null
- CanRead
- key: null
where:
- node_type(?key) == EncryptionKey
- ?principal has EFFECTIVE kms:ReEncryptFrom on ?key, permitted by identity + key policy
optional:
- - principal: null
- CanDecrypt
- destkey: null
emit:
target_type: EncryptionKey
source: ?principal
target: ?key
permissions:
- kms:ReEncryptFrom
- kms:ReEncryptTo
conditions:
- key_permission
- resource_policy
- condition_expression
state_logic: "if matched(optional[0]) (attacker controls a decryptable destination key) AND policies\
\ permit: ACTIVE; else CONDITIONAL(key_permission) \u2014 ReEncryptFrom alone never returns plaintext"
confidence: min(contributing_confidences) * 0.6
derived_from:
- <effective kms:ReEncryptFrom permission>
- <CanDecrypt destkey?>
false_positive_note: "kms:ReEncryptFrom NEVER returns plaintext \u2014 it only re-wraps ciphertext under\
\ another key. Data is recovered only if the attacker can decrypt the DESTINATION key (ReEncryptTo\
\ target). Without that, this is a data-movement/persistence primitive, not exfil -> keep CONDITIONAL\
\ and low confidence (0.6)."
narrative: '{principal.name} can ReEncrypt ciphertext from {key.name} (kms:ReEncryptFrom); paired with
a key it can decrypt, this recovers the data.'