#!/usr/bin/env python3 """Cuckoo Sandbox behavioral analysis agent for automated malware detonation and reporting.""" import json import os import sys import hashlib try: import requests HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False CUCKOO_API = os.environ.get("CUCKOO_API", "http://localhost:8090") CUCKOO_STORAGE = os.environ.get("CUCKOO_STORAGE", "/opt/cuckoo/storage/analyses") def submit_file(filepath, timeout=300, machine=None, package=None): """Submit a malware sample to Cuckoo via REST API.""" if not HAS_REQUESTS: return None url = f"{CUCKOO_API}/tasks/create/file" files = {"file": (os.path.basename(filepath), open(filepath, "rb"))} data = {"timeout": timeout} if machine: data["machine"] = machine if package: data["package"] = package resp = requests.post(url, files=files, data=data, timeout=30) if resp.status_code == 200: return resp.json().get("task_id") return None def submit_url(url_to_analyze, timeout=300): """Submit a URL to Cuckoo for analysis.""" if not HAS_REQUESTS: return None url = f"{CUCKOO_API}/tasks/create/url" data = {"url": url_to_analyze, "timeout": timeout} resp = requests.post(url, data=data, timeout=30) if resp.status_code == 200: return resp.json().get("task_id") return None def get_task_status(task_id): """Check the status of a Cuckoo analysis task.""" if not HAS_REQUESTS: return None url = f"{CUCKOO_API}/tasks/view/{task_id}" resp = requests.get(url, timeout=30) if resp.status_code == 200: return resp.json().get("task", {}).get("status") return None def load_report(task_id, report_dir=None): """Load a Cuckoo JSON report from disk.""" if report_dir is None: report_dir = CUCKOO_STORAGE report_path = os.path.join(report_dir, str(task_id), "reports", "report.json") if os.path.exists(report_path): with open(report_path, "r") as f: return json.load(f) return None def analyze_processes(report): """Extract and analyze the process tree from the Cuckoo report.""" processes = [] for proc in report.get("behavior", {}).get("processes", []): pid = proc.get("pid") ppid = proc.get("ppid") name = proc.get("process_name") suspicious_apis = [] dangerous_apis = [ "CreateRemoteThread", "VirtualAllocEx", "WriteProcessMemory", "NtCreateThreadEx", "RegSetValueExA", "URLDownloadToFileA", "ShellExecuteA", "ShellExecuteW", "WinExec", "CreateProcessA", "NtWriteVirtualMemory", "QueueUserAPC", ] for call in proc.get("calls", []): if call.get("api") in dangerous_apis: args = {arg["name"]: arg["value"] for arg in call.get("arguments", [])} suspicious_apis.append({"api": call["api"], "args": args}) processes.append({ "pid": pid, "ppid": ppid, "name": name, "suspicious_api_calls": len(suspicious_apis), "top_suspicious": suspicious_apis[:10], }) return processes def analyze_network(report): """Extract network activity from the Cuckoo report.""" network = report.get("network", {}) return { "dns": [ {"request": d.get("request"), "answers": d.get("answers", [])} for d in network.get("dns", []) ], "http": [ {"method": h.get("method"), "host": h.get("host"), "uri": h.get("uri"), "body_size": len(h.get("body", ""))} for h in network.get("http", []) ], "tcp_connections": [ {"src": t.get("src"), "sport": t.get("sport"), "dst": t.get("dst"), "dport": t.get("dport")} for t in network.get("tcp", []) ], "udp_connections": [ {"src": u.get("src"), "sport": u.get("sport"), "dst": u.get("dst"), "dport": u.get("dport")} for u in network.get("udp", []) ], } def analyze_dropped_files(report): """Extract dropped file information from the report.""" dropped = [] for d in report.get("dropped", []): dropped.append({ "filepath": d.get("filepath", ""), "sha256": d.get("sha256", ""), "size": d.get("size", 0), "type": d.get("type", ""), }) return dropped def analyze_signatures(report): """Extract triggered behavioral signatures.""" signatures = [] for sig in report.get("signatures", []): marks = [] for mark in sig.get("marks", []): if mark.get("ioc"): marks.append(mark["ioc"]) elif mark.get("call"): marks.append(mark["call"].get("api", "")) signatures.append({ "name": sig.get("name"), "severity": sig.get("severity"), "description": sig.get("description"), "marks": marks[:5], }) return sorted(signatures, key=lambda x: x.get("severity", 0), reverse=True) def analyze_registry(report): """Extract registry modifications from behavior summary.""" summary = report.get("behavior", {}).get("summary", {}) return { "keys_modified": summary.get("keys", [])[:20], "files_created": summary.get("files", [])[:20], "mutexes": summary.get("mutexes", [])[:10], } def generate_summary(report, processes, network, dropped, signatures, registry): """Generate a consolidated analysis summary.""" info = report.get("info", {}) score = info.get("score", 0) return { "task_id": info.get("id"), "sample": info.get("category", "file"), "analysis_time": info.get("duration", 0), "machine": info.get("machine", {}).get("name", ""), "threat_score": score, "process_count": len(processes), "suspicious_api_total": sum(p["suspicious_api_calls"] for p in processes), "dns_queries": len(network["dns"]), "http_requests": len(network["http"]), "tcp_connections": len(network["tcp_connections"]), "dropped_files": len(dropped), "signatures_triggered": len(signatures), "high_severity_sigs": len([s for s in signatures if s["severity"] >= 3]), "registry_keys_modified": len(registry["keys_modified"]), "files_created": len(registry["files_created"]), } if __name__ == "__main__": print("=" * 60) print("Cuckoo Sandbox Behavioral Analysis Agent") print("Automated malware detonation and report parsing") print("=" * 60) if len(sys.argv) > 1: arg = sys.argv[1] # Check if argument is a report JSON path if arg.endswith(".json") and os.path.exists(arg): print(f"\n[*] Loading report: {arg}") with open(arg, "r") as f: report = json.load(f) elif arg.isdigit(): print(f"\n[*] Loading report for task ID: {arg}") report = load_report(int(arg)) elif os.path.exists(arg): print(f"\n[*] Submitting sample: {arg}") sha256 = hashlib.sha256(open(arg, "rb").read()).hexdigest() print(f"[*] SHA-256: {sha256}") task_id = submit_file(arg) if task_id: print(f"[*] Task submitted: ID={task_id}") print(f"[*] Monitor at: {CUCKOO_API.replace('8090', '8080')}/analysis/{task_id}/") else: print("[ERROR] Failed to submit. Check Cuckoo API connection.") sys.exit(0) else: report = None if report: processes = analyze_processes(report) network = analyze_network(report) dropped = analyze_dropped_files(report) signatures = analyze_signatures(report) registry = analyze_registry(report) summary = generate_summary(report, processes, network, dropped, signatures, registry) print(f"\n--- Analysis Summary ---") print(f" Score: {summary['threat_score']}/10") print(f" Processes: {summary['process_count']}") print(f" Suspicious APIs: {summary['suspicious_api_total']}") print(f" Signatures: {summary['signatures_triggered']} " f"({summary['high_severity_sigs']} high severity)") print(f"\n--- Network ---") print(f" DNS: {summary['dns_queries']}, HTTP: {summary['http_requests']}, " f"TCP: {summary['tcp_connections']}") for http in network["http"][:5]: print(f" {http['method']} {http['host']}{http['uri']}") print(f"\n--- Dropped Files ---") for d in dropped[:5]: print(f" {d['filepath']} ({d['size']} bytes)") print(f"\n--- Top Signatures ---") for s in signatures[:5]: print(f" [{s['severity']}/5] {s['name']}: {s['description']}") else: print(f"\n[DEMO] Usage:") print(f" python agent.py # Submit to Cuckoo") print(f" python agent.py # Parse existing report") print(f" python agent.py # Parse JSON report file")