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

153 lines
5.7 KiB
Python

#!/usr/bin/env python3
"""Autoruns Persistence Analysis Agent - Analyzes Windows autostart entries for malware persistence."""
import json
import csv
import re
import logging
import argparse
from datetime import datetime
from collections import Counter
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)
SUSPICIOUS_PATHS = [
r"\\temp\\", r"\\tmp\\", r"\\appdata\\local\\temp",
r"\\public\\", r"\\programdata\\", r"\\users\\default",
r"\\recycler\\", r"\\windows\\debug",
]
SUSPICIOUS_COMMANDS = [
"powershell", "cmd.exe /c", "wscript", "cscript", "mshta",
"regsvr32", "rundll32", "certutil", "bitsadmin",
"schtasks", "msiexec /q", "forfiles",
]
KNOWN_PERSISTENCE_LOCATIONS = [
"HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
"HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",
"HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",
"HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
"HKLM\\SYSTEM\\CurrentControlSet\\Services",
"Task Scheduler",
"Startup Folder",
"WMI",
]
def parse_autoruns_csv(csv_file):
"""Parse Autoruns CSV export file."""
entries = []
with open(csv_file, "r", encoding="utf-8-sig", errors="ignore") as f:
reader = csv.DictReader(f, delimiter=",")
for row in reader:
entries.append({
"time": row.get("Time", ""),
"entry_location": row.get("Entry Location", ""),
"entry": row.get("Entry", ""),
"enabled": row.get("Enabled", ""),
"category": row.get("Category", ""),
"profile": row.get("Profile", ""),
"description": row.get("Description", ""),
"company": row.get("Company", ""),
"image_path": row.get("Image Path", ""),
"version": row.get("Version", ""),
"launch_string": row.get("Launch String", ""),
"md5": row.get("MD5", ""),
"sha1": row.get("SHA-1", ""),
"sha256": row.get("SHA-256", ""),
"signer": row.get("Signer", ""),
"vt_detection": row.get("VT detection", ""),
})
logger.info("Parsed %d autoruns entries from %s", len(entries), csv_file)
return entries
def analyze_entry(entry):
"""Analyze a single autoruns entry for suspicious indicators."""
findings = []
image_path = (entry.get("image_path") or "").lower()
launch_string = (entry.get("launch_string") or "").lower()
signer = entry.get("signer") or ""
vt = entry.get("vt_detection") or ""
company = entry.get("company") or ""
for pattern in SUSPICIOUS_PATHS:
if re.search(pattern, image_path, re.IGNORECASE):
findings.append({"type": "Suspicious file path", "severity": "high", "detail": image_path})
break
for cmd in SUSPICIOUS_COMMANDS:
if cmd.lower() in launch_string:
findings.append({"type": "LOLBin in launch string", "severity": "high", "detail": cmd})
break
if signer in ("(Not verified)", "") or "(Not verified)" in signer:
findings.append({"type": "Unsigned binary", "severity": "medium", "detail": signer})
if vt and "/" in vt:
try:
detections, total = vt.split("/")
if int(detections.strip()) > 0:
findings.append({"type": "VirusTotal detections", "severity": "critical", "detail": vt})
except (ValueError, AttributeError):
pass
if not company and entry.get("enabled") == "enabled":
findings.append({"type": "No company info", "severity": "low", "detail": "Enabled entry without publisher"})
return findings
def analyze_all_entries(entries):
"""Analyze all autoruns entries and generate findings."""
all_findings = []
for entry in entries:
entry_findings = analyze_entry(entry)
if entry_findings:
all_findings.append({
"entry": entry.get("entry"),
"location": entry.get("entry_location"),
"category": entry.get("category"),
"image_path": entry.get("image_path"),
"findings": entry_findings,
"max_severity": max((f["severity"] for f in entry_findings), key=lambda s: {"critical": 4, "high": 3, "medium": 2, "low": 1}.get(s, 0)),
})
return all_findings
def generate_report(entries, findings):
"""Generate persistence analysis report."""
categories = Counter(e.get("category", "Unknown") for e in entries)
critical = [f for f in findings if f["max_severity"] == "critical"]
report = {
"timestamp": datetime.utcnow().isoformat(),
"total_entries": len(entries),
"enabled_entries": len([e for e in entries if e.get("enabled") == "enabled"]),
"suspicious_entries": len(findings),
"critical_entries": len(critical),
"category_breakdown": dict(categories.most_common()),
"findings": findings,
}
print(f"AUTORUNS REPORT: {len(entries)} entries, {len(findings)} suspicious, {len(critical)} critical")
return report
def main():
parser = argparse.ArgumentParser(description="Autoruns Persistence Analysis Agent")
parser.add_argument("--csv-file", required=True, help="Autoruns CSV export file")
parser.add_argument("--output", default="autoruns_report.json")
args = parser.parse_args()
entries = parse_autoruns_csv(args.csv_file)
findings = analyze_all_entries(entries)
report = generate_report(entries, findings)
with open(args.output, "w") as f:
json.dump(report, f, indent=2)
logger.info("Report saved to %s", args.output)
if __name__ == "__main__":
main()