Files
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

169 lines
7.2 KiB
Python

#!/usr/bin/env python3
"""Agent for performing memory forensics with Volatility3 plugins."""
import json
import argparse
import subprocess
from datetime import datetime
VOL3_PLUGINS = {
"pslist": "windows.pslist.PsList",
"pstree": "windows.pstree.PsTree",
"psscan": "windows.psscan.PsScan",
"dlllist": "windows.dlllist.DllList",
"handles": "windows.handles.Handles",
"netscan": "windows.netscan.NetScan",
"netstat": "windows.netstat.NetStat",
"malfind": "windows.malfind.Malfind",
"cmdline": "windows.cmdline.CmdLine",
"filescan": "windows.filescan.FileScan",
"hivelist": "windows.registry.hivelist.HiveList",
"hashdump": "windows.hashdump.Hashdump",
"lsadump": "windows.lsadump.Lsadump",
"svcscan": "windows.svcscan.SvcScan",
"ssdt": "windows.ssdt.SSDT",
"callbacks": "windows.callbacks.Callbacks",
"vadinfo": "windows.vadinfo.VadInfo",
"envars": "windows.envars.Envars",
}
def run_vol3_plugin(memory_dump, plugin_name, extra_args=None):
"""Execute a Volatility3 plugin against a memory dump."""
plugin_class = VOL3_PLUGINS.get(plugin_name, plugin_name)
cmd = ["vol", "-f", memory_dump, "-r", "json", plugin_class]
if extra_args:
cmd += extra_args
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
if result.returncode != 0:
return {"error": result.stderr[:500], "plugin": plugin_name}
data = json.loads(result.stdout)
return {"plugin": plugin_name, "memory_dump": memory_dump, "results": data, "count": len(data) if isinstance(data, list) else 1}
except FileNotFoundError:
return {"error": "Volatility3 (vol) not found — pip install volatility3"}
except json.JSONDecodeError:
return {"plugin": plugin_name, "raw_output": result.stdout[:2000]}
except subprocess.TimeoutExpired:
return {"error": f"Plugin {plugin_name} timed out after 300s"}
def detect_malicious_processes(memory_dump):
"""Run process analysis plugins to detect suspicious processes."""
suspicious_names = ["mimikatz", "procdump", "psexec", "cobalt", "beacon",
"meterpreter", "nc.exe", "ncat", "powercat", "lazagne",
"bloodhound", "rubeus", "certify", "seatbelt", "sharphound"]
pslist = run_vol3_plugin(memory_dump, "pslist")
cmdline = run_vol3_plugin(memory_dump, "cmdline")
suspicious = []
if isinstance(pslist.get("results"), list):
for proc in pslist["results"]:
name = str(proc.get("ImageFileName", proc.get("Name", ""))).lower()
pid = proc.get("PID", proc.get("pid", ""))
ppid = proc.get("PPID", proc.get("ppid", ""))
if any(s in name for s in suspicious_names):
suspicious.append({"pid": pid, "name": name, "ppid": ppid, "reason": "KNOWN_ATTACK_TOOL"})
if name == "cmd.exe" and str(ppid) not in ("0", "1"):
suspicious.append({"pid": pid, "name": name, "ppid": ppid, "reason": "CMD_SPAWNED"})
if name in ("powershell.exe", "pwsh.exe"):
suspicious.append({"pid": pid, "name": name, "ppid": ppid, "reason": "POWERSHELL_EXECUTION"})
return {
"memory_dump": memory_dump,
"total_processes": len(pslist.get("results", [])) if isinstance(pslist.get("results"), list) else 0,
"suspicious_processes": suspicious,
"timestamp": datetime.utcnow().isoformat(),
}
def detect_injected_code(memory_dump):
"""Run malfind to detect code injection."""
malfind = run_vol3_plugin(memory_dump, "malfind")
findings = []
if isinstance(malfind.get("results"), list):
for entry in malfind["results"]:
findings.append({
"pid": entry.get("PID", entry.get("pid")),
"process": entry.get("Process", entry.get("process", "")),
"address": entry.get("Start VPN", entry.get("start", "")),
"protection": entry.get("Protection", entry.get("protection", "")),
"tag": entry.get("Tag", ""),
})
return {
"memory_dump": memory_dump,
"injections_found": len(findings),
"findings": findings[:30],
"severity": "HIGH" if findings else "INFO",
}
def analyze_network_connections(memory_dump):
"""Extract network connections from memory."""
netscan = run_vol3_plugin(memory_dump, "netscan")
connections = []
if isinstance(netscan.get("results"), list):
for conn in netscan["results"]:
connections.append({
"pid": conn.get("PID", conn.get("pid")),
"process": conn.get("Owner", conn.get("process", "")),
"local_addr": conn.get("LocalAddr", conn.get("local_addr", "")),
"local_port": conn.get("LocalPort", conn.get("local_port", "")),
"remote_addr": conn.get("ForeignAddr", conn.get("remote_addr", "")),
"remote_port": conn.get("ForeignPort", conn.get("remote_port", "")),
"state": conn.get("State", conn.get("state", "")),
})
external = [c for c in connections if c.get("remote_addr") and not c["remote_addr"].startswith(("0.0", "127.", "10.", "192.168.", "172."))]
return {
"total_connections": len(connections),
"external_connections": len(external),
"connections": connections[:30],
"external_only": external[:20],
}
def full_triage(memory_dump):
"""Run full memory triage with key plugins."""
return {
"memory_dump": memory_dump,
"timestamp": datetime.utcnow().isoformat(),
"processes": detect_malicious_processes(memory_dump),
"injections": detect_injected_code(memory_dump),
"network": analyze_network_connections(memory_dump),
}
def main():
parser = argparse.ArgumentParser(description="Volatility3 Memory Forensics Agent")
sub = parser.add_subparsers(dest="command")
p = sub.add_parser("plugin", help="Run specific Volatility3 plugin")
p.add_argument("--dump", required=True, help="Memory dump file path")
p.add_argument("--name", required=True, help="Plugin name or class", choices=list(VOL3_PLUGINS.keys()))
p.add_argument("--args", nargs="*", help="Extra plugin arguments")
m = sub.add_parser("malproc", help="Detect malicious processes")
m.add_argument("--dump", required=True)
i = sub.add_parser("inject", help="Detect code injection")
i.add_argument("--dump", required=True)
n = sub.add_parser("network", help="Analyze network connections")
n.add_argument("--dump", required=True)
t = sub.add_parser("triage", help="Full memory triage")
t.add_argument("--dump", required=True)
args = parser.parse_args()
if args.command == "plugin":
result = run_vol3_plugin(args.dump, args.name, args.args)
elif args.command == "malproc":
result = detect_malicious_processes(args.dump)
elif args.command == "inject":
result = detect_injected_code(args.dump)
elif args.command == "network":
result = analyze_network_connections(args.dump)
elif args.command == "triage":
result = full_triage(args.dump)
else:
parser.print_help()
return
print(json.dumps(result, indent=2, default=str))
if __name__ == "__main__":
main()