azure-adls-listkeys-full-exfiltrate

Account key (from listKeys) grants full data-plane read and SAS generation - enabling complete data exfiltration.

derived azure emits CanExfiltrate

match

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

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

where

node_class(?account) == Data and node_type(?account) == DataLake ?account.properties.isHnsEnabled == true ?principal has EFFECTIVE Microsoft.Storage/storageAccounts/listKeys/action at ?account scope node_type(?key) == AccessKey # the primary/secondary storage account key

emit

source typeIdentity
target typeData
source?principal
target?account
permissionsMicrosoft.Storage/storageAccounts/listKeys/action
conditionsiam_permission
state logicif a deny assignment/deny Azure Policy covers listKeys/action: BLOCKED; else ACTIVE. The account key itself is valid from any network — no network_reachability gate applies once the key is obtained.

Narrative

{principal.name} can list the account keys of {account.name}, then use those keys to read, copy, or delete every blob and file in the data lake and generate SAS tokens for external distribution - a full data exfiltration capability.

Raw rule rules/derived/azure/adls.yaml

id: azure-adls-listkeys-full-exfiltrate
emits: CanExfiltrate
description: "Account key (from listKeys) grants full data-plane read and SAS generation \u2014 enabling\
  \ complete data exfiltration."
match:
- - principal: null
  - HasPermission
  - account: null
- - account: null
  - ContainsCredential
  - key: null
where:
- node_class(?account) == Data and node_type(?account) == DataLake
- ?account.properties.isHnsEnabled == true
- ?principal has EFFECTIVE Microsoft.Storage/storageAccounts/listKeys/action at ?account scope
- 'node_type(?key) == AccessKey  # the primary/secondary storage account key'
emit:
  source_type: Identity
  target_type: Data
  source: ?principal
  target: ?account
  permissions:
  - Microsoft.Storage/storageAccounts/listKeys/action
  conditions:
  - iam_permission
  state_logic: "if a deny assignment/deny Azure Policy covers listKeys/action: BLOCKED; else ACTIVE. The\
    \ account key itself is valid from any network \u2014 no network_reachability gate applies once the\
    \ key is obtained."
  confidence: '0.95'
  derived_from:
  - <HasPermission edge_id (listKeys/action)>
  - "<ContainsCredential edge_id (account \u2192 AccessKey)>"
  false_positive_note: "This rule is the two-step collapse of (a) list the key and (b) use it to read\
    \ all data. The key is account-scoped and bypasses container-level RBAC, POSIX ACLs, and soft-delete\
    \ protections (keys can even be used to permanently delete versioned blobs if soft-delete retention\
    \ has expired). Separately, the key can generate SAS tokens allowing read/write by third parties with\
    \ no Entra identity. Do NOT emit CanExfiltrate on the Storage Blob Data Reader role alone \u2014 that\
    \ role cannot generate account-key-level SAS tokens and is ACL-constrained. credential-chains.yaml\
    \ read-secret-yields-identity handles the pivot from ExposesCredential \u2192 CredentialsFor \u2192\
    \ CanImpersonate when the key is the registered credential for a service principal (rare but documented\
    \ pattern)."
  narrative: "{principal.name} can list the account keys of {account.name}, then use those keys to read,\
    \ copy, or delete every blob and file in the data lake and generate SAS tokens for external distribution\
    \ \u2014 a full data exfiltration capability."
move · open · esc close