mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-13 14:44:58 +03:00
328 lines
12 KiB
Python
328 lines
12 KiB
Python
"""
|
|
Velociraptor IR Collection Automation Script
|
|
Manages artifact collection, hunt creation, and result analysis via Velociraptor API.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import csv
|
|
import hashlib
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from collections import defaultdict
|
|
|
|
|
|
class VelociraptorCollector:
|
|
"""Manages Velociraptor artifact collection for incident response."""
|
|
|
|
TRIAGE_ARTIFACTS_WINDOWS = [
|
|
"Windows.EventLogs.EvtxHunter",
|
|
"Windows.Forensics.Prefetch",
|
|
"Windows.Registry.AppCompatCache",
|
|
"Windows.Forensics.Amcache",
|
|
"Windows.Forensics.UserAssist",
|
|
"Windows.System.TaskScheduler",
|
|
"Windows.System.Pslist",
|
|
"Windows.Network.Netstat",
|
|
"Windows.Network.DNSCache",
|
|
"Windows.Forensics.PowerShellHistory",
|
|
"Windows.System.Services",
|
|
"Windows.System.StartupItems",
|
|
"Windows.Persistence.PermanentWMIEvents",
|
|
]
|
|
|
|
TRIAGE_ARTIFACTS_LINUX = [
|
|
"Linux.Sys.AuthLogs",
|
|
"Linux.Forensics.BashHistory",
|
|
"Linux.Sys.Crontab",
|
|
"Linux.Sys.Pslist",
|
|
"Linux.Network.Netstat",
|
|
"Linux.Ssh.AuthorizedKeys",
|
|
"Linux.Services",
|
|
]
|
|
|
|
def __init__(self, server_url=None, api_key=None, output_dir="velociraptor_results"):
|
|
self.server_url = server_url or "https://localhost:8001"
|
|
self.api_key = api_key
|
|
self.output_dir = Path(output_dir)
|
|
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
self.collection_manifest = []
|
|
|
|
def generate_vql_triage_pack(self, target_os="windows", output_file=None):
|
|
"""Generate VQL artifact collection pack for triage."""
|
|
artifacts = (
|
|
self.TRIAGE_ARTIFACTS_WINDOWS
|
|
if target_os == "windows"
|
|
else self.TRIAGE_ARTIFACTS_LINUX
|
|
)
|
|
|
|
vql_queries = []
|
|
for artifact in artifacts:
|
|
vql_queries.append({
|
|
"artifact": artifact,
|
|
"vql": f"SELECT * FROM Artifact.{artifact}()",
|
|
"description": f"Collect {artifact.split('.')[-1]} artifacts",
|
|
})
|
|
|
|
pack = {
|
|
"name": f"IR Triage Pack - {target_os.title()}",
|
|
"version": "1.0",
|
|
"generated": datetime.utcnow().isoformat(),
|
|
"target_os": target_os,
|
|
"artifacts": vql_queries,
|
|
}
|
|
|
|
if output_file is None:
|
|
output_file = self.output_dir / f"triage_pack_{target_os}.json"
|
|
|
|
with open(output_file, "w") as f:
|
|
json.dump(pack, f, indent=2)
|
|
|
|
print(f"[+] Generated {target_os} triage pack: {output_file}")
|
|
print(f" Artifacts: {len(vql_queries)}")
|
|
return pack
|
|
|
|
def generate_hunt_config(self, hunt_name, description, artifacts, parameters=None,
|
|
include_labels=None, exclude_labels=None):
|
|
"""Generate hunt configuration for enterprise-wide collection."""
|
|
hunt_config = {
|
|
"hunt_name": hunt_name,
|
|
"description": description,
|
|
"created": datetime.utcnow().isoformat(),
|
|
"artifacts": artifacts,
|
|
"parameters": parameters or {},
|
|
"targeting": {
|
|
"include_labels": include_labels or [],
|
|
"exclude_labels": exclude_labels or [],
|
|
},
|
|
"resource_limits": {
|
|
"cpu_limit": 50,
|
|
"iops_limit": 100,
|
|
"timeout_seconds": 600,
|
|
"max_rows": 100000,
|
|
"max_upload_bytes": 104857600,
|
|
},
|
|
}
|
|
|
|
config_file = self.output_dir / f"hunt_{hunt_name.replace(' ', '_')}.json"
|
|
with open(config_file, "w") as f:
|
|
json.dump(hunt_config, f, indent=2)
|
|
|
|
print(f"[+] Generated hunt config: {config_file}")
|
|
return hunt_config
|
|
|
|
def generate_ioc_hunt(self, iocs, hunt_name="IOC Hunt"):
|
|
"""Generate VQL-based IOC hunt queries."""
|
|
vql_queries = []
|
|
|
|
if "hashes" in iocs:
|
|
hash_list = "|".join(iocs["hashes"])
|
|
vql_queries.append({
|
|
"name": "Hash Hunt",
|
|
"vql": f"""SELECT * FROM Artifact.Generic.Detection.HashHunter(
|
|
Hashes='{hash_list}'
|
|
)""",
|
|
})
|
|
|
|
if "ips" in iocs:
|
|
ip_regex = "|".join(ip.replace(".", "\\.") for ip in iocs["ips"])
|
|
vql_queries.append({
|
|
"name": "Network Connection Hunt",
|
|
"vql": f"""SELECT * FROM Artifact.Windows.Network.Netstat()
|
|
WHERE RemoteAddr =~ '{ip_regex}'""",
|
|
})
|
|
|
|
if "domains" in iocs:
|
|
domain_regex = "|".join(d.replace(".", "\\.") for d in iocs["domains"])
|
|
vql_queries.append({
|
|
"name": "DNS Cache Hunt",
|
|
"vql": f"""SELECT * FROM Artifact.Windows.Network.DNSCache()
|
|
WHERE Name =~ '{domain_regex}'""",
|
|
})
|
|
|
|
if "filenames" in iocs:
|
|
file_regex = "|".join(iocs["filenames"])
|
|
vql_queries.append({
|
|
"name": "File Hunt",
|
|
"vql": f"""SELECT * FROM Artifact.Windows.NTFS.MFT(
|
|
FileRegex='{file_regex}'
|
|
)""",
|
|
})
|
|
|
|
if "yara_rules" in iocs:
|
|
for rule_name, rule_content in iocs["yara_rules"].items():
|
|
vql_queries.append({
|
|
"name": f"YARA Hunt - {rule_name}",
|
|
"vql": f"""SELECT * FROM Artifact.Windows.Detection.Yara.Process(
|
|
YaraRule='{rule_content}'
|
|
)""",
|
|
})
|
|
|
|
hunt_file = self.output_dir / f"ioc_hunt_{hunt_name.replace(' ', '_')}.json"
|
|
with open(hunt_file, "w") as f:
|
|
json.dump({"name": hunt_name, "queries": vql_queries}, f, indent=2)
|
|
|
|
print(f"[+] Generated IOC hunt with {len(vql_queries)} queries: {hunt_file}")
|
|
return vql_queries
|
|
|
|
def generate_collection_checklist(self, case_id, target_hosts):
|
|
"""Generate collection checklist for IR case."""
|
|
checklist = {
|
|
"case_id": case_id,
|
|
"generated": datetime.utcnow().isoformat(),
|
|
"targets": [],
|
|
}
|
|
|
|
for host in target_hosts:
|
|
target = {
|
|
"hostname": host,
|
|
"collections": [
|
|
{"artifact": "Volatile Data", "status": "pending", "items": [
|
|
"Running processes", "Network connections", "DNS cache",
|
|
"Logged-in users", "Open files",
|
|
]},
|
|
{"artifact": "Event Logs", "status": "pending", "items": [
|
|
"Security.evtx", "System.evtx", "Application.evtx",
|
|
"PowerShell/Operational.evtx", "Sysmon/Operational.evtx",
|
|
]},
|
|
{"artifact": "Execution Evidence", "status": "pending", "items": [
|
|
"Prefetch files", "Amcache.hve", "Shimcache",
|
|
"UserAssist", "BAM/DAM",
|
|
]},
|
|
{"artifact": "Persistence Mechanisms", "status": "pending", "items": [
|
|
"Scheduled tasks", "Services", "Registry Run keys",
|
|
"WMI subscriptions", "Startup items",
|
|
]},
|
|
{"artifact": "File System", "status": "pending", "items": [
|
|
"MFT entries", "USN Journal", "Recycle Bin",
|
|
"Recent files", "Downloads folder",
|
|
]},
|
|
{"artifact": "User Activity", "status": "pending", "items": [
|
|
"Browser history", "PowerShell history", "RDP cache",
|
|
"Recent documents", "Jump lists",
|
|
]},
|
|
],
|
|
}
|
|
checklist["targets"].append(target)
|
|
|
|
checklist_file = self.output_dir / f"collection_checklist_{case_id}.json"
|
|
with open(checklist_file, "w") as f:
|
|
json.dump(checklist, f, indent=2)
|
|
|
|
print(f"[+] Collection checklist for {len(target_hosts)} hosts: {checklist_file}")
|
|
return checklist
|
|
|
|
def analyze_collection_results(self, results_dir):
|
|
"""Analyze collected Velociraptor results for suspicious indicators."""
|
|
results_path = Path(results_dir)
|
|
findings = []
|
|
|
|
for json_file in results_path.glob("**/*.json"):
|
|
try:
|
|
with open(json_file) as f:
|
|
data = json.load(f)
|
|
if isinstance(data, list):
|
|
for item in data:
|
|
finding = self._check_for_indicators(item, str(json_file))
|
|
if finding:
|
|
findings.append(finding)
|
|
except (json.JSONDecodeError, KeyError):
|
|
continue
|
|
|
|
findings_file = self.output_dir / "analysis_findings.json"
|
|
with open(findings_file, "w") as f:
|
|
json.dump(findings, f, indent=2, default=str)
|
|
|
|
print(f"[+] Analysis complete: {len(findings)} findings -> {findings_file}")
|
|
return findings
|
|
|
|
def _check_for_indicators(self, item, source_file):
|
|
"""Check a single result item for suspicious indicators."""
|
|
suspicious_processes = [
|
|
"mimikatz", "rubeus", "lazagne", "sharphound", "bloodhound",
|
|
"cobaltstrike", "beacon", "psexec", "wmiexec", "smbexec",
|
|
]
|
|
suspicious_commands = [
|
|
"invoke-mimikatz", "invoke-expression", "downloadstring",
|
|
"net user /add", "net localgroup administrators",
|
|
"reg save hklm\\sam", "reg save hklm\\system",
|
|
"ntdsutil", "vssadmin create shadow",
|
|
]
|
|
|
|
name = str(item.get("Name", "") or item.get("CommandLine", "")).lower()
|
|
|
|
for proc in suspicious_processes:
|
|
if proc in name:
|
|
return {
|
|
"severity": "CRITICAL",
|
|
"indicator": f"Suspicious process: {proc}",
|
|
"detail": item,
|
|
"source": source_file,
|
|
}
|
|
|
|
for cmd in suspicious_commands:
|
|
if cmd in name:
|
|
return {
|
|
"severity": "HIGH",
|
|
"indicator": f"Suspicious command: {cmd}",
|
|
"detail": item,
|
|
"source": source_file,
|
|
}
|
|
|
|
return None
|
|
|
|
def generate_report(self):
|
|
"""Generate collection summary report."""
|
|
report = {
|
|
"title": "Velociraptor IR Collection Report",
|
|
"generated": datetime.utcnow().isoformat(),
|
|
"collections": self.collection_manifest,
|
|
"output_directory": str(self.output_dir),
|
|
}
|
|
|
|
report_file = self.output_dir / "collection_report.json"
|
|
with open(report_file, "w") as f:
|
|
json.dump(report, f, indent=2)
|
|
|
|
print(f"[+] Collection report: {report_file}")
|
|
return report
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description="Velociraptor IR Collection Manager")
|
|
parser.add_argument("--action", choices=[
|
|
"triage-pack", "hunt-config", "ioc-hunt", "checklist", "analyze",
|
|
], required=True)
|
|
parser.add_argument("--os", default="windows", choices=["windows", "linux"])
|
|
parser.add_argument("--case-id", default="IR-2025-001")
|
|
parser.add_argument("--hosts", nargs="+", help="Target hostnames")
|
|
parser.add_argument("--iocs", help="JSON file with IOCs")
|
|
parser.add_argument("--results-dir", help="Directory with collection results")
|
|
parser.add_argument("-o", "--output", default="velociraptor_results")
|
|
|
|
args = parser.parse_args()
|
|
collector = VelociraptorCollector(output_dir=args.output)
|
|
|
|
if args.action == "triage-pack":
|
|
collector.generate_vql_triage_pack(target_os=args.os)
|
|
elif args.action == "hunt-config":
|
|
collector.generate_hunt_config("IR Hunt", "Incident response hunt",
|
|
collector.TRIAGE_ARTIFACTS_WINDOWS)
|
|
elif args.action == "ioc-hunt":
|
|
if args.iocs:
|
|
with open(args.iocs) as f:
|
|
iocs = json.load(f)
|
|
collector.generate_ioc_hunt(iocs)
|
|
elif args.action == "checklist":
|
|
hosts = args.hosts or ["WKS001", "SRV001", "DC01"]
|
|
collector.generate_collection_checklist(args.case_id, hosts)
|
|
elif args.action == "analyze":
|
|
if args.results_dir:
|
|
collector.analyze_collection_results(args.results_dir)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|