#!/usr/bin/env python3 """Agent for implementing and auditing AppLocker application whitelisting policies.""" import json import argparse import re import xml.etree.ElementTree as ET from datetime import datetime from pathlib import Path APPLOCKER_EVENT_IDS = { 8002: ("EXE/DLL allowed", "INFO"), 8003: ("EXE/DLL denied", "HIGH"), 8004: ("EXE/DLL would be denied (audit)", "MEDIUM"), 8005: ("Script allowed", "INFO"), 8006: ("Script denied", "HIGH"), 8007: ("Script would be denied (audit)", "MEDIUM"), 8020: ("Packaged app allowed", "INFO"), 8021: ("Packaged app denied", "HIGH"), 8022: ("Packaged app would be denied (audit)", "MEDIUM"), } def parse_applocker_policy(xml_path): """Parse an exported AppLocker policy XML file.""" tree = ET.parse(xml_path) root = tree.getroot() ns = {"al": "http://schemas.microsoft.com/windows/2006/applocker"} rules = [] for collection in root.findall(".//al:RuleCollection", ns): rule_type = collection.get("Type", "") mode = collection.get("EnforcementMode", "NotConfigured") for rule in collection: rule_data = { "collection": rule_type, "enforcement_mode": mode, "name": rule.get("Name", ""), "action": rule.get("Action", ""), "user_or_group": rule.get("UserOrGroupSid", ""), "type": rule.tag.replace(f"{{{ns.get('al', '')}}}", ""), } conditions = rule.findall(".//*") for cond in conditions: if "Path" in cond.tag: rule_data["path"] = cond.get("Path", "") elif "Publisher" in cond.tag: rule_data["publisher"] = cond.get("PublisherName", "") elif "Hash" in cond.tag: rule_data["hash"] = cond.get("Data", "") rules.append(rule_data) return rules def audit_applocker_rules(rules): """Audit AppLocker rules for security weaknesses.""" findings = [] for rule in rules: if rule.get("enforcement_mode") == "AuditOnly": findings.append({ "collection": rule["collection"], "issue": "audit_mode_only", "severity": "MEDIUM", "recommendation": "Switch to Enforce mode after validation", }) if rule.get("enforcement_mode") == "NotConfigured": findings.append({ "collection": rule["collection"], "issue": "not_configured", "severity": "HIGH", "recommendation": "Enable enforcement for this rule collection", }) path = rule.get("path", "") if path and rule.get("action") == "Allow": risky_paths = [r"\\Users\\", r"\\Temp\\", r"\\Downloads\\", r"\\AppData\\", r"\\ProgramData\\"] for rp in risky_paths: if re.search(rp, path, re.IGNORECASE): findings.append({ "rule_name": rule["name"], "path": path, "issue": "allow_from_user_writable_path", "severity": "CRITICAL", }) break if rule.get("user_or_group") == "S-1-1-0" and rule.get("action") == "Allow": findings.append({ "rule_name": rule["name"], "issue": "allow_for_everyone", "severity": "MEDIUM", }) return findings def analyze_applocker_events(log_path): """Analyze AppLocker event logs for blocked and audit events.""" events = [] with open(log_path) as f: for line in f: try: entry = json.loads(line) except json.JSONDecodeError: continue event_id = int(entry.get("EventID", entry.get("event_id", 0))) if event_id in APPLOCKER_EVENT_IDS: desc, severity = APPLOCKER_EVENT_IDS[event_id] events.append({ "event_id": event_id, "description": desc, "severity": severity, "timestamp": entry.get("TimeCreated", entry.get("timestamp", "")), "computer": entry.get("Computer", entry.get("hostname", "")), "user": entry.get("User", entry.get("user", "")), "file_path": entry.get("FilePath", entry.get("file_path", "")), "publisher": entry.get("Publisher", ""), }) denied = [e for e in events if "denied" in e["description"].lower()] audit = [e for e in events if "audit" in e["description"].lower()] return {"total_events": len(events), "denied": denied, "audit_blocks": audit} def generate_baseline_policy(): """Generate a baseline AppLocker policy recommendation.""" return { "exe_rules": { "enforcement_mode": "Enforce", "default_rules": [ {"action": "Allow", "path": "%PROGRAMFILES%\\*", "scope": "Everyone"}, {"action": "Allow", "path": "%WINDIR%\\*", "scope": "Everyone"}, {"action": "Allow", "path": "*", "scope": "BUILTIN\\Administrators"}, ], }, "script_rules": { "enforcement_mode": "Enforce", "default_rules": [ {"action": "Allow", "path": "%PROGRAMFILES%\\*", "scope": "Everyone"}, {"action": "Allow", "path": "%WINDIR%\\*", "scope": "Everyone"}, ], }, "dll_rules": { "enforcement_mode": "AuditOnly", "note": "Start with audit mode due to high volume", }, } def main(): parser = argparse.ArgumentParser(description="AppLocker Whitelisting Agent") parser.add_argument("--policy", help="Exported AppLocker policy XML") parser.add_argument("--events", help="AppLocker event log (JSON lines)") parser.add_argument("--output", default="applocker_audit_report.json") parser.add_argument("--action", choices=["audit", "events", "baseline", "full"], default="full") args = parser.parse_args() report = {"generated_at": datetime.utcnow().isoformat(), "findings": {}} if args.action in ("audit", "full") and args.policy: rules = parse_applocker_policy(args.policy) findings = audit_applocker_rules(rules) report["findings"]["policy_audit"] = findings report["findings"]["total_rules"] = len(rules) print(f"[+] Policy rules: {len(rules)}, Issues: {len(findings)}") if args.action in ("events", "full") and args.events: result = analyze_applocker_events(args.events) report["findings"]["event_analysis"] = result print(f"[+] Events: {result['total_events']}, Denied: {len(result['denied'])}") if args.action in ("baseline", "full"): baseline = generate_baseline_policy() report["findings"]["baseline_policy"] = baseline print("[+] Baseline policy generated") with open(args.output, "w") as fout: json.dump(report, fout, indent=2, default=str) print(f"[+] Report saved to {args.output}") if __name__ == "__main__": main()