aws-rds-master-secret-yields-db-access

Reading the RDS-managed master password secret (secretsmanager:GetSecretValue on MasterUserSecretArn) yields superuser DB access - the master credential authenticates as the DB admin user.

derived aws emits CanReadData

match

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

{'db': None} ContainsSecret {'secret': None} {'principal': None} CanReadSecret {'secret': None}

where

node_type(?db) == RelationalDatabase ?db.provider_type in ['AWS::RDS::DBInstance', 'AWS::RDS::DBCluster'] ?secret.evidence_field == 'MasterUserSecret.SecretArn' node_class(?secret) == Secret

emit

source typeIdentity
target typeRelationalDatabase
source?principal
target?db
permissionssecretsmanager:GetSecretValue
conditionsiam_permission resource_policy key_permission service_state
state logicInherit weakest(ContainsSecret, CanReadSecret): ACTIVE when both are ACTIVE — the DB has a MasterUserSecretArn (ContainsSecret confirmed explicit) AND the principal's CanReadSecret on that secret is ACTIVE (secretsmanager:GetSecretValue confirmed effective, no SCP or resource-policy denial, KMS decrypt satisfied). CONDITIONAL(key_permission) when the CanReadSecret is CONDITIONAL because the secret is wrapped by a CMK and the principal lacks or has unresolved kms:Decrypt — decrypt-gates-secret-read in credential-chains.yaml handles the upgrade when decrypt is confirmed. CONDITIONAL(service_state) when the DB is STOPPED or unavailable for connections even though the secret is readable (the attacker can read the password but cannot yet connect — emit CONDITIONAL(service_state)). BLOCKED when CanReadSecret is BLOCKED (explicit deny on secretsmanager:GetSecretValue or kms:Decrypt).

Narrative

{principal.name} can read the Secrets Manager secret {secret.name} that holds the master DB password for {db.name} (secretsmanager:GetSecretValue), then connect as the database superuser. No control-plane mutation needed - this is a pure credential-read path.

Raw rule rules/derived/aws/rds.yaml

id: aws-rds-master-secret-yields-db-access
emits: CanReadData
description: "Reading the RDS-managed master password secret (secretsmanager:GetSecretValue on MasterUserSecretArn)\
  \ yields superuser DB access \u2014 the master credential authenticates as the DB admin user."
match:
- - db: null
  - ContainsSecret
  - secret: null
- - principal: null
  - CanReadSecret
  - secret: null
where:
- node_type(?db) == RelationalDatabase
- ?db.provider_type in ['AWS::RDS::DBInstance', 'AWS::RDS::DBCluster']
- ?secret.evidence_field == 'MasterUserSecret.SecretArn'
- node_class(?secret) == Secret
emit:
  source_type: Identity
  target_type: RelationalDatabase
  source: ?principal
  target: ?db
  permissions:
  - secretsmanager:GetSecretValue
  conditions:
  - iam_permission
  - resource_policy
  - key_permission
  - service_state
  state_logic: "Inherit weakest(ContainsSecret, CanReadSecret): ACTIVE when both are ACTIVE \u2014 the\
    \ DB has a MasterUserSecretArn (ContainsSecret confirmed explicit) AND the principal's CanReadSecret\
    \ on that secret is ACTIVE (secretsmanager:GetSecretValue confirmed effective, no SCP or resource-policy\
    \ denial, KMS decrypt satisfied). CONDITIONAL(key_permission) when the CanReadSecret is CONDITIONAL\
    \ because the secret is wrapped by a CMK and the principal lacks or has unresolved kms:Decrypt \u2014\
    \ decrypt-gates-secret-read in credential-chains.yaml handles the upgrade when decrypt is confirmed.\
    \ CONDITIONAL(service_state) when the DB is STOPPED or unavailable for connections even though the\
    \ secret is readable (the attacker can read the password but cannot yet connect \u2014 emit CONDITIONAL(service_state)).\
    \ BLOCKED when CanReadSecret is BLOCKED (explicit deny on secretsmanager:GetSecretValue or kms:Decrypt)."
  confidence: min(contributing_confidences) * 0.95
  derived_from:
  - ?db ContainsSecret ?secret
  - ?principal CanReadSecret ?secret
  - "credential-chains:read-secret-yields-identity (conceptual roll-up \u2014 cited, not re-derived; DB\
    \ master user is a DB-internal identity, not an IAM principal)"
  false_positive_note: "ContainsSecret must be evidenced by the presence of MasterUserSecret.SecretArn\
    \ in DescribeDBInstances/DescribeDBClusters output \u2014 only emit when the field is populated (ManageMasterUserPassword\
    \ was enabled). A DB with a manually managed master password has no MasterUserSecretArn -> ContainsSecret\
    \ does not fire -> this rule does not apply. The credential read yields superuser access ONLY if the\
    \ DB is in an AVAILABLE state and network-reachable (PubliclyAccessible or the principal has a VPC\
    \ foothold). The secret rotation by RDS means the password changes automatically; an attacker who\
    \ reads the secret but cannot connect immediately may encounter a rotated credential (typically 7-day\
    \ default rotation). Do not downgrade the edge state for rotation \u2014 it is not a blocking control.\
    \ KMS-encrypted secrets (CONDITIONAL(key_permission)) are handled by credential-chains decrypt-gates-secret-read;\
    \ do not re-derive the upgrade here."
  narrative: "{principal.name} can read the Secrets Manager secret {secret.name} that holds the master\
    \ DB password for {db.name} (secretsmanager:GetSecretValue), then connect as the database superuser.\
    \ No control-plane mutation needed \u2014 this is a pure credential-read path."
move · open · esc close