Detection rules › Kusto

Dev-0270 Malicious Powershell usage

Status
available
Severity
high
Time window
6h
Source
github.com/Azure/Azure-Sentinel

DEV-0270 heavily uses powershell to achieve their objective at various stages of their attack. To locate powershell related activity tied to the actor, Microsoft Sentinel customers can run the following query.

MITRE ATT&CK coverage

Telemetry coverage

Rule body

id: 422ca2bf-598b-4872-82bb-5f7e8fa731e7
name: Dev-0270 Malicious Powershell usage
description: | 
  'DEV-0270 heavily uses powershell to achieve their objective at various stages of their attack. To locate powershell related activity tied to the actor, Microsoft Sentinel customers can run the following query.'
severity: High 
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
status: Available
tactics: 
  - Exfiltration
  - DefenseEvasion
relevantTechniques:
  -  T1048
  -  T1562
tags:
  - Dev-0270
query: |
  (union isfuzzy=true
  (SecurityEvent
  | where EventID==4688
  | extend FileName=tostring(split(NewProcessName, @'\')[(-1)]),  ProcessCommandLine = CommandLine, InitiatingProcessFileName=ParentProcessName
  | where (FileName =~ "powershell.exe" and ProcessCommandLine has_all("try", "Add-MpPreference", "-ExclusionPath", "ProgramData", "catch")) or (FileName =~ 'powershell.exe' and ProcessCommandLine has_all('Add-PSSnapin', 'Get-Recipient', '-ExpandProperty', 'EmailAddresses', 'SmtpAddress', '-hidetableheaders') )
  | project TimeGenerated, Computer, Account, AccountDomain, ProcessName, ProcessNameFullPath = NewProcessName, InitiatingProcessFileName, EventID, Activity, CommandLine, EventSourceName, Type
  ),
  (DeviceProcessEvents 
  | where (FileName =~ "powershell.exe" and ((ProcessCommandLine has_all("try", "Add-MpPreference", "-ExclusionPath", "ProgramData", "catch"))  or (ProcessCommandLine has_all('Add-PSSnapin', 'Get-Recipient', '-ExpandProperty', 'EmailAddresses', 'SmtpAddress', '-hidetableheaders'))))
  or ( InitiatingProcessFileName =~ 'powershell.exe' and (((InitiatingProcessCommandLine has_all('$file=', 'dllhost.exe', 'Invoke-WebRequest', '-OutFile')) or ((InitiatingProcessCommandLine has_all('$admins=', 'System.Security.Principal.SecurityIdentifier', 'Translate', '-split', 'localgroup', '/add', '$rdp='))))))
  | extend Account = strcat(InitiatingProcessAccountDomain, @'\', InitiatingProcessAccountName),  Computer = DeviceName
  )
  )
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
  | extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
version: 1.0.4
kind: Scheduled

Stages and Predicates

union isfuzzy=true (2 sources)

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

Leg 1: SecurityEvent

SecurityEvent
| where EventID==4688
| extend FileName=tostring(split(NewProcessName, @'\')[(-1)]),  ProcessCommandLine = CommandLine, InitiatingProcessFileName=ParentProcessName
| where (FileName =~ "powershell.exe" and ProcessCommandLine has_all("try", "Add-MpPreference", "-ExclusionPath", "ProgramData", "catch")) or (FileName =~ 'powershell.exe' and ProcessCommandLine has_all('Add-PSSnapin', 'Get-Recipient', '-ExpandProperty', 'EmailAddresses', 'SmtpAddress', '-hidetableheaders') )
| project TimeGenerated, Computer, Account, AccountDomain, ProcessName, ProcessNameFullPath = NewProcessName, InitiatingProcessFileName, EventID, Activity, CommandLine, EventSourceName, Type

Leg 2: DeviceProcessEvents

DeviceProcessEvents 
| where (FileName =~ "powershell.exe" and ((ProcessCommandLine has_all("try", "Add-MpPreference", "-ExclusionPath", "ProgramData", "catch"))  or (ProcessCommandLine has_all('Add-PSSnapin', 'Get-Recipient', '-ExpandProperty', 'EmailAddresses', 'SmtpAddress', '-hidetableheaders'))))
or ( InitiatingProcessFileName =~ 'powershell.exe' and (((InitiatingProcessCommandLine has_all('$file=', 'dllhost.exe', 'Invoke-WebRequest', '-OutFile')) or ((InitiatingProcessCommandLine has_all('$admins=', 'System.Security.Principal.SecurityIdentifier', 'Translate', '-split', 'localgroup', '/add', '$rdp='))))))
| extend Account = strcat(InitiatingProcessAccountDomain, @'\', InitiatingProcessAccountName),  Computer = DeviceName

Applied to the combined result

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

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventIDeq
  • 4688 corpus 317 (splunk 283, kusto 33, elastic 1)
field:"EventID" kind:eq value:"4688"
FileNameeq
  • powershell.exe corpus 3 (kusto 3)
field:"file_name" kind:eq value:"powershell.exe"
InitiatingProcessCommandLinematch
  • $admins= transforms: term
  • $file= transforms: term
  • $rdp= transforms: term
  • -OutFile transforms: term corpus 2 (sigma 1, kusto 1)
  • -split transforms: term
  • /add transforms: term corpus 9 (sigma 5, splunk 2, kusto 2)
  • Invoke-WebRequest transforms: term corpus 15 (sigma 10, elastic 3, chronicle 1, kusto 1)
  • System.Security.Principal.SecurityIdentifier transforms: term
  • Translate transforms: term
  • dllhost.exe transforms: term corpus 2 (kusto 2)
  • localgroup transforms: term corpus 2 (sigma 1, kusto 1)
field:"CommandLine" kind:match
InitiatingProcessFileNameeq
  • powershell.exe corpus 39 (elastic 36, kusto 2, splunk 1)
field:"parent_process_name" kind:eq value:"powershell.exe"
ProcessCommandLinematch
  • -ExclusionPath transforms: term corpus 3 (kusto 2, elastic 1)
  • -ExpandProperty transforms: term corpus 2 (sigma 1, kusto 1)
  • -hidetableheaders transforms: term corpus 2 (sigma 1, kusto 1)
  • Add-MpPreference transforms: term corpus 4 (sigma 2, elastic 1, kusto 1)
  • Add-PSSnapin transforms: term corpus 3 (sigma 2, kusto 1)
  • EmailAddresses transforms: term corpus 2 (sigma 1, kusto 1)
  • Get-Recipient transforms: term corpus 2 (sigma 1, kusto 1)
  • ProgramData transforms: term
  • SmtpAddress transforms: term corpus 2 (sigma 1, kusto 1)
  • catch transforms: term
  • try transforms: term
field:"CommandLine" kind:match

Output fields

These fields are emitted when the rule matches.

FieldSource
Accountextend
AccountDomainproject
Activityproject
CommandLineproject
Computerextend
EventIDproject
EventSourceNameproject
InitiatingProcessFileNameproject
ProcessNameproject
ProcessNameFullPathproject
TimeGeneratedproject
Typeproject
DomainIndexextend
HostNameextend
HostNameDomainextend
AccountNTDomainextend
AccountNameextend