Detection rules › Kusto

Qakbot Discovery Activies

Status
available
Severity
medium
Time window
5m
Group by
DeviceId, DeviceName
Source
github.com/Azure/Azure-Sentinel

This query searches for injected processes launching discovery activity. Qakbot has been observed leading to ransomware in numerous instances. It looks for discovery commands such as net.exe, whoami.exe, nslookup.exe, netstat.exe, arp.exe, and ping.exe.

MITRE ATT&CK coverage

Event coverage

Rule body kusto

id: ba9db6b2-3d05-42ae-8aee-3a15bbe29f27
name: Qakbot Discovery Activies
description: |
  This query searches for injected processes launching discovery activity. Qakbot has been observed leading to ransomware in numerous instances. It looks for discovery commands such as net.exe, whoami.exe, nslookup.exe, netstat.exe, arp.exe, and ping.exe.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - Discovery
  - Execution
relevantTechniques:
  - T1140
  - T1010
  - T1059
tags:
  - Ransomware
  - Qakbot
  - Discovery
query: |
  DeviceProcessEvents 
  | where InitiatingProcessFileName in~('mobsync.exe','explorer.exe')
  | where (FileName =~ 'net.exe' and InitiatingProcessCommandLine has_all('view','/all'))
       or (FileName =~ 'whoami.exe' and InitiatingProcessCommandLine has '/all')
       or (FileName =~ 'nslookup.exe' and InitiatingProcessCommandLine has_all('querytype=ALL','timeout=10'))
       or (FileName =~ 'netstat.exe' and InitiatingProcessCommandLine has '-nao')
       or (FileName =~ 'arp.exe' and InitiatingProcessCommandLine has '-a')
       or (FileName =~ 'ping.exe' and InitiatingProcessCommandLine has '-t' and InitiatingProcessCommandLine endswith '127.0.0.1')
  | summarize DiscoveryCommands = dcount(InitiatingProcessCommandLine), make_set(InitiatingProcessFileName), make_set(FileName), make_set(InitiatingProcessCommandLine) by DeviceId, DeviceName, bin(TimeGenerated, 5m)   
  | where DiscoveryCommands >= 3
  | extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
  | extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
entityMappings:  
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DeviceName
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

DeviceProcessEvents

Stage 2: where

| where InitiatingProcessFileName in~('mobsync.exe','explorer.exe')

Stage 3: where

| where (FileName =~ 'net.exe' and InitiatingProcessCommandLine has_all('view','/all'))
     or (FileName =~ 'whoami.exe' and InitiatingProcessCommandLine has '/all')
     or (FileName =~ 'nslookup.exe' and InitiatingProcessCommandLine has_all('querytype=ALL','timeout=10'))
     or (FileName =~ 'netstat.exe' and InitiatingProcessCommandLine has '-nao')
     or (FileName =~ 'arp.exe' and InitiatingProcessCommandLine has '-a')
     or (FileName =~ 'ping.exe' and InitiatingProcessCommandLine has '-t' and InitiatingProcessCommandLine endswith '127.0.0.1')

Stage 4: summarize

| summarize DiscoveryCommands = dcount(InitiatingProcessCommandLine), make_set(InitiatingProcessFileName), make_set(FileName), make_set(InitiatingProcessCommandLine) by DeviceId, DeviceName, bin(TimeGenerated, 5m)
Threshold
ge 3

Stage 5: where

| where DiscoveryCommands >= 3

Stage 6: extend

| extend HostName = iff(DeviceName has '.', substring(DeviceName, 0, indexof(DeviceName, '.')), DeviceName)
HostName =
ifDeviceName has "."substring(DeviceName, 0, indexof(DeviceName, '.'))
elseDeviceName

Stage 7: extend

| extend DnsDomain = iff(DeviceName has '.', substring(DeviceName, indexof(DeviceName, '.') + 1), "")
DnsDomain =
ifDeviceName has "."substring(DeviceName, (indexof(DeviceName, '.') + 1))
else""

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
DiscoveryCommandsge
  • 3 transforms: cased
FileNameeq
  • arp.exe
  • net.exe
  • netstat.exe
  • nslookup.exe
  • ping.exe corpus 2 (kusto 2)
  • whoami.exe
InitiatingProcessCommandLineends_with
  • 127.0.0.1
InitiatingProcessCommandLinematch
  • -a transforms: term
  • -nao transforms: term
  • -t transforms: term
  • /all
  • querytype=ALL
  • timeout=10
  • view
InitiatingProcessFileNamein
  • explorer.exe corpus 20 (elastic 19, splunk 1)
  • mobsync.exe

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
DeviceIdsummarize
DeviceNamesummarize
DiscoveryCommandssummarize
HostNameextend
DnsDomainextend