Detection rules › Kusto

Identify Mango Sandstorm powershell commands

Severity
high
Time window
1d
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

The query below identifies powershell commands used by the threat actor Mango Sandstorm. Reference: https://www.microsoft.com/security/blog/2022/08/25/mercury-leveraging-log4j-2-vulnerabilities-in-unpatched-systems-to-target-israeli-organizations/

MITRE ATT&CK coverage

TacticTechniques
Lateral MovementT1570 Lateral Tool Transfer

Event coverage

Rule body kusto

id: ce74dc9a-cb3c-4081-8c2f-7d39f6b7bae1
name: Identify Mango Sandstorm powershell commands
description: |
  'The query below identifies powershell commands used by the threat actor Mango Sandstorm.
  Reference:  https://www.microsoft.com/security/blog/2022/08/25/mercury-leveraging-log4j-2-vulnerabilities-in-unpatched-systems-to-target-israeli-organizations/'
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - LateralMovement
relevantTechniques:
  - T1570
tags:
  - Mango Sandstorm
  - Schema: ASIMFileEvent
    SchemaVersion: 0.1.0
query: |
  (union isfuzzy=true
  (SecurityEvent
  | where EventID == 4688
  | where Process has_any ("powershell.exe","powershell_ise.exe","pwsh.exe") and CommandLine has_cs "-exec bypass -w 1 -enc"
  | where CommandLine  contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
  | extend DvcHostname = Computer, ProcessId = tostring(ProcessId), ActorUsername = Account
  ),
  (DeviceProcessEvents
  | where FileName =~ "powershell.exe" and ProcessCommandLine has_cs "-exec bypass -w 1 -enc"
  | where ProcessCommandLine contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
  | extend DvcHostname = DeviceName, ProcessId = tostring(InitiatingProcessId), ActorUsername = strcat(AccountDomain, @"\", AccountName)
  ),
  (imProcessCreate
  | where Process has_any ("powershell.exe","powershell_ise.exe","pwsh.exe") and CommandLine has_cs "-exec bypass -w 1 -enc"
  | where CommandLine  contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
  | extend ProcessId = tostring(TargetProcessId)
  )
  )
  | extend AccountName = tostring(split(ActorUsername, "\\")[0]), AccountNTDomain = tostring(split(ActorUsername, "\\")[1])
  | extend HostName = tostring(split(DvcHostname, ".")[0]), DomainIndex = toint(indexof(DvcHostname, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(DvcHostname, DomainIndex + 1), DvcHostname)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActorUsername
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DvcHostname
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: HostNameDomain
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: ProcessId
version: 1.0.5
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Threat Intelligence" ]

Stages and Predicates

union isfuzzy=true (3 sources)

Each leg below queries one source; the rule matches if any leg does. Sources: SecurityEvent, DeviceProcessEvents, imProcessCreate

Leg 1: SecurityEvent

SecurityEvent
| where EventID == 4688
| where Process has_any ("powershell.exe","powershell_ise.exe","pwsh.exe") and CommandLine has_cs "-exec bypass -w 1 -enc"
| where CommandLine  contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
| extend DvcHostname = Computer, ProcessId = tostring(ProcessId), ActorUsername = Account

Leg 2: DeviceProcessEvents

DeviceProcessEvents
| where FileName =~ "powershell.exe" and ProcessCommandLine has_cs "-exec bypass -w 1 -enc"
| where ProcessCommandLine contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
| extend DvcHostname = DeviceName, ProcessId = tostring(InitiatingProcessId), ActorUsername = strcat(AccountDomain, @"\", AccountName)

Leg 3: imProcessCreate

imProcessCreate
| where Process has_any ("powershell.exe","powershell_ise.exe","pwsh.exe") and CommandLine has_cs "-exec bypass -w 1 -enc"
| where CommandLine  contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
| extend ProcessId = tostring(TargetProcessId)

Applied to the combined result

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

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
CommandLinecontains
  • UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA transforms: cased
CommandLinematch
  • -exec bypass -w 1 -enc transforms: cased, term corpus 2 (sigma 1, kusto 1)
EventIDeq
  • 4688 transforms: cased corpus 313 (splunk 283, kusto 30)
FileNameeq
  • powershell.exe corpus 3 (kusto 3)
Processmatch
  • powershell.exe corpus 2 (kusto 2)
  • powershell_ise.exe
  • pwsh.exe
ProcessCommandLinecontains
  • UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA transforms: cased
ProcessCommandLinematch
  • -exec bypass -w 1 -enc transforms: cased, term corpus 2 (sigma 1, kusto 1)

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
ActorUsernameextend
DvcHostnameextend
ProcessIdextend
AccountNTDomainextend
AccountNameextend
DomainIndexextend
HostNameextend
HostNameDomainextend