azure-auto-exposes-runas-cred

An Automation Account with a legacy RunAs account exposes the RunAs service principal certificate to any runbook via Get-AutomationConnection.

derived azure emits ExposesCredential

match

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

{'account': None} ContainsSecret {'runasConn': None}

where

node_type(?account) == AutomationService node_type(?runasConn) == Certificate ?runasConn is of connection type AzureServicePrincipal with CertificateThumbprint field (RunAs account)

emit

source typeGenericCompute
target typeApplicationIdentity
source<runbook (BuildWorker) in ?account>
target<RunAs service principal (ApplicationIdentity)>
conditionstrigger_exists
state logicACTIVE when the account contains a RunAs Connection of type AzureServicePrincipal with CertificateThumbprint (ContainsSecret confirmed) and a runbook can execute in the sandbox. CONDITIONAL(trigger_exists) if no confirmed run path exists. BLOCKED if the RunAs account has been removed.

Narrative

Automation Account {account.name} has a legacy RunAs connection; any runbook can call Get-AutomationConnection to retrieve the RunAs SP certificate and authenticate as the RunAs service principal {target.name} without holding the SP's client secret in ARM RBAC.

Raw rule rules/derived/azure/automation.yaml

id: azure-auto-exposes-runas-cred
emits: ExposesCredential
description: An Automation Account with a legacy RunAs account exposes the RunAs service principal certificate
  to any runbook via Get-AutomationConnection.
match:
- - account: null
  - ContainsSecret
  - runasConn: null
where:
- node_type(?account) == AutomationService
- node_type(?runasConn) == Certificate
- ?runasConn is of connection type AzureServicePrincipal with CertificateThumbprint field (RunAs account)
emit:
  source_type: GenericCompute
  target_type: ApplicationIdentity
  source: <runbook (BuildWorker) in ?account>
  target: <RunAs service principal (ApplicationIdentity)>
  permissions: []
  conditions:
  - trigger_exists
  state_logic: ACTIVE when the account contains a RunAs Connection of type AzureServicePrincipal with
    CertificateThumbprint (ContainsSecret confirmed) and a runbook can execute in the sandbox. CONDITIONAL(trigger_exists)
    if no confirmed run path exists. BLOCKED if the RunAs account has been removed.
  confidence: 0.95
  derived_from:
  - 'ContainsSecret: ?account stores RunAs Connection (Certificate)'
  - 'credential-chains.yaml: ExposesCredential + CredentialsFor(RunAs cert -> SP) => CanImpersonate'
  false_positive_note: The RunAs certificate private key is NOT returned via ARM `connections/read`; it
    is only accessible from within a runbook via `Get-AutomationConnection`. This edge therefore only
    materializes when an attacker can execute code in the sandbox (combined with a CanModifyCode or CanTrigger
    edge). Do not emit as a standalone credential read from ARM. Verify the connection type is AzureServicePrincipal
    with a CertificateThumbprint field (the RunAs pattern); a non-RunAs connection is not this threat.
    RunAs accounts are deprecated; check for their presence explicitly.
  narrative: Automation Account {account.name} has a legacy RunAs connection; any runbook can call Get-AutomationConnection
    to retrieve the RunAs SP certificate and authenticate as the RunAs service principal {target.name}
    without holding the SP's client secret in ARM RBAC.
move · open · esc close