aws-kms-cross-account-credential-entry

Cross-account decrypt that unlocks THIS account's credentials is an account foothold.

derived aws emits CanEnterAccount

match

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

{'external': None} CanDecrypt {'key': None} {'acct': None} Contains {'key': None} {'secret': None} ContainsResourceReference {'key': None} {'secret': None} CredentialsFor {'identity': None}

where

node_class(?external) == Identity (CanEnterAccount source is [Identity]) node_class(?acct) == Account (Contains source is [AdministrativeBoundary]; binds the real Account node that owns ?key so the CanEnterAccount target is a bound Account, not a placeholder string) structural 'encrypted-by' link: ContainsResourceReference(secret->key), NOT LocatedIn (whose target set is [AdministrativeBoundary, Network]); see the source-class caveat in aws-kms-decrypt-unlocks-secret ?external.account != ?acct.account ?identity is a principal in ?acct (the ciphertext is a credential for THIS account) ?external can also read the ciphertext store (the secret/blob), not just the key

emit

source typeIdentity
target typeAccount
source?external
target?acct
permissionskms:Decrypt
conditionsresource_policy iam_permission condition_expression
state logicif ?external can both read the ciphertext store AND decrypt it, and the plaintext is a valid credential for ?acct: ACTIVE; else CONDITIONAL(iam_permission)

Narrative

{external.name} can decrypt {secret.name} in account {acct.name} and obtain credentials for {identity.name}, gaining a foothold in that account.

Raw rule rules/derived/aws/kms.yaml

id: aws-kms-cross-account-credential-entry
emits: CanEnterAccount
description: Cross-account decrypt that unlocks THIS account's credentials is an account foothold.
match:
- - external: null
  - CanDecrypt
  - key: null
- - acct: null
  - Contains
  - key: null
- - secret: null
  - ContainsResourceReference
  - key: null
- - secret: null
  - CredentialsFor
  - identity: null
where:
- node_class(?external) == Identity (CanEnterAccount source is [Identity])
- node_class(?acct) == Account (Contains source is [AdministrativeBoundary]; binds the real Account node
  that owns ?key so the CanEnterAccount target is a bound Account, not a placeholder string)
- 'structural ''encrypted-by'' link: ContainsResourceReference(secret->key), NOT LocatedIn (whose target
  set is [AdministrativeBoundary, Network]); see the source-class caveat in aws-kms-decrypt-unlocks-secret'
- ?external.account != ?acct.account
- ?identity is a principal in ?acct (the ciphertext is a credential for THIS account)
- ?external can also read the ciphertext store (the secret/blob), not just the key
emit:
  source_type: Identity
  target_type: Account
  source: ?external
  target: ?acct
  permissions:
  - kms:Decrypt
  conditions:
  - resource_policy
  - iam_permission
  - condition_expression
  state_logic: 'if ?external can both read the ciphertext store AND decrypt it, and the plaintext is a
    valid credential for ?acct: ACTIVE; else CONDITIONAL(iam_permission)'
  confidence: min(contributing_confidences) * 0.8
  derived_from:
  - <CanDecrypt edge_id>
  - <Contains account->key edge_id>
  - <ContainsResourceReference secret->key edge_id>
  - <CredentialsFor secret->identity edge_id>
  false_positive_note: "Bare cross-account kms:Decrypt is NOT account entry \u2014 only when the decrypted\
    \ material is itself a usable credential for the key's account AND the external principal can also\
    \ read the ciphertext store. Otherwise emit aws-kms-cross-account-key-use (CanDecrypt) instead."
  narrative: '{external.name} can decrypt {secret.name} in account {acct.name} and obtain credentials
    for {identity.name}, gaining a foothold in that account.'
move · open · esc close