Detection rules › Kusto

Caramel Tsunami Actor IOC - July 2021

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

Identifies a match across IOC's related to an actor tracked by Microsoft as Caramel Tsunami

MITRE ATT&CK coverage

TacticTechniques
PersistenceT1546 Event Triggered Execution

Event coverage

Rule body kusto

id: 066395ac-ef91-4993-8bf6-25c61ab0ca5a
name: Caramel Tsunami Actor IOC - July 2021
description: | 
  'Identifies a match across IOC's related to an actor tracked by Microsoft as Caramel Tsunami'
severity: High
status: Available 
requiredDataConnectors: 
  - connectorId: WindowsForwardedEvents
    dataTypes:
      - WindowsEvent
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - Persistence
relevantTechniques:
  - T1546
tags:
  - Caramel Tsunami
query:  |
  let iocs = externaldata(DateAdded:string,IoC:string,Type:string,TLP:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/SOURGUM.csv"] with (format="csv", ignoreFirstRecord=True);
  let file_path1 = (iocs | where Type =~ "filepath1" | project IoC);
  let file_path2 = (iocs | where Type =~ "filepath2" | project IoC);
  let file_path3 = (iocs | where Type =~ "filepath3" | project IoC);
  let reg_key = (iocs | where Type =~ "regkey" | project IoC);
  WindowsEvent
  | where EventID == 4688 and (EventData has_any (file_path1) or EventData has_any (file_path2) or  EventData has_any (file_path3) or EventData has_any ('reg add') or EventData has_any (reg_key) )
  | extend CommandLine = tostring(EventData.CommandLine)
  | extend NewProcessName = tostring(EventData.NewProcessName)
  | extend ParentProcessName = tostring(EventData.ParentProcessName)
  | where (CommandLine has_any (file_path1)) or
    (CommandLine has_any (file_path3)) or
    (CommandLine has 'reg add' and CommandLine has_any (reg_key) and CommandLine has_any (file_path2)) or 
    (NewProcessName has_any (file_path1)) or
    (NewProcessName has_any (file_path3)) or
    (ParentProcessName has_any (file_path1)) or 
    (ParentProcessName has_any (file_path3)) 
  | extend Account = strcat(EventData.SubjectDomainName,"\\", EventData.SubjectUserName)
  | extend NewProcessId = tostring(EventData.NewProcessId)
  | extend IP = tostring(EventData.IpAddress)
  | project TimeGenerated, Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type, IP
  | extend Alert = 'SOURGUM IOC detected'
  | extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
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
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IP 
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: NewProcessId
version: 1.0.4
kind: Scheduled

Stages and Predicates

Let binding: iocs

let iocs = externaldata(DateAdded:string,IoC:string,Type:string,TLP:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/SOURGUM.csv"] with (format="csv", ignoreFirstRecord=True);

Let binding: file_path1

let file_path1 = (iocs | where Type =~ "filepath1" | project IoC);

Derived from iocs.

Let binding: file_path2

let file_path2 = (iocs | where Type =~ "filepath2" | project IoC);

Derived from iocs.

Let binding: file_path3

let file_path3 = (iocs | where Type =~ "filepath3" | project IoC);

Derived from iocs.

Let binding: reg_key

let reg_key = (iocs | where Type =~ "regkey" | project IoC);

Derived from iocs.

Stage 1: source

WindowsEvent

Stage 2: where

| where EventID == 4688 and (EventData has_any (file_path1) or EventData has_any (file_path2) or  EventData has_any (file_path3) or EventData has_any ('reg add') or EventData has_any (reg_key) )

References file_path1, file_path2, file_path3, reg_key (defined above).

Stage 3: extend (3 consecutive steps)

| extend CommandLine = tostring(EventData.CommandLine)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend ParentProcessName = tostring(EventData.ParentProcessName)

Stage 4: where

| where (CommandLine has_any (file_path1)) or
  (CommandLine has_any (file_path3)) or
  (CommandLine has 'reg add' and CommandLine has_any (reg_key) and CommandLine has_any (file_path2)) or 
  (NewProcessName has_any (file_path1)) or
  (NewProcessName has_any (file_path3)) or
  (ParentProcessName has_any (file_path1)) or 
  (ParentProcessName has_any (file_path3))

References file_path1, file_path2, file_path3, reg_key (defined above).

Stage 5: extend (3 consecutive steps)

| extend Account = strcat(EventData.SubjectDomainName,"\\", EventData.SubjectUserName)
| extend NewProcessId = tostring(EventData.NewProcessId)
| extend IP = tostring(EventData.IpAddress)

Stage 6: project

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

Stage 7: extend (4 consecutive steps)

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

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
  • file_path1
  • file_path2
  • file_path3
  • reg add transforms: term corpus 14 (sigma 13, kusto 1)
  • reg_key
EventDatamatch
  • file_path1
  • file_path2
  • file_path3
  • reg add
  • reg_key
EventIDeq
  • 4688 transforms: cased corpus 313 (splunk 283, kusto 30)
NewProcessNamematch
  • file_path1
  • file_path3
ParentProcessNamematch
  • file_path1
  • file_path3

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
IPproject
NewProcessIdproject
NewProcessNameproject
ParentProcessNameproject
TimeGeneratedproject
Typeproject
Alertextend
AccountNTDomainextend
AccountNameextend
DomainIndexextend
HostNameextend
HostNameDomainextend