mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-13 19:05:17 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,418 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Volatile Evidence Collection Script
|
||||
|
||||
Collects volatile forensic evidence from a live system following
|
||||
RFC 3227 order of volatility. Runs from external media.
|
||||
|
||||
Collects:
|
||||
- Network connections and state
|
||||
- Running processes with command lines
|
||||
- Logged-in users and sessions
|
||||
- System configuration and state
|
||||
- DNS cache, ARP table, routing table
|
||||
- Hashes all evidence files
|
||||
|
||||
Requirements:
|
||||
pip install psutil
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import psutil
|
||||
except ImportError:
|
||||
print("Install psutil: pip install psutil")
|
||||
sys.exit(1)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
)
|
||||
logger = logging.getLogger("volatile_evidence")
|
||||
|
||||
|
||||
class EvidenceCollector:
|
||||
"""Collect volatile evidence from a live system."""
|
||||
|
||||
def __init__(self, output_dir: str, case_id: str):
|
||||
self.case_id = case_id
|
||||
self.hostname = socket.gethostname()
|
||||
self.timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
|
||||
self.output_dir = os.path.join(output_dir, f"{self.hostname}_{self.timestamp}")
|
||||
os.makedirs(self.output_dir, exist_ok=True)
|
||||
self.evidence_manifest = []
|
||||
self.collection_log = []
|
||||
|
||||
# Start collection log
|
||||
self._log_action("Collection initiated", f"Case: {case_id}, Host: {self.hostname}")
|
||||
self._log_action("System time", datetime.now(timezone.utc).isoformat())
|
||||
self._log_action("Platform", f"{platform.system()} {platform.release()}")
|
||||
self._log_action("Collector", os.getenv("USERNAME", os.getenv("USER", "unknown")))
|
||||
|
||||
def _log_action(self, action: str, details: str):
|
||||
entry = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"action": action,
|
||||
"details": details,
|
||||
}
|
||||
self.collection_log.append(entry)
|
||||
logger.info(f"{action}: {details}")
|
||||
|
||||
def _save_evidence(self, filename: str, content: str, category: str):
|
||||
filepath = os.path.join(self.output_dir, filename)
|
||||
with open(filepath, "w", encoding="utf-8", errors="replace") as f:
|
||||
f.write(content)
|
||||
file_hash = self._hash_file(filepath)
|
||||
self.evidence_manifest.append({
|
||||
"filename": filename,
|
||||
"category": category,
|
||||
"sha256": file_hash,
|
||||
"size_bytes": os.path.getsize(filepath),
|
||||
"collected_at": datetime.now(timezone.utc).isoformat(),
|
||||
})
|
||||
self._log_action(f"Evidence collected: {filename}", f"SHA256: {file_hash}")
|
||||
return filepath
|
||||
|
||||
def _save_evidence_csv(self, filename: str, data: list, category: str):
|
||||
if not data:
|
||||
self._log_action(f"No data for {filename}", "Empty result set")
|
||||
return None
|
||||
filepath = os.path.join(self.output_dir, filename)
|
||||
with open(filepath, "w", newline="", encoding="utf-8") as f:
|
||||
writer = csv.DictWriter(f, fieldnames=data[0].keys())
|
||||
writer.writeheader()
|
||||
writer.writerows(data)
|
||||
file_hash = self._hash_file(filepath)
|
||||
self.evidence_manifest.append({
|
||||
"filename": filename,
|
||||
"category": category,
|
||||
"sha256": file_hash,
|
||||
"size_bytes": os.path.getsize(filepath),
|
||||
"collected_at": datetime.now(timezone.utc).isoformat(),
|
||||
})
|
||||
self._log_action(f"Evidence collected: {filename}", f"SHA256: {file_hash}, Rows: {len(data)}")
|
||||
return filepath
|
||||
|
||||
@staticmethod
|
||||
def _hash_file(filepath: str) -> str:
|
||||
sha256 = hashlib.sha256()
|
||||
with open(filepath, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(8192), b""):
|
||||
sha256.update(chunk)
|
||||
return sha256.hexdigest()
|
||||
|
||||
def collect_network_connections(self):
|
||||
"""Collect active network connections with process information."""
|
||||
self._log_action("Collecting", "Network connections")
|
||||
connections = []
|
||||
for conn in psutil.net_connections(kind="all"):
|
||||
try:
|
||||
proc_name = psutil.Process(conn.pid).name() if conn.pid else "N/A"
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
proc_name = "N/A"
|
||||
connections.append({
|
||||
"fd": conn.fd,
|
||||
"family": str(conn.family),
|
||||
"type": str(conn.type),
|
||||
"local_address": f"{conn.laddr.ip}:{conn.laddr.port}" if conn.laddr else "",
|
||||
"remote_address": f"{conn.raddr.ip}:{conn.raddr.port}" if conn.raddr else "",
|
||||
"status": conn.status,
|
||||
"pid": conn.pid or "",
|
||||
"process_name": proc_name,
|
||||
})
|
||||
self._save_evidence_csv("network_connections.csv", connections, "network")
|
||||
|
||||
# Also save in text format
|
||||
text_output = f"Network Connections - {datetime.now(timezone.utc).isoformat()}\n"
|
||||
text_output += f"{'='*80}\n"
|
||||
text_output += f"Total connections: {len(connections)}\n\n"
|
||||
for conn in connections:
|
||||
text_output += f"PID:{conn['pid']} ({conn['process_name']}) "
|
||||
text_output += f"{conn['local_address']} -> {conn['remote_address']} "
|
||||
text_output += f"[{conn['status']}]\n"
|
||||
self._save_evidence("network_connections.txt", text_output, "network")
|
||||
return connections
|
||||
|
||||
def collect_arp_table(self):
|
||||
"""Collect ARP cache."""
|
||||
self._log_action("Collecting", "ARP table")
|
||||
try:
|
||||
if platform.system() == "Windows":
|
||||
result = subprocess.run(["arp", "-a"], capture_output=True, text=True, timeout=10)
|
||||
else:
|
||||
result = subprocess.run(["ip", "neigh"], capture_output=True, text=True, timeout=10)
|
||||
self._save_evidence("arp_cache.txt", result.stdout, "network")
|
||||
except Exception as e:
|
||||
self._log_action("ARP collection failed", str(e))
|
||||
|
||||
def collect_routing_table(self):
|
||||
"""Collect routing table."""
|
||||
self._log_action("Collecting", "Routing table")
|
||||
try:
|
||||
if platform.system() == "Windows":
|
||||
result = subprocess.run(["route", "print"], capture_output=True, text=True, timeout=10)
|
||||
else:
|
||||
result = subprocess.run(["ip", "route", "show"], capture_output=True, text=True, timeout=10)
|
||||
self._save_evidence("routing_table.txt", result.stdout, "network")
|
||||
except Exception as e:
|
||||
self._log_action("Routing table collection failed", str(e))
|
||||
|
||||
def collect_dns_cache(self):
|
||||
"""Collect DNS resolver cache."""
|
||||
self._log_action("Collecting", "DNS cache")
|
||||
try:
|
||||
if platform.system() == "Windows":
|
||||
result = subprocess.run(["ipconfig", "/displaydns"], capture_output=True, text=True, timeout=30)
|
||||
if result.stdout:
|
||||
self._save_evidence("dns_cache.txt", result.stdout, "network")
|
||||
else:
|
||||
# Linux - attempt systemd-resolved
|
||||
result = subprocess.run(
|
||||
["systemd-resolve", "--statistics"],
|
||||
capture_output=True, text=True, timeout=10,
|
||||
)
|
||||
if result.stdout:
|
||||
self._save_evidence("dns_stats.txt", result.stdout, "network")
|
||||
except Exception as e:
|
||||
self._log_action("DNS cache collection failed", str(e))
|
||||
|
||||
def collect_running_processes(self):
|
||||
"""Collect detailed information about all running processes."""
|
||||
self._log_action("Collecting", "Running processes")
|
||||
processes = []
|
||||
for proc in psutil.process_iter(
|
||||
["pid", "ppid", "name", "username", "cmdline", "exe",
|
||||
"create_time", "status", "cpu_percent", "memory_percent",
|
||||
"num_threads", "connections"]
|
||||
):
|
||||
try:
|
||||
info = proc.info
|
||||
cmdline = " ".join(info["cmdline"]) if info["cmdline"] else ""
|
||||
create_time = datetime.fromtimestamp(info["create_time"]).isoformat() if info["create_time"] else ""
|
||||
num_conns = len(info["connections"]) if info["connections"] else 0
|
||||
processes.append({
|
||||
"pid": info["pid"],
|
||||
"ppid": info["ppid"],
|
||||
"name": info["name"],
|
||||
"username": info["username"] or "",
|
||||
"command_line": cmdline[:500],
|
||||
"executable": info["exe"] or "",
|
||||
"create_time": create_time,
|
||||
"status": info["status"],
|
||||
"cpu_percent": info["cpu_percent"],
|
||||
"memory_percent": round(info["memory_percent"] or 0, 2),
|
||||
"threads": info["num_threads"],
|
||||
"network_connections": num_conns,
|
||||
})
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
self._save_evidence_csv("running_processes.csv", processes, "processes")
|
||||
|
||||
# Process tree text format
|
||||
tree_output = f"Process Tree - {datetime.now(timezone.utc).isoformat()}\n{'='*80}\n"
|
||||
tree_output += f"Total processes: {len(processes)}\n\n"
|
||||
for p in sorted(processes, key=lambda x: x["pid"]):
|
||||
tree_output += f"PID:{p['pid']} PPID:{p['ppid']} User:{p['username']} "
|
||||
tree_output += f"{p['name']} [{p['status']}]\n"
|
||||
if p["command_line"]:
|
||||
tree_output += f" CMD: {p['command_line']}\n"
|
||||
self._save_evidence("process_tree.txt", tree_output, "processes")
|
||||
return processes
|
||||
|
||||
def collect_open_files(self):
|
||||
"""Collect open file handles for all processes."""
|
||||
self._log_action("Collecting", "Open file handles")
|
||||
open_files = []
|
||||
for proc in psutil.process_iter(["pid", "name"]):
|
||||
try:
|
||||
for f in proc.open_files():
|
||||
open_files.append({
|
||||
"pid": proc.info["pid"],
|
||||
"process_name": proc.info["name"],
|
||||
"file_path": f.path,
|
||||
"fd": f.fd,
|
||||
"mode": getattr(f, "mode", ""),
|
||||
})
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
if open_files:
|
||||
self._save_evidence_csv("open_files.csv", open_files, "processes")
|
||||
return open_files
|
||||
|
||||
def collect_logged_in_users(self):
|
||||
"""Collect information about currently logged-in users."""
|
||||
self._log_action("Collecting", "Logged-in users")
|
||||
users = []
|
||||
for user in psutil.users():
|
||||
users.append({
|
||||
"username": user.name,
|
||||
"terminal": user.terminal or "",
|
||||
"host": user.host or "",
|
||||
"started": datetime.fromtimestamp(user.started).isoformat(),
|
||||
"pid": getattr(user, "pid", ""),
|
||||
})
|
||||
self._save_evidence_csv("logged_in_users.csv", users, "users")
|
||||
|
||||
# Text format
|
||||
text = f"Logged-in Users - {datetime.now(timezone.utc).isoformat()}\n{'='*80}\n"
|
||||
for u in users:
|
||||
text += f"User: {u['username']} | Terminal: {u['terminal']} | "
|
||||
text += f"Host: {u['host']} | Since: {u['started']}\n"
|
||||
self._save_evidence("logged_in_users.txt", text, "users")
|
||||
return users
|
||||
|
||||
def collect_system_info(self):
|
||||
"""Collect system configuration and state."""
|
||||
self._log_action("Collecting", "System information")
|
||||
boot_time = datetime.fromtimestamp(psutil.boot_time())
|
||||
info = {
|
||||
"hostname": self.hostname,
|
||||
"platform": platform.platform(),
|
||||
"architecture": platform.machine(),
|
||||
"processor": platform.processor(),
|
||||
"python_version": platform.python_version(),
|
||||
"boot_time": boot_time.isoformat(),
|
||||
"uptime_seconds": (datetime.now() - boot_time).total_seconds(),
|
||||
"cpu_count_physical": psutil.cpu_count(logical=False),
|
||||
"cpu_count_logical": psutil.cpu_count(logical=True),
|
||||
"memory_total_gb": round(psutil.virtual_memory().total / (1024**3), 2),
|
||||
"memory_used_percent": psutil.virtual_memory().percent,
|
||||
"disk_partitions": [
|
||||
{"device": p.device, "mountpoint": p.mountpoint, "fstype": p.fstype}
|
||||
for p in psutil.disk_partitions()
|
||||
],
|
||||
"network_interfaces": {
|
||||
name: [{"address": addr.address, "family": str(addr.family)}
|
||||
for addr in addrs]
|
||||
for name, addrs in psutil.net_if_addrs().items()
|
||||
},
|
||||
}
|
||||
self._save_evidence(
|
||||
"system_info.json",
|
||||
json.dumps(info, indent=2),
|
||||
"system",
|
||||
)
|
||||
return info
|
||||
|
||||
def collect_services(self):
|
||||
"""Collect running services (platform-specific)."""
|
||||
self._log_action("Collecting", "Running services")
|
||||
try:
|
||||
if platform.system() == "Windows":
|
||||
result = subprocess.run(
|
||||
["sc", "queryex", "type=", "service", "state=", "all"],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
self._save_evidence("services_all.txt", result.stdout, "system")
|
||||
else:
|
||||
result = subprocess.run(
|
||||
["systemctl", "list-units", "--type=service", "--all", "--no-pager"],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
self._save_evidence("systemd_services.txt", result.stdout, "system")
|
||||
except Exception as e:
|
||||
self._log_action("Service collection failed", str(e))
|
||||
|
||||
def collect_scheduled_tasks(self):
|
||||
"""Collect scheduled tasks / cron jobs."""
|
||||
self._log_action("Collecting", "Scheduled tasks")
|
||||
try:
|
||||
if platform.system() == "Windows":
|
||||
result = subprocess.run(
|
||||
["schtasks", "/query", "/fo", "CSV", "/v"],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
self._save_evidence("scheduled_tasks.csv", result.stdout, "system")
|
||||
else:
|
||||
result = subprocess.run(
|
||||
["crontab", "-l"],
|
||||
capture_output=True, text=True, timeout=10,
|
||||
)
|
||||
self._save_evidence("crontab.txt", result.stdout or "No crontab", "system")
|
||||
except Exception as e:
|
||||
self._log_action("Scheduled task collection failed", str(e))
|
||||
|
||||
def collect_environment_variables(self):
|
||||
"""Collect environment variables."""
|
||||
self._log_action("Collecting", "Environment variables")
|
||||
env_data = "\n".join(f"{k}={v}" for k, v in sorted(os.environ.items()))
|
||||
self._save_evidence("environment_variables.txt", env_data, "system")
|
||||
|
||||
def finalize(self):
|
||||
"""Generate evidence manifest and collection log."""
|
||||
# Save collection log
|
||||
log_path = os.path.join(self.output_dir, "collection_log.json")
|
||||
with open(log_path, "w") as f:
|
||||
json.dump(self.collection_log, f, indent=2)
|
||||
|
||||
# Save evidence manifest
|
||||
manifest_path = os.path.join(self.output_dir, "evidence_manifest.json")
|
||||
manifest = {
|
||||
"case_id": self.case_id,
|
||||
"hostname": self.hostname,
|
||||
"collection_start": self.collection_log[0]["timestamp"] if self.collection_log else "",
|
||||
"collection_end": datetime.now(timezone.utc).isoformat(),
|
||||
"total_evidence_items": len(self.evidence_manifest),
|
||||
"evidence": self.evidence_manifest,
|
||||
}
|
||||
with open(manifest_path, "w") as f:
|
||||
json.dump(manifest, f, indent=2)
|
||||
|
||||
# Generate SHA256 manifest text file
|
||||
hash_manifest = os.path.join(self.output_dir, "sha256_manifest.txt")
|
||||
with open(hash_manifest, "w") as f:
|
||||
for item in self.evidence_manifest:
|
||||
f.write(f"{item['sha256']} {item['filename']}\n")
|
||||
|
||||
logger.info(f"Collection complete. Evidence directory: {self.output_dir}")
|
||||
logger.info(f"Total evidence items: {len(self.evidence_manifest)}")
|
||||
return self.output_dir
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Volatile Evidence Collection Tool")
|
||||
parser.add_argument("--case-id", required=True, help="Case/incident ID")
|
||||
parser.add_argument("--output-dir", default="./evidence_collection", help="Output directory")
|
||||
parser.add_argument("--skip-memory", action="store_true", help="Skip memory dump (use dedicated tool)")
|
||||
parser.add_argument("--collect-all", action="store_true", help="Collect all available evidence types")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
collector = EvidenceCollector(args.output_dir, args.case_id)
|
||||
|
||||
if not args.skip_memory:
|
||||
logger.info("NOTE: For memory acquisition, use dedicated tools (WinPmem/LiME) from forensic USB")
|
||||
|
||||
# Collect in order of volatility
|
||||
collector.collect_network_connections()
|
||||
collector.collect_arp_table()
|
||||
collector.collect_dns_cache()
|
||||
collector.collect_routing_table()
|
||||
collector.collect_running_processes()
|
||||
collector.collect_open_files()
|
||||
collector.collect_logged_in_users()
|
||||
collector.collect_system_info()
|
||||
collector.collect_services()
|
||||
collector.collect_scheduled_tasks()
|
||||
collector.collect_environment_variables()
|
||||
|
||||
evidence_dir = collector.finalize()
|
||||
print(f"\nEvidence collection complete")
|
||||
print(f"Output directory: {evidence_dir}")
|
||||
print(f"Total items: {len(collector.evidence_manifest)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user