#!/usr/bin/env python3 """Detect Living Off the Land Binaries (LOLBAS) abuse via process telemetry and Sigma rules.""" import json import argparse from datetime import datetime from collections import defaultdict LOLBIN_SIGNATURES = { "certutil.exe": { "suspicious_args": ["-urlcache", "-split", "-decode", "-encode", "-f http", "-verifyctl"], "mitre": "T1140", "description": "Certificate utility abused for download/decode", }, "mshta.exe": { "suspicious_args": ["http://", "https://", "javascript:", "vbscript:", ".hta"], "mitre": "T1218.005", "description": "HTML Application host executing remote content", }, "regsvr32.exe": { "suspicious_args": ["/s /n /u /i:http", "/i:http", "scrobj.dll", ".sct"], "mitre": "T1218.010", "description": "COM scriptlet execution via regsvr32 (Squiblydoo)", }, "rundll32.exe": { "suspicious_args": ["javascript:", "http://", "shell32.dll,ShellExec_RunDLL", "comsvcs.dll,MiniDump"], "mitre": "T1218.011", "description": "Rundll32 proxy execution or credential dumping", }, "msbuild.exe": { "suspicious_args": [".xml", ".csproj", "/p:", "inline task"], "mitre": "T1127.001", "description": "MSBuild inline task code execution", }, "bitsadmin.exe": { "suspicious_args": ["/transfer", "/create", "/addfile", "/resume", "/complete"], "mitre": "T1197", "description": "BITS job used for file download or persistence", }, "wmic.exe": { "suspicious_args": ["process call create", "/node:", "os get", "format:"], "mitre": "T1047", "description": "WMI command-line for execution or reconnaissance", }, "cmstp.exe": { "suspicious_args": ["/ni", "/s", ".inf"], "mitre": "T1218.003", "description": "CMSTP UAC bypass with malicious INF", }, } SUSPICIOUS_PARENTS = { "winword.exe", "excel.exe", "powerpnt.exe", "outlook.exe", "wmiprvse.exe", "svchost.exe", "taskeng.exe", "cmd.exe", } def parse_process_events(log_path): """Parse Sysmon or Windows 4688 process creation events from JSON log.""" events = [] with open(log_path) as f: for line in f: line = line.strip() if not line: continue try: event = json.loads(line) events.append(event) except json.JSONDecodeError: continue return events def detect_lolbin_abuse(events): """Detect LOLBin abuse from process creation events.""" detections = [] for event in events: image = event.get("Image", event.get("NewProcessName", "")).lower() cmdline = event.get("CommandLine", event.get("ProcessCommandLine", "")).lower() parent = event.get("ParentImage", event.get("ParentProcessName", "")).lower() binary_name = image.rsplit("\\", 1)[-1] if "\\" in image else image.rsplit("/", 1)[-1] if binary_name not in LOLBIN_SIGNATURES: continue sig = LOLBIN_SIGNATURES[binary_name] matched_args = [arg for arg in sig["suspicious_args"] if arg.lower() in cmdline] if not matched_args: continue parent_name = parent.rsplit("\\", 1)[-1] if "\\" in parent else parent.rsplit("/", 1)[-1] parent_suspicious = parent_name in SUSPICIOUS_PARENTS severity = "high" if parent_suspicious else "medium" if len(matched_args) > 1: severity = "critical" detections.append({ "timestamp": event.get("UtcTime", event.get("TimeCreated", datetime.utcnow().isoformat())), "binary": binary_name, "command_line": event.get("CommandLine", event.get("ProcessCommandLine", "")), "parent_process": parent, "parent_suspicious": parent_suspicious, "matched_signatures": matched_args, "mitre_technique": sig["mitre"], "description": sig["description"], "severity": severity, "user": event.get("User", event.get("SubjectUserName", "unknown")), "pid": event.get("ProcessId", event.get("NewProcessId", "")), }) return detections def generate_sigma_rule(binary_name): """Generate a Sigma detection rule for a specific LOLBin.""" if binary_name not in LOLBIN_SIGNATURES: return None sig = LOLBIN_SIGNATURES[binary_name] rule = { "title": f"Suspicious {binary_name} Execution", "id": f"lolbas-{binary_name.replace('.exe', '')}-detection", "status": "experimental", "description": sig["description"], "references": ["https://lolbas-project.github.io/"], "logsource": {"category": "process_creation", "product": "windows"}, "detection": { "selection": {"Image|endswith": f"\\{binary_name}"}, "condition_args": { "CommandLine|contains": sig["suspicious_args"] }, "condition": "selection and condition_args", }, "falsepositives": ["Legitimate administrative use"], "level": "high", "tags": [f"attack.{sig['mitre'].lower()}"], } return rule def build_report(detections, log_path): """Build structured detection report.""" by_binary = defaultdict(list) for d in detections: by_binary[d["binary"]].append(d) severity_counts = defaultdict(int) for d in detections: severity_counts[d["severity"]] += 1 return { "report_time": datetime.utcnow().isoformat(), "log_source": log_path, "total_detections": len(detections), "severity_summary": dict(severity_counts), "detections_by_binary": {k: len(v) for k, v in by_binary.items()}, "mitre_techniques": list({d["mitre_technique"] for d in detections}), "detections": detections, } def main(): parser = argparse.ArgumentParser(description="LOLBAS Abuse Detection Agent") parser.add_argument("--log-file", required=True, help="JSON log file with process creation events") parser.add_argument("--output", default="lolbas_detections.json", help="Output report path") parser.add_argument("--generate-sigma", action="store_true", help="Generate Sigma rules for all LOLBins") args = parser.parse_args() events = parse_process_events(args.log_file) detections = detect_lolbin_abuse(events) report = build_report(detections, args.log_file) if args.generate_sigma: report["sigma_rules"] = {} for binary in LOLBIN_SIGNATURES: report["sigma_rules"][binary] = generate_sigma_rule(binary) with open(args.output, "w") as f: json.dump(report, f, indent=2, default=str) print(f"[+] Analyzed {len(events)} events, found {len(detections)} LOLBin abuse detections") print(f"[+] Report saved to {args.output}") if __name__ == "__main__": main()