Hunting Playbook: LOLBins in Microsoft Defender XDR.
The executable was signed. The file was already on the machine. The name looked familiar.
No exploit kit appeared. No strange binary was dropped. No obvious malware filename stood out.
But a trusted Windows tool had launched with unusual arguments, connected to the internet, and created activity that did not match normal administration.
Agent Foskett did not ask whether the binary was legitimate.
He asked whether the behaviour was legitimate.
Lesson overview
Learn how to hunt for Living Off the Land Binaries by analysing process execution, command-line arguments, parent-child relationships, network activity and suspicious use of trusted Windows tools inside Microsoft Defender XDR.
Why LOLBins matter
The LOLBin hunting workflow
Step 1 — Hunt for common LOLBins
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
let TimeFrame = 7d;
let LOLBins = dynamic(["rundll32.exe", "regsvr32.exe", "mshta.exe", "certutil.exe", "bitsadmin.exe", "wmic.exe", "installutil.exe", "msbuild.exe"]);
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ (LOLBins)
| project Timestamp,
DeviceName,
AccountName,
FileName,
ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine
| order by Timestamp descStep 2 — Look for suspicious command-line indicators
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
let TimeFrame = 7d;
let LOLBins = dynamic(["rundll32.exe", "regsvr32.exe", "mshta.exe", "certutil.exe", "bitsadmin.exe"]);
let SuspiciousTerms = dynamic(["http", "https", "javascript", "vbscript", "scrobj", "urlcache", "decode", "download", "temp", "appdata"]);
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ (LOLBins)
| where ProcessCommandLine has_any (SuspiciousTerms)
| project Timestamp,
DeviceName,
AccountName,
FileName,
ProcessCommandLine,
InitiatingProcessFileName
| order by Timestamp descStep 3 — Focus on suspicious parent processes
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
let TimeFrame = 7d;
let SuspiciousParents = dynamic(["winword.exe", "excel.exe", "outlook.exe", "powerpnt.exe", "chrome.exe", "msedge.exe", "wscript.exe", "cscript.exe", "powershell.exe"]);
let LOLBins = dynamic(["rundll32.exe", "regsvr32.exe", "mshta.exe", "certutil.exe", "bitsadmin.exe"]);
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ (LOLBins)
| where InitiatingProcessFileName in~ (SuspiciousParents)
| project Timestamp,
DeviceName,
AccountName,
InitiatingProcessFileName,
FileName,
ProcessCommandLine
| order by Timestamp descStep 4 — Correlate LOLBins with network activity
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
let TimeFrame = 7d;
let LOLBinActivity =
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ ("rundll32.exe", "regsvr32.exe", "mshta.exe", "certutil.exe", "bitsadmin.exe")
| project ProcessTime = Timestamp,
DeviceId,
DeviceName,
AccountName,
FileName,
ProcessCommandLine;
let NetworkActivity =
DeviceNetworkEvents
| where Timestamp > ago(TimeFrame)
| project NetworkTime = Timestamp,
DeviceId,
InitiatingProcessFileName,
RemoteUrl,
RemoteIP,
RemotePort;
LOLBinActivity
| join kind=leftouter NetworkActivity on DeviceId
| where NetworkTime between (ProcessTime .. ProcessTime + 30m)
| project ProcessTime,
NetworkTime,
DeviceName,
AccountName,
FileName,
ProcessCommandLine,
RemoteUrl,
RemoteIP,
RemotePort
| order by ProcessTime descStep 5 — Summarise LOLBin usage by device and account
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
let TimeFrame = 14d;
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where FileName in~ ("rundll32.exe", "regsvr32.exe", "mshta.exe", "certutil.exe", "bitsadmin.exe", "wmic.exe")
| summarize ExecutionCount = count(),
Accounts = make_set(AccountName, 20),
Commands = make_set(ProcessCommandLine, 20),
FirstSeen = min(Timestamp),
LastSeen = max(Timestamp)
by DeviceName,
FileName
| order by ExecutionCount descStep 6 — Build the LOLBin investigation timeline
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
let TimeFrame = 7d;
let DeviceToReview = "DEVICE-01";
DeviceProcessEvents
| where Timestamp > ago(TimeFrame)
| where DeviceName =~ DeviceToReview
| where FileName in~ ("rundll32.exe", "regsvr32.exe", "mshta.exe", "certutil.exe", "bitsadmin.exe")
or InitiatingProcessFileName in~ ("rundll32.exe", "regsvr32.exe", "mshta.exe", "certutil.exe", "bitsadmin.exe")
| project Timestamp,
DeviceName,
AccountName,
InitiatingProcessFileName,
FileName,
ProcessCommandLine
| order by Timestamp ascCommon LOLBin investigation clues
False positives to consider
Investigation checklist
Related Agent Foskett Academy lessons
Coming next
Final thought
Hunting Playbook LOLBins in Microsoft Defender XDR
Agent Foskett Academy Lesson 106 teaches defenders how to hunt for Living Off the Land Binaries using Microsoft Defender XDR, DeviceProcessEvents, command-line analysis and KQL investigation workflows.
Learn LOLBin hunting with KQL and Microsoft Defender XDR
This lesson explains how defenders investigate rundll32.exe, regsvr32.exe, mshta.exe, certutil.exe, bitsadmin.exe and other trusted Windows tools when they are abused by attackers.
Microsoft Defender XDR LOLBin investigation tutorial
Use process telemetry, parent-child relationships, network evidence, file activity and timelines to distinguish legitimate administration from malicious Living Off the Land activity.
