mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-12 14:14:56 +03:00
c47eed6a64
- 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
249 lines
9.3 KiB
Python
249 lines
9.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for performing malware persistence investigation.
|
|
|
|
Enumerates Windows registry Run keys, services, scheduled tasks,
|
|
WMI subscriptions, and Linux cron/systemd persistence mechanisms.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import xml.etree.ElementTree as ET
|
|
from pathlib import Path
|
|
|
|
|
|
SUSPICIOUS_INDICATORS = [
|
|
"powershell", "cmd /c", "wscript", "cscript", "mshta",
|
|
"regsvr32", "rundll32", "certutil", "bitsadmin",
|
|
"downloadstring", "invoke-", "iex", "hidden",
|
|
"bypass", "base64", "-enc", "-e ", ".ps1", ".vbs", ".hta",
|
|
"programdata", "appdata\\local\\temp", "/tmp/",
|
|
]
|
|
|
|
|
|
class PersistenceInvestigator:
|
|
"""Investigates malware persistence mechanisms on forensic images."""
|
|
|
|
def __init__(self, evidence_root, output_dir):
|
|
self.evidence_root = Path(evidence_root)
|
|
self.output_dir = Path(output_dir)
|
|
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
self.findings = []
|
|
|
|
def _is_suspicious(self, value):
|
|
"""Check if a value matches suspicious indicators."""
|
|
value_lower = str(value).lower()
|
|
return any(ind in value_lower for ind in SUSPICIOUS_INDICATORS)
|
|
|
|
def check_registry_run_keys(self):
|
|
"""Check registry Run keys using python-registry."""
|
|
try:
|
|
from Registry import Registry
|
|
except ImportError:
|
|
return [{"error": "Install python-registry: pip install python-registry"}]
|
|
|
|
entries = []
|
|
sw_path = self.evidence_root / "Windows/System32/config/SOFTWARE"
|
|
if not sw_path.exists():
|
|
return entries
|
|
|
|
reg = Registry.Registry(str(sw_path))
|
|
run_paths = [
|
|
"Microsoft\\Windows\\CurrentVersion\\Run",
|
|
"Microsoft\\Windows\\CurrentVersion\\RunOnce",
|
|
"Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run",
|
|
]
|
|
for key_path in run_paths:
|
|
try:
|
|
key = reg.open(key_path)
|
|
for value in key.values():
|
|
entry = {
|
|
"location": f"HKLM\\SOFTWARE\\{key_path}",
|
|
"name": value.name(),
|
|
"value": str(value.value()),
|
|
"timestamp": str(key.timestamp()),
|
|
"suspicious": self._is_suspicious(str(value.value())),
|
|
}
|
|
entries.append(entry)
|
|
if entry["suspicious"]:
|
|
self.findings.append(entry)
|
|
except Exception:
|
|
continue
|
|
return entries
|
|
|
|
def check_services(self):
|
|
"""Check Windows services for suspicious auto-start entries."""
|
|
try:
|
|
from Registry import Registry
|
|
except ImportError:
|
|
return []
|
|
|
|
entries = []
|
|
system_path = self.evidence_root / "Windows/System32/config/SYSTEM"
|
|
if not system_path.exists():
|
|
return entries
|
|
|
|
reg = Registry.Registry(str(system_path))
|
|
try:
|
|
select = reg.open("Select")
|
|
current = select.value("Current").value()
|
|
cs = f"ControlSet{current:03d}"
|
|
services = reg.open(f"{cs}\\Services")
|
|
|
|
for svc in services.subkeys():
|
|
try:
|
|
start = svc.value("Start").value()
|
|
if start not in (0, 1, 2):
|
|
continue
|
|
image_path = ""
|
|
try:
|
|
image_path = svc.value("ImagePath").value()
|
|
except Exception:
|
|
continue
|
|
entry = {
|
|
"service_name": svc.name(),
|
|
"image_path": image_path,
|
|
"start_type": start,
|
|
"timestamp": str(svc.timestamp()),
|
|
"suspicious": self._is_suspicious(image_path),
|
|
}
|
|
entries.append(entry)
|
|
if entry["suspicious"]:
|
|
self.findings.append(entry)
|
|
except Exception:
|
|
continue
|
|
except Exception:
|
|
pass
|
|
return entries
|
|
|
|
def check_scheduled_tasks(self):
|
|
"""Parse Windows scheduled task XML files."""
|
|
tasks_dir = self.evidence_root / "Windows/System32/Tasks"
|
|
if not tasks_dir.exists():
|
|
return []
|
|
|
|
entries = []
|
|
ns = {"t": "http://schemas.microsoft.com/windows/2004/02/mit/task"}
|
|
|
|
for task_file in tasks_dir.rglob("*"):
|
|
if not task_file.is_file():
|
|
continue
|
|
try:
|
|
tree = ET.parse(str(task_file))
|
|
root = tree.getroot()
|
|
for action in root.findall(".//t:Exec", ns):
|
|
cmd = action.find("t:Command", ns)
|
|
args = action.find("t:Arguments", ns)
|
|
cmd_text = cmd.text if cmd is not None else ""
|
|
args_text = args.text if args is not None else ""
|
|
combined = f"{cmd_text} {args_text}"
|
|
entry = {
|
|
"task_name": task_file.name,
|
|
"command": cmd_text,
|
|
"arguments": args_text,
|
|
"suspicious": self._is_suspicious(combined),
|
|
}
|
|
entries.append(entry)
|
|
if entry["suspicious"]:
|
|
self.findings.append(entry)
|
|
except Exception:
|
|
continue
|
|
return entries
|
|
|
|
def check_startup_folder(self):
|
|
"""Check Windows startup folder contents."""
|
|
entries = []
|
|
startup_paths = [
|
|
"ProgramData/Microsoft/Windows/Start Menu/Programs/Startup",
|
|
]
|
|
for user_dir in (self.evidence_root / "Users").iterdir() if (self.evidence_root / "Users").exists() else []:
|
|
if user_dir.is_dir():
|
|
startup_paths.append(
|
|
f"Users/{user_dir.name}/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup"
|
|
)
|
|
|
|
for spath in startup_paths:
|
|
target = self.evidence_root / spath
|
|
if target.exists() and target.is_dir():
|
|
for item in target.iterdir():
|
|
if item.is_file():
|
|
entries.append({
|
|
"location": str(spath),
|
|
"filename": item.name,
|
|
"size": item.stat().st_size,
|
|
"suspicious": self._is_suspicious(item.name),
|
|
})
|
|
return entries
|
|
|
|
def check_linux_persistence(self):
|
|
"""Check Linux persistence mechanisms."""
|
|
entries = []
|
|
checks = {
|
|
"crontab": "etc/crontab",
|
|
"rc_local": "etc/rc.local",
|
|
"ld_preload": "etc/ld.so.preload",
|
|
}
|
|
for name, path in checks.items():
|
|
target = self.evidence_root / path
|
|
if target.exists() and target.is_file():
|
|
content = target.read_text(errors="ignore")
|
|
entries.append({
|
|
"mechanism": name,
|
|
"file": path,
|
|
"content_preview": content[:500],
|
|
"suspicious": self._is_suspicious(content),
|
|
})
|
|
|
|
cron_dirs = ["etc/cron.d", "etc/cron.daily", "var/spool/cron/crontabs"]
|
|
for cron_dir in cron_dirs:
|
|
target = self.evidence_root / cron_dir
|
|
if target.exists() and target.is_dir():
|
|
for f in target.iterdir():
|
|
if f.is_file():
|
|
content = f.read_text(errors="ignore")
|
|
entries.append({
|
|
"mechanism": "cron_job",
|
|
"file": str(f.relative_to(self.evidence_root)),
|
|
"content_preview": content[:300],
|
|
})
|
|
|
|
auth_keys_pattern = "home/*/. ssh/authorized_keys".replace(". ", ".")
|
|
for ak in self.evidence_root.glob("home/*/.ssh/authorized_keys"):
|
|
entries.append({
|
|
"mechanism": "ssh_authorized_keys",
|
|
"file": str(ak.relative_to(self.evidence_root)),
|
|
"content_preview": ak.read_text(errors="ignore")[:300],
|
|
})
|
|
return entries
|
|
|
|
def generate_report(self):
|
|
"""Run all persistence checks and generate report."""
|
|
report = {
|
|
"case_output": str(self.output_dir),
|
|
"evidence_root": str(self.evidence_root),
|
|
"registry_run_keys": self.check_registry_run_keys(),
|
|
"services": self.check_services()[:30],
|
|
"scheduled_tasks": self.check_scheduled_tasks(),
|
|
"startup_folder": self.check_startup_folder(),
|
|
"linux_persistence": self.check_linux_persistence(),
|
|
"suspicious_findings": self.findings,
|
|
"total_suspicious": len(self.findings),
|
|
}
|
|
|
|
report_path = self.output_dir / "persistence_report.json"
|
|
with open(report_path, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
print(json.dumps(report, indent=2))
|
|
return report
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 3:
|
|
print("Usage: agent.py <evidence_root> <output_dir>")
|
|
sys.exit(1)
|
|
investigator = PersistenceInvestigator(sys.argv[1], sys.argv[2])
|
|
investigator.generate_report()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|