mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-26 11:44:37 +03:00
27c6414ca5
Complete skill folder anatomy across all cybersecurity skills: - scripts/agent.py: 80-150 line Python agents using real libraries (impacket, boto3, azure-mgmt-*, kubernetes, pefile, yara, scapy, shodan, stix2, etc.) - references/api-reference.md: real API documentation with method signatures - LICENSE: MIT license for all skill folders
186 lines
6.7 KiB
Python
186 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for hunting shadow copy deletion activity indicating ransomware or anti-forensics."""
|
|
|
|
import json
|
|
import argparse
|
|
import re
|
|
import xml.etree.ElementTree as ET
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
try:
|
|
import Evtx.Evtx as evtx
|
|
except ImportError:
|
|
evtx = None
|
|
|
|
|
|
SHADOW_PATTERNS = [
|
|
r"vssadmin\s+delete\s+shadows",
|
|
r"vssadmin\.exe.*delete.*shadows",
|
|
r"wmic\s+shadowcopy\s+delete",
|
|
r"Get-WmiObject\s+Win32_ShadowCopy.*Delete",
|
|
r"gwmi\s+Win32_ShadowCopy.*Remove",
|
|
r"bcdedit.*recoveryenabled.*no",
|
|
r"bcdedit.*/set.*bootstatuspolicy\s+ignoreallfailures",
|
|
r"wbadmin\s+delete\s+catalog",
|
|
r"Win32_ShadowCopy.*\.Delete",
|
|
r"powershell.*shadowcopy.*delete",
|
|
]
|
|
|
|
RECOVERY_DISABLE_PATTERNS = [
|
|
r"bcdedit\s+/set\s+\{default\}\s+recoveryenabled\s+no",
|
|
r"reagentc\s+/disable",
|
|
r"vssadmin\s+resize\s+shadowstorage.*maxsize=",
|
|
]
|
|
|
|
|
|
def parse_evtx_file(evtx_path):
|
|
"""Parse Windows EVTX file for shadow copy deletion events."""
|
|
if evtx is None:
|
|
return []
|
|
events = []
|
|
with evtx.Evtx(evtx_path) as log:
|
|
for record in log.records():
|
|
xml_str = record.xml()
|
|
try:
|
|
root = ET.fromstring(xml_str)
|
|
ns = {"ns": "http://schemas.microsoft.com/win/2004/08/events/event"}
|
|
event_id_el = root.find(".//ns:EventID", ns)
|
|
if event_id_el is None:
|
|
continue
|
|
event_id = int(event_id_el.text)
|
|
if event_id in (1, 4688, 4698):
|
|
data_elements = root.findall(".//ns:Data", ns)
|
|
event_data = {}
|
|
for d in data_elements:
|
|
name = d.get("Name", "")
|
|
event_data[name] = d.text or ""
|
|
events.append({
|
|
"event_id": event_id,
|
|
"timestamp": record.timestamp().isoformat(),
|
|
"data": event_data,
|
|
})
|
|
except ET.ParseError:
|
|
continue
|
|
return events
|
|
|
|
|
|
def scan_command_line(cmd_line):
|
|
"""Check a command line string against shadow copy deletion patterns."""
|
|
findings = []
|
|
for pattern in SHADOW_PATTERNS:
|
|
if re.search(pattern, cmd_line, re.IGNORECASE):
|
|
findings.append({"pattern": pattern, "severity": "CRITICAL", "category": "shadow_copy_deletion"})
|
|
for pattern in RECOVERY_DISABLE_PATTERNS:
|
|
if re.search(pattern, cmd_line, re.IGNORECASE):
|
|
findings.append({"pattern": pattern, "severity": "HIGH", "category": "recovery_disable"})
|
|
return findings
|
|
|
|
|
|
def hunt_evtx(evtx_path):
|
|
"""Hunt for shadow copy deletion in EVTX logs."""
|
|
events = parse_evtx_file(evtx_path)
|
|
results = []
|
|
for event in events:
|
|
cmd = event["data"].get("CommandLine", "") or event["data"].get("TaskContent", "")
|
|
if not cmd:
|
|
cmd = " ".join(event["data"].values())
|
|
matches = scan_command_line(cmd)
|
|
if matches:
|
|
results.append({
|
|
"timestamp": event["timestamp"],
|
|
"event_id": event["event_id"],
|
|
"command_line": cmd[:500],
|
|
"user": event["data"].get("SubjectUserName", event["data"].get("User", "")),
|
|
"computer": event["data"].get("Computer", ""),
|
|
"findings": matches,
|
|
})
|
|
return results
|
|
|
|
|
|
def scan_sysmon_json(log_path):
|
|
"""Scan JSON-exported Sysmon logs for shadow copy deletion."""
|
|
results = []
|
|
with open(log_path) as f:
|
|
for line in f:
|
|
try:
|
|
entry = json.loads(line)
|
|
except json.JSONDecodeError:
|
|
continue
|
|
cmd = entry.get("CommandLine", entry.get("command_line", ""))
|
|
image = entry.get("Image", entry.get("process_name", ""))
|
|
matches = scan_command_line(cmd)
|
|
if matches:
|
|
results.append({
|
|
"timestamp": entry.get("UtcTime", entry.get("timestamp", "")),
|
|
"image": image,
|
|
"command_line": cmd[:500],
|
|
"parent_image": entry.get("ParentImage", ""),
|
|
"user": entry.get("User", ""),
|
|
"hostname": entry.get("Computer", entry.get("hostname", "")),
|
|
"findings": matches,
|
|
})
|
|
return results
|
|
|
|
|
|
def generate_sigma_rule():
|
|
"""Generate a Sigma detection rule for shadow copy deletion."""
|
|
return {
|
|
"title": "Shadow Copy Deletion via Vssadmin or WMIC",
|
|
"id": "faa6e1e2-5b4c-4e1a-bb2a-5c1f3e5e3f0a",
|
|
"status": "production",
|
|
"level": "critical",
|
|
"logsource": {"category": "process_creation", "product": "windows"},
|
|
"detection": {
|
|
"selection_vssadmin": {
|
|
"Image|endswith": "\\vssadmin.exe",
|
|
"CommandLine|contains|all": ["delete", "shadows"],
|
|
},
|
|
"selection_wmic": {
|
|
"Image|endswith": "\\wmic.exe",
|
|
"CommandLine|contains|all": ["shadowcopy", "delete"],
|
|
},
|
|
"selection_powershell": {
|
|
"Image|endswith": ["\\powershell.exe", "\\pwsh.exe"],
|
|
"CommandLine|contains": "Win32_ShadowCopy",
|
|
},
|
|
"condition": "selection_vssadmin or selection_wmic or selection_powershell",
|
|
},
|
|
"tags": ["attack.impact", "attack.t1490"],
|
|
}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Shadow Copy Deletion Hunter")
|
|
parser.add_argument("--evtx", help="Path to EVTX log file")
|
|
parser.add_argument("--json-log", help="Path to JSON Sysmon log")
|
|
parser.add_argument("--output", default="shadow_copy_hunt_report.json")
|
|
parser.add_argument("--action", choices=["hunt_evtx", "hunt_json", "sigma", "full"],
|
|
default="full")
|
|
args = parser.parse_args()
|
|
|
|
report = {"generated_at": datetime.utcnow().isoformat(), "findings": {}}
|
|
|
|
if args.action in ("hunt_evtx", "full") and args.evtx:
|
|
results = hunt_evtx(args.evtx)
|
|
report["findings"]["evtx_hits"] = results
|
|
print(f"[+] EVTX shadow copy deletion events: {len(results)}")
|
|
|
|
if args.action in ("hunt_json", "full") and args.json_log:
|
|
results = scan_sysmon_json(args.json_log)
|
|
report["findings"]["sysmon_hits"] = results
|
|
print(f"[+] Sysmon JSON shadow copy hits: {len(results)}")
|
|
|
|
if args.action in ("sigma", "full"):
|
|
rule = generate_sigma_rule()
|
|
report["findings"]["sigma_rule"] = rule
|
|
print("[+] Sigma rule generated")
|
|
|
|
with open(args.output, "w") as f:
|
|
json.dump(report, f, indent=2, default=str)
|
|
print(f"[+] Report saved to {args.output}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|