#!/usr/bin/env python3 """DEP/ASLR Memory Protection Agent - audits processes and binaries for memory protection mitigations.""" import json import argparse import logging import subprocess import os from datetime import datetime logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger(__name__) def check_windows_dep_policy(): """Check Windows DEP/NX policy via bcdedit.""" cmd = ["bcdedit", "/enum", "{current}"] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) dep_policy = "unknown" for line in result.stdout.split("\n"): if "nx" in line.lower(): dep_policy = line.split()[-1] if line.split() else "unknown" return {"dep_policy": dep_policy, "recommended": "AlwaysOn"} def check_windows_process_mitigations(): """Check process mitigation policies using PowerShell Get-ProcessMitigation.""" cmd = ["powershell", "-Command", "Get-Process | ForEach-Object { try { $m = Get-ProcessMitigation -Id $_.Id -ErrorAction Stop; " "[PSCustomObject]@{Name=$_.Name;PID=$_.Id;DEP=$m.DEP.Enable;ASLR=$m.ASLR.ForceRelocateImages;" "CFG=$m.CFG.Enable;StrictHandle=$m.StrictHandle.Enable} } catch {} } | ConvertTo-Json"] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) try: processes = json.loads(result.stdout) if result.stdout else [] if isinstance(processes, dict): processes = [processes] return processes except json.JSONDecodeError: return [] def check_linux_aslr(): """Check Linux ASLR configuration.""" try: with open("/proc/sys/kernel/randomize_va_space") as f: value = int(f.read().strip()) levels = {0: "disabled", 1: "partial", 2: "full"} return {"aslr_level": value, "status": levels.get(value, "unknown"), "recommended": 2} except (FileNotFoundError, ValueError): return {"aslr_level": -1, "status": "unknown"} def check_linux_nx_support(): """Check NX bit support on Linux.""" result = subprocess.run(["grep", "nx", "/proc/cpuinfo"], capture_output=True, text=True, timeout=120) return {"nx_supported": "nx" in result.stdout.lower()} def check_elf_pie_relro(binary_path): """Check ELF binary for PIE, RELRO, stack canary, and NX using readelf/checksec.""" findings = {"binary": binary_path, "pie": False, "relro": "none", "nx": False, "canary": False} readelf_cmd = subprocess.run(["readelf", "-h", binary_path], capture_output=True, text=True, timeout=120) if "DYN" in readelf_cmd.stdout: findings["pie"] = True readelf_d = subprocess.run(["readelf", "-d", binary_path], capture_output=True, text=True, timeout=120) if "BIND_NOW" in readelf_d.stdout: findings["relro"] = "full" elif "GNU_RELRO" in readelf_d.stdout: findings["relro"] = "partial" readelf_s = subprocess.run(["readelf", "-s", binary_path], capture_output=True, text=True, timeout=120) if "__stack_chk_fail" in readelf_s.stdout: findings["canary"] = True readelf_l = subprocess.run(["readelf", "-l", binary_path], capture_output=True, text=True, timeout=120) for line in readelf_l.stdout.split("\n"): if "GNU_STACK" in line and "RWE" not in line: findings["nx"] = True score = sum([findings["pie"], findings["relro"] == "full", findings["nx"], findings["canary"]]) findings["protection_score"] = f"{score}/4" return findings def scan_directory_binaries(directory, extensions=None): """Scan directory for binaries and check protections.""" if extensions is None: extensions = [""] results = [] for root, dirs, files in os.walk(directory): for fname in files: fpath = os.path.join(root, fname) if os.path.isfile(fpath) and os.access(fpath, os.X_OK): try: result = check_elf_pie_relro(fpath) results.append(result) except Exception: continue if len(results) >= 100: break return results def generate_report(system_checks, binary_checks): weak_binaries = [b for b in binary_checks if not b.get("pie") or b.get("relro") == "none"] report = { "timestamp": datetime.utcnow().isoformat(), "system_protections": system_checks, "binaries_scanned": len(binary_checks), "weak_binaries": len(weak_binaries), "protection_summary": { "pie_enabled": sum(1 for b in binary_checks if b.get("pie")), "full_relro": sum(1 for b in binary_checks if b.get("relro") == "full"), "nx_enabled": sum(1 for b in binary_checks if b.get("nx")), "canary_present": sum(1 for b in binary_checks if b.get("canary")), }, "weak_binary_details": weak_binaries[:20], } return report def main(): parser = argparse.ArgumentParser(description="DEP/ASLR Memory Protection Audit Agent") parser.add_argument("--scan-dir", default="/usr/bin", help="Directory to scan binaries") parser.add_argument("--platform", choices=["linux", "windows", "auto"], default="auto") parser.add_argument("--output", default="memory_protection_report.json") args = parser.parse_args() platform = args.platform if platform == "auto": platform = "windows" if os.name == "nt" else "linux" system_checks = {} if platform == "linux": system_checks["aslr"] = check_linux_aslr() system_checks["nx"] = check_linux_nx_support() binary_checks = scan_directory_binaries(args.scan_dir) else: system_checks["dep"] = check_windows_dep_policy() process_mitigations = check_windows_process_mitigations() binary_checks = [] for proc in process_mitigations: binary_checks.append({ "binary": proc.get("Name", ""), "pid": proc.get("PID", 0), "pie": bool(proc.get("ASLR")), "nx": bool(proc.get("DEP")), "canary": False, "relro": "n/a", }) report = generate_report(system_checks, binary_checks) with open(args.output, "w") as f: json.dump(report, f, indent=2, default=str) logger.info("Scanned %d binaries, %d with weak protections", report["binaries_scanned"], report["weak_binaries"]) print(json.dumps(report, indent=2, default=str)) if __name__ == "__main__": main()