aws-secretsmanager-value-tamper

PutSecretValue/UpdateSecret plants an attacker-controlled credential in the secret that any consumer refetches and trusts.

derived aws emits ExposesCredential

match

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

{'principal': None} CanModifyConfiguration {'secret': None} {'secret': None} CredentialsFor {'identity': None}

where

node_class(?secret) == Secret node_class(?identity) == Identity ?principal has EFFECTIVE secretsmanager:PutSecretValue OR secretsmanager:UpdateSecret on ?secret

emit

source?secret
target?identity
permissionssecretsmanager:PutSecretValue secretsmanager:UpdateSecret
conditionsiam_permission consumer_refetch
state logicCONDITIONAL(consumer_refetch) — the write itself is ACTIVE (the explicit CanModifyConfiguration edge), but the poisoning only yields access once a downstream consumer refetches the secret and authenticates with the planted credential; until an evidenced consumer exists, this stays CONDITIONAL. BLOCKED on an SCP/resource-policy Deny of PutSecretValue/UpdateSecret.

Narrative

{principal.name} can overwrite the value of {secret.name} (secretsmanager:PutSecretValue), planting a credential for {identity.name} that any consumer refetching the secret will trust.

Raw rule rules/derived/aws/secretsmanager.yaml

id: aws-secretsmanager-value-tamper
emits: ExposesCredential
description: PutSecretValue/UpdateSecret plants an attacker-controlled credential in the secret that any
  consumer refetches and trusts.
match:
- - principal: null
  - CanModifyConfiguration
  - secret: null
- - secret: null
  - CredentialsFor
  - identity: null
where:
- node_class(?secret) == Secret
- node_class(?identity) == Identity
- ?principal has EFFECTIVE secretsmanager:PutSecretValue OR secretsmanager:UpdateSecret on ?secret
emit:
  source: ?secret
  target: ?identity
  permissions:
  - secretsmanager:PutSecretValue
  - secretsmanager:UpdateSecret
  conditions:
  - iam_permission
  - consumer_refetch
  state_logic: "CONDITIONAL(consumer_refetch) \u2014 the write itself is ACTIVE (the explicit CanModifyConfiguration\
    \ edge), but the poisoning only yields access once a downstream consumer refetches the secret and\
    \ authenticates with the planted credential; until an evidenced consumer exists, this stays CONDITIONAL.\
    \ BLOCKED on an SCP/resource-policy Deny of PutSecretValue/UpdateSecret."
  confidence: min(contributing_confidences) * 0.8
  derived_from:
  - <CanModifyConfiguration(PutSecretValue) edge_id>
  - <CredentialsFor edge_id>
  false_positive_note: 'A write is credential POISONING, not a read: it does NOT reveal the existing value,
    so do not chain it to read-secret-yields-identity (that needs CanReadSecret). It only yields access
    if some downstream consumer refetches and trusts the attacker-planted credential -> CONDITIONAL(consumer_refetch)
    unless a consuming resource (ContainsSecret/ContainsResourceReference pointing at this secret) is
    evidenced. Secrets Manager encrypts on write using the secret''s key policy (which grants the service
    kms:GenerateDataKey/Encrypt), so the writer does NOT need kms:Encrypt themselves. Emit only when CredentialsFor
    is evidenced; a poisoned GenericSecret with no known consumer authenticates nothing.'
  narrative: '{principal.name} can overwrite the value of {secret.name} (secretsmanager:PutSecretValue),
    planting a credential for {identity.name} that any consumer refetching the secret will trust.'
move · open · esc close