azure-netapp-listcredentials-exposes-ad-cred

listCredentials/action permission on ANF account grants CanReadCredential on the AD join AccessKey.

derived azure emits CanReadCredential

match

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

{'principal': None} HasPermission {'account': None} {'account': None} ContainsCredential {'cred': None}

where

node_class(?account) == Storage and node_type(?account) == FileStorage ?account.provider_type == 'Microsoft.NetApp/netAppAccounts' node_type(?cred) == AccessKey # The credential is the AD machine account password ?principal has EFFECTIVE Microsoft.NetApp/netAppAccounts/listCredentials/action at ?account scope (Owner, Contributor, or custom role with listCredentials)

emit

source typeIdentity
target typeAccessKey
source?principal
target?cred
permissionsMicrosoft.NetApp/netAppAccounts/listCredentials/action
conditionsiam_permission
state logicif a deny assignment or Azure Policy deny effect covers listCredentials/action at the effective scope: BLOCKED; elif ?account.properties.activeDirectories is empty (no AD join, no credential): DO NOT EMIT (the ContainsCredential match will not fire); else ACTIVE. This is an ARM control-plane action — NOT gated by volume-level network access controls or delegated subnet firewall rules.

Narrative

{principal.name} can invoke listCredentials on ANF account {account.name} (Microsoft.NetApp/netAppAccounts/listCredentials/action), retrieving the Active Directory join AccessKey credential. Combined with the credential-chains rule read-credential-yields-identity (pending linchpin extension), this enables lateral movement to the AD machine account within the joined domain.

Raw rule rules/derived/azure/netapp.yaml

id: azure-netapp-listcredentials-exposes-ad-cred
emits: CanReadCredential
description: listCredentials/action permission on ANF account grants CanReadCredential on the AD join
  AccessKey.
match:
- - principal: null
  - HasPermission
  - account: null
- - account: null
  - ContainsCredential
  - cred: null
where:
- node_class(?account) == Storage and node_type(?account) == FileStorage
- ?account.provider_type == 'Microsoft.NetApp/netAppAccounts'
- 'node_type(?cred) == AccessKey  # The credential is the AD machine account password'
- ?principal has EFFECTIVE Microsoft.NetApp/netAppAccounts/listCredentials/action at ?account scope (Owner,
  Contributor, or custom role with listCredentials)
emit:
  source_type: Identity
  target_type: AccessKey
  source: ?principal
  target: ?cred
  permissions:
  - Microsoft.NetApp/netAppAccounts/listCredentials/action
  conditions:
  - iam_permission
  state_logic: "if a deny assignment or Azure Policy deny effect covers listCredentials/action at the\
    \ effective scope: BLOCKED; elif ?account.properties.activeDirectories is empty (no AD join, no credential):\
    \ DO NOT EMIT (the ContainsCredential match will not fire); else ACTIVE. This is an ARM control-plane\
    \ action \u2014 NOT gated by volume-level network access controls or delegated subnet firewall rules."
  confidence: '0.93'
  derived_from:
  - ?principal HasPermission ?account
  - ?account ContainsCredential ?cred
  false_positive_note: Only emit when [?account, ContainsCredential, ?cred] matches (i.e., ANF account
    is AD-joined). If the account is not AD-joined, the explicit rule does not emit ContainsCredential
    and this derived rule never fires. listCredentials/action is an ARM control-plane call; it succeeds
    from any network reaching management.azure.com regardless of volume-level network firewall rules.
    Do NOT apply a network_reachability condition to the CanReadCredential emit itself. Emit BLOCKED only
    when an Azure deny assignment explicitly covers listCredentials/action. The downstream CredentialsFor
    link (AD join credential -> AD machine account identity) requires a dedicated linchpin rule in credential-chains.yaml
    (read-credential-yields-identity, not read-secret-yields-identity, because AccessKey is NOT in CanReadSecret's
    target set). The identity model confirms that the AD machine account (MachineIdentity node) is a valid
    CanImpersonate target, so the credential chain fires automatically when both edges are present and
    the linchpin rule is in place.
  narrative: '{principal.name} can invoke listCredentials on ANF account {account.name} (Microsoft.NetApp/netAppAccounts/listCredentials/action),
    retrieving the Active Directory join AccessKey credential. Combined with the credential-chains rule
    read-credential-yields-identity (pending linchpin extension), this enables lateral movement to the
    AD machine account within the joined domain.'
move · open · esc close