mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-14 03:15:16 +03:00
c21af3347e
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
154 lines
5.7 KiB
Python
154 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for hunting registry-based persistence mechanisms on Windows."""
|
|
|
|
import json
|
|
import argparse
|
|
import subprocess
|
|
import re
|
|
from datetime import datetime
|
|
|
|
PERSISTENCE_KEYS = {
|
|
"run_keys": [
|
|
r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
|
|
r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
|
|
r"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run",
|
|
r"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce",
|
|
r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx",
|
|
],
|
|
"winlogon": [
|
|
r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon",
|
|
],
|
|
"ifeo": [
|
|
r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options",
|
|
],
|
|
"appinit": [
|
|
r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows",
|
|
],
|
|
"shell_extensions": [
|
|
r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellExecuteHooks",
|
|
r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad",
|
|
],
|
|
"browser_helpers": [
|
|
r"HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects",
|
|
],
|
|
"com_hijack": [
|
|
r"HKCU\SOFTWARE\Classes\CLSID",
|
|
],
|
|
"boot_execute": [
|
|
r"HKLM\SYSTEM\CurrentControlSet\Control\Session Manager",
|
|
],
|
|
}
|
|
|
|
SUSPICIOUS_PATTERNS = [
|
|
r"\\temp\\", r"\\tmp\\", r"\\appdata\\local\\temp",
|
|
r"powershell.*-enc", r"powershell.*-nop",
|
|
r"cmd\.exe\s+/c\s+", r"mshta\.exe", r"rundll32\.exe.*javascript",
|
|
r"regsvr32\.exe.*/s\s+/n\s+/u\s+/i:",
|
|
r"\\users\\public\\", r"\\programdata\\[^m]",
|
|
r"certutil.*-decode", r"bitsadmin.*transfer",
|
|
r"base64", r"downloadstring", r"iex\s*\(",
|
|
]
|
|
|
|
|
|
def scan_persistence_keys(categories=None):
|
|
"""Scan specified registry persistence key categories."""
|
|
if categories is None:
|
|
categories = list(PERSISTENCE_KEYS.keys())
|
|
results = {"timestamp": datetime.utcnow().isoformat(), "categories": {}, "all_suspicious": []}
|
|
for category in categories:
|
|
keys = PERSISTENCE_KEYS.get(category, [])
|
|
category_findings = []
|
|
for key in keys:
|
|
try:
|
|
proc = subprocess.run(
|
|
["reg", "query", key, "/s"], capture_output=True, text=True, timeout=10
|
|
)
|
|
if proc.returncode == 0:
|
|
entries = _parse_reg(proc.stdout, key)
|
|
for entry in entries:
|
|
entry["suspicious"] = _check_suspicious(entry.get("value", ""))
|
|
entry["category"] = category
|
|
if entry["suspicious"]:
|
|
results["all_suspicious"].append(entry)
|
|
category_findings.extend(entries)
|
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
continue
|
|
results["categories"][category] = {
|
|
"total": len(category_findings),
|
|
"suspicious": sum(1 for f in category_findings if f.get("suspicious")),
|
|
"entries": category_findings,
|
|
}
|
|
results["total_suspicious"] = len(results["all_suspicious"])
|
|
return results
|
|
|
|
|
|
def _parse_reg(output, default_key):
|
|
entries = []
|
|
current_key = default_key
|
|
for line in output.strip().split("\n"):
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
if line.startswith("HK"):
|
|
current_key = line
|
|
continue
|
|
parts = re.split(r"\s{2,}", line, maxsplit=2)
|
|
if len(parts) >= 3:
|
|
entries.append({"key": current_key, "name": parts[0], "type": parts[1], "value": parts[2]})
|
|
return entries
|
|
|
|
|
|
def _check_suspicious(value):
|
|
return any(re.search(p, value, re.I) for p in SUSPICIOUS_PATTERNS)
|
|
|
|
|
|
def compare_baseline(baseline_file, current_scan=None):
|
|
"""Compare current registry state against a known-good baseline."""
|
|
with open(baseline_file, "r") as f:
|
|
baseline = json.load(f)
|
|
if current_scan is None:
|
|
current_scan = scan_persistence_keys()
|
|
baseline_set = set()
|
|
for cat_data in baseline.get("categories", {}).values():
|
|
for entry in cat_data.get("entries", []):
|
|
baseline_set.add((entry.get("key", ""), entry.get("name", ""), entry.get("value", "")))
|
|
new_entries = []
|
|
for cat_name, cat_data in current_scan["categories"].items():
|
|
for entry in cat_data.get("entries", []):
|
|
key_tuple = (entry.get("key", ""), entry.get("name", ""), entry.get("value", ""))
|
|
if key_tuple not in baseline_set:
|
|
entry["status"] = "NEW"
|
|
new_entries.append(entry)
|
|
return {
|
|
"baseline_entries": len(baseline_set),
|
|
"new_entries": len(new_entries),
|
|
"findings": new_entries,
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Hunt for registry persistence mechanisms")
|
|
sub = parser.add_subparsers(dest="command")
|
|
s = sub.add_parser("scan", help="Scan registry persistence keys")
|
|
s.add_argument("--categories", nargs="*", choices=list(PERSISTENCE_KEYS.keys()),
|
|
help="Categories to scan (default: all)")
|
|
s.add_argument("--save-baseline", help="Save scan as baseline JSON file")
|
|
c = sub.add_parser("compare", help="Compare against baseline")
|
|
c.add_argument("--baseline", required=True, help="Baseline JSON file")
|
|
args = parser.parse_args()
|
|
if args.command == "scan":
|
|
result = scan_persistence_keys(args.categories)
|
|
if args.save_baseline:
|
|
with open(args.save_baseline, "w") as f:
|
|
json.dump(result, f, indent=2)
|
|
elif args.command == "compare":
|
|
result = compare_baseline(args.baseline)
|
|
else:
|
|
parser.print_help()
|
|
return
|
|
print(json.dumps(result, indent=2, default=str))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|