Detection rules › Kusto

SUNBURST suspicious SolarWinds child processes (Normalized Process Events)

Severity
medium
Time window
1d
Author
Yuval Naor
Source
github.com/Azure/Azure-Sentinel

Identifies suspicious child processes of SolarWinds.Orion.Core.BusinessLayer.dll that may be evidence of the SUNBURST backdoor References: - https://www.fireeye.com/blog/threat-research/2020/12/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor.html - https://gist.github.com/olafhartong/71ffdd4cab4b6acd5cbcd1a0691ff82f To use this analytics rule, make sure you have deployed the ASIM normalization parsers'

MITRE ATT&CK coverage

Telemetry coverage

Rule body

id: 631d02df-ab51-46c1-8d72-32d0cfec0720
name: SUNBURST suspicious SolarWinds child processes (Normalized Process Events)
description: |
  Identifies suspicious child processes of SolarWinds.Orion.Core.BusinessLayer.dll that may be evidence of the SUNBURST backdoor
  References:
  - https://www.fireeye.com/blog/threat-research/2020/12/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor.html
  - https://gist.github.com/olafhartong/71ffdd4cab4b6acd5cbcd1a0691ff82f
  To use this analytics rule, make sure you have deployed the [ASIM normalization parsers](https://aka.ms/ASimProcessEvent)'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - Persistence
relevantTechniques:
  - T1059
  - T1543
tags:
  - Id: 4a3073ac-7383-48a9-90a8-eb6716183a54
    version: 1.0.0
  - Schema: ASIMProcessEvent
    SchemaVersion: 0.1.0
  - Solorigate
  - NOBELIUM

query:  |
  let excludeProcs = dynamic([@"\SolarWinds\Orion\APM\APMServiceControl.exe", @"\SolarWinds\Orion\ExportToPDFCmd.Exe", @"\SolarWinds.Credentials\SolarWinds.Credentials.Orion.WebApi.exe", @"\SolarWinds\Orion\Topology\SolarWinds.Orion.Topology.Calculator.exe", @"\SolarWinds\Orion\Database-Maint.exe", @"\SolarWinds.Orion.ApiPoller.Service\SolarWinds.Orion.ApiPoller.Service.exe", @"\Windows\SysWOW64\WerFault.exe"]);
  imProcessCreate
  | where Process hassuffix 'solarwinds.businesslayerhost.exe'
  | where not(Process has_any (excludeProcs))
  | extend AccountName = tostring(split(ActorUsername, @'\')[1]), AccountNTDomain = tostring(split(ActorUsername, @'\')[0])
  | extend HostName = tostring(split(Dvc, ".")[0]), DomainIndex = toint(indexof(Dvc, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Dvc, DomainIndex + 1), Dvc)
  | project-away DomainIndex
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActorUsername
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Dvc
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: AlgorithmType
      - identifier: Value
        columnName: TargetFileMD5
version: 1.1.6
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Yuval Naor
    support:
        tier: Community
    categories:
        domains: [ "Security - 0-day Vulnerability" ]

Stages and Predicates

Let binding: excludeProcs used in Stage 3

let excludeProcs = dynamic([@"\SolarWinds\Orion\APM\APMServiceControl.exe", @"\SolarWinds\Orion\ExportToPDFCmd.Exe", @"\SolarWinds.Credentials\SolarWinds.Credentials.Orion.WebApi.exe", @"\SolarWinds\Orion\Topology\SolarWinds.Orion.Topology.Calculator.exe", @"\SolarWinds\Orion\Database-Maint.exe", @"\SolarWinds.Orion.ApiPoller.Service\SolarWinds.Orion.ApiPoller.Service.exe", @"\Windows\SysWOW64\WerFault.exe"]);

Stage 1: source

imProcessCreate

Stage 2: where

| where Process hassuffix 'solarwinds.businesslayerhost.exe'

Stage 3: where

| where not(Process has_any (excludeProcs))

Stage 4: extend (3 consecutive steps)

| extend AccountName = tostring(split(ActorUsername, @'\')[1]), AccountNTDomain = tostring(split(ActorUsername, @'\')[0])
| extend HostName = tostring(split(Dvc, ".")[0]), DomainIndex = toint(indexof(Dvc, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Dvc, DomainIndex + 1), Dvc)

Stage 5: project-away

| project-away DomainIndex

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
Processmatch\SolarWinds\Orion\APM\APMServiceControl.exe, \SolarWinds\Orion\ExportToPDFCmd.Exe, \SolarWinds.Credentials\SolarWinds.Credentials.Orion.WebApi.exe, \SolarWinds\Orion\Topology\SolarWinds.Orion.Topology.Calculator.exe, \SolarWinds\Orion\Database-Maint.exe, \SolarWinds.Orion.ApiPoller.Service\SolarWinds.Orion.ApiPoller.Service.exe, \Windows\SysWOW64\WerFault.exeexcludes:Process

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Processends_with
  • solarwinds.businesslayerhost.exe
field:"Process" kind:ends_with value:"solarwinds.businesslayerhost.exe"

Output fields

These fields are emitted when the rule matches.

FieldSource
AccountNTDomainextend
AccountNameextend
HostNameextend
HostNameDomainextend