Detection rules › Kusto

Zinc Actor IOCs files - October 2022

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

Identifies a match across filename and commandline IOC's related to an actor tracked by Microsoft as Zinc. Reference: https://www.microsoft.com/security/blog/2022/09/29/zinc-weaponizing-open-source-software/

MITRE ATT&CK coverage

TacticTechniques
PersistenceT1546 Event Triggered Execution

Event coverage

Rule body kusto

id: 9a7f6651-801b-491c-a548-8b454b356eaa
name: Zinc Actor IOCs files - October 2022
description: | 
  'Identifies a match across filename and commandline IOC's related to an actor tracked by Microsoft as Zinc.
  Reference: https://www.microsoft.com/security/blog/2022/09/29/zinc-weaponizing-open-source-software/'
severity: High 
status: Available
requiredDataConnectors:                
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceNetworkEvents
      - DeviceFileEvents
      - DeviceEvents
      - DeviceProcessEvents
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes: 
      - SecurityEvents          
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - Persistence
relevantTechniques:
  - T1546
tags:
  - Zinc
query:  |
  let iocs = externaldata(DateAdded:string,IoC:string,Type:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/ZincOctober2022IOCs.csv"] with (format="csv", ignoreFirstRecord=True);
  let file_path = (iocs | where Type =~ "filepath" | project IoC);
  let commandline = (iocs | where Type =~ "commandline" | project IoC);
  (union isfuzzy=true 
  (DeviceNetworkEvents
  | where  InitiatingProcessFolderPath has_any (file_path) or InitiatingProcessCommandLine has_any (commandline)
  | project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, RemoteIP, RemoteUrl, LocalIP, Type
  | extend timestamp = TimeGenerated, HostEntity = DeviceName
  ),
  (Event
  | where Source =~ "Microsoft-Windows-Sysmon"
  | where EventID == 1
  | extend EvData = parse_xml(EventData)
  | extend EventDetail = EvData.DataItem.EventData.Data
  | extend Image = EventDetail.[4].["#text"],  CommandLine = EventDetail.[10].["#text"]
  | where Image has_any (file_path)  or   CommandLine has_any (commandline)
  | project TimeGenerated, EventDetail, UserName, Computer, Type, Source, CommandLine, Image
  | extend Type = strcat(Type, ": ", Source)
  | extend timestamp = TimeGenerated, HostEntity = Computer , AccountEntity = UserName, ProcessEntity = tostring(split(Image, '\\', -1)[-1])
  ),  
  (DeviceProcessEvents
  | where  ( InitiatingProcessCommandLine has_any (file_path)) or ( InitiatingProcessCommandLine has_any (commandline))  or (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath has_any (commandline))
  | project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, InitiatingProcessSHA256, FolderPath, Type
  | extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = InitiatingProcessAccountName, ProcessEntity = InitiatingProcessFileName
  ),
  (DeviceFileEvents
  | where  (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath  has_any (commandline)) or ( InitiatingProcessCommandLine has_any (commandline)) or ( InitiatingProcessCommandLine has_any (file_path))
  | project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, RequestAccountName, RequestSourceIP, InitiatingProcessSHA256, FolderPath, Type
  | extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = RequestAccountName, ProcessEntity = InitiatingProcessFileName, AlgorithmEntity = "SHA256", FileHashEntity = InitiatingProcessSHA256
  ),
  (DeviceEvents
  | where  ( InitiatingProcessCommandLine has_any (file_path)) or ( InitiatingProcessCommandLine has_any (commandline)) or (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath has_any (commandline))
  | project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, FolderPath, Type
  | extend CommandLine = InitiatingProcessCommandLine
  | extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = InitiatingProcessAccountName, ProcessEntity = InitiatingProcessFileName
  ),
  (SecurityEvent
  | where EventID == 4688
  | where ( CommandLine has_any (file_path)) or ( CommandLine has_any (commandline))  or (NewProcessName has_any (file_path)) or (NewProcessName has_any (commandline)) or (ParentProcessName has_any (file_path)) or (ParentProcessName has_any (commandline))
  | project TimeGenerated, Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type
  | extend timestamp = TimeGenerated, HostEntity = Computer , AccountEntity = Account, ProcessEntity = NewProcessName
  )
  )
  | extend HostName = tostring(split(HostEntity, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(HostEntity, '.'), 1, -1), '.'))
  | extend Name = tostring(split(AccountEntity, '@', 0)[0]), UPNSuffix = tostring(split(AccountEntity, '@', 1)[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: RemoteIP 
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: ProcessEntity
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: AlgorithmEntity
      - identifier: Value
        columnName: FileHashEntity     
version: 1.0.3
kind: Scheduled

Stages and Predicates

Stage 0: let

let iocs = externaldata(DateAdded:string,IoC:string,Type:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/ZincOctober2022IOCs.csv"] with (format="csv", ignoreFirstRecord=True);
let file_path = (iocs | where Type =~ "filepath" | project IoC);
let commandline = (iocs | where Type =~ "commandline" | project IoC);

Stage 1: union

union isfuzzy=true

Stage 2: source time_window=21600s

DeviceNetworkEvents

Stage 3: where

| where  InitiatingProcessFolderPath has_any (file_path) or InitiatingProcessCommandLine has_any (commandline)

Stage 4: project

| project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, RemoteIP, RemoteUrl, LocalIP, Type

Stage 5: extend

| extend timestamp = TimeGenerated, HostEntity = DeviceName

Stage 6: source

Event

Stage 7: where

| where Source =~ "Microsoft-Windows-Sysmon"

Stage 8: where

| where EventID == 1

Stage 9: extend

| extend EvData = parse_xml(EventData)

Stage 10: extend

| extend EventDetail = EvData.DataItem.EventData.Data

Stage 11: extend

| extend Image = EventDetail.[4].["#text"],  CommandLine = EventDetail.[10].["#text"]

Stage 12: where

| where Image has_any (file_path)  or   CommandLine has_any (commandline)

Stage 13: project

| project TimeGenerated, EventDetail, UserName, Computer, Type, Source, CommandLine, Image

Stage 14: extend

| extend Type = strcat(Type, ": ", Source)

Stage 15: extend

| extend timestamp = TimeGenerated, HostEntity = Computer , AccountEntity = UserName, ProcessEntity = tostring(split(Image, '\\', -1)[-1])

Stage 16: source

DeviceProcessEvents

Stage 17: where

| where  ( InitiatingProcessCommandLine has_any (file_path)) or ( InitiatingProcessCommandLine has_any (commandline))  or (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath has_any (commandline))

Stage 18: project

| project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, InitiatingProcessSHA256, FolderPath, Type

Stage 19: extend

| extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = InitiatingProcessAccountName, ProcessEntity = InitiatingProcessFileName

Stage 20: source

DeviceFileEvents

Stage 21: where

| where  (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath  has_any (commandline)) or ( InitiatingProcessCommandLine has_any (commandline)) or ( InitiatingProcessCommandLine has_any (file_path))

Stage 22: project

| project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, RequestAccountName, RequestSourceIP, InitiatingProcessSHA256, FolderPath, Type

Stage 23: extend

| extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = RequestAccountName, ProcessEntity = InitiatingProcessFileName, AlgorithmEntity = "SHA256", FileHashEntity = InitiatingProcessSHA256

Stage 24: source

DeviceEvents

Stage 25: where

| where  ( InitiatingProcessCommandLine has_any (file_path)) or ( InitiatingProcessCommandLine has_any (commandline)) or (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath has_any (commandline))

Stage 26: project

| project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, FolderPath, Type

Stage 27: extend

| extend CommandLine = InitiatingProcessCommandLine

Stage 28: extend

| extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = InitiatingProcessAccountName, ProcessEntity = InitiatingProcessFileName

Stage 29: source

SecurityEvent

Stage 30: where

| where EventID == 4688

Stage 31: where

| where ( CommandLine has_any (file_path)) or ( CommandLine has_any (commandline))  or (NewProcessName has_any (file_path)) or (NewProcessName has_any (commandline)) or (ParentProcessName has_any (file_path)) or (ParentProcessName has_any (commandline))

Stage 32: project

| project TimeGenerated, Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type

Stage 33: extend

| extend timestamp = TimeGenerated, HostEntity = Computer , AccountEntity = Account, ProcessEntity = NewProcessName

Stage 34: extend

| extend HostName = tostring(split(HostEntity, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(HostEntity, '.'), 1, -1), '.'))

Stage 35: extend

| extend Name = tostring(split(AccountEntity, '@', 0)[0]), UPNSuffix = tostring(split(AccountEntity, '@', 1)[0])

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
CommandLinematch
  • commandline corpus 2 (splunk 1, kusto 1)
  • file_path
EventIDeq
  • 1 transforms: cased corpus 232 (splunk 223, kusto 9)
  • 4688 transforms: cased corpus 312 (splunk 283, kusto 29)
FolderPathmatch
  • commandline
  • file_path
Imagematch
  • file_path
InitiatingProcessCommandLinematch
  • commandline corpus 2 (splunk 1, kusto 1)
  • file_path
InitiatingProcessFolderPathmatch
  • commandline
  • file_path
NewProcessNamematch
  • commandline
  • file_path
ParentProcessNamematch
  • commandline
  • file_path

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
Accountproject
Computerproject
NewProcessIdproject
NewProcessNameproject
ParentProcessNameproject
TimeGeneratedproject
Typeproject
AccountEntityextend
HostEntityextend
ProcessEntityextend
timestampextend
DnsDomainextend
HostNameextend
Nameextend
UPNSuffixextend