aws-secretsmanager-value-tamper
PutSecretValue/UpdateSecret plants an attacker-controlled credential in the secret that any consumer refetches and trusts.
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 |
| permissions | secretsmanager:PutSecretValue secretsmanager:UpdateSecret |
| conditions | iam_permission consumer_refetch |
| state logic | CONDITIONAL(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.'