malware analysis

Performing Automated Malware Analysis with CAPE

Deploy and operate CAPEv2 sandbox for automated malware analysis with behavioral monitoring, payload extraction, configuration parsing, and anti-evasion capabilities.

automated-analysisbehavioral-analysiscapecuckoomalware-analysispayload-extractionsandbox
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

CAPE (Config And Payload Extraction) is an open-source malware sandbox derived from Cuckoo that automates behavioral analysis, payload dumping, and configuration extraction. CAPEv2 features API hooking for behavioral instrumentation, captures files created/modified/deleted during execution, records network traffic in PCAP format, and includes 70+ custom configuration extractors (cape-parsers) for families like Emotet, TrickBot, Cobalt Strike, AsyncRAT, and Rhadamanthys. The signature system includes 1000+ behavioral signatures detecting evasion techniques, persistence, credential theft, and ransomware behavior. CAPE's debugger enables dynamic anti-evasion bypasses combining debugger actions within YARA signatures. Recommended deployment: Ubuntu LTS host with Windows 10 21H2 guest VM.

When to Use

  • When conducting security assessments that involve performing automated malware analysis with cape
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Ubuntu 22.04 LTS server (8+ CPU cores, 32GB+ RAM, 500GB+ SSD)
  • KVM/QEMU virtualization support
  • Windows 10 21H2 guest image
  • Python 3.9+ with CAPEv2 dependencies
  • Network configuration for isolated analysis network

Workflow

Step 1: Submit and Analyze Samples via API

#!/usr/bin/env python3
"""CAPE sandbox API client for automated malware submission and analysis."""
import requests
import json
import time
import sys
from pathlib import Path
 
 
class CAPEClient:
    def __init__(self, base_url="http://localhost:8000", api_token=None):
        self.base_url = base_url.rstrip("/")
        self.headers = {}
        if api_token:
            self.headers["Authorization"] = f"Token {api_token}"
 
    def submit_file(self, filepath, options=None):
        """Submit a file for analysis."""
        url = f"{self.base_url}/apiv2/tasks/create/file/"
        files = {"file": open(filepath, "rb")}
        data = options or {}
        data.setdefault("timeout", 120)
        data.setdefault("enforce_timeout", False)
 
        resp = requests.post(url, files=files, data=data, headers=self.headers)
        resp.raise_for_status()
        result = resp.json()
        task_id = result.get("data", {}).get("task_ids", [None])[0]
        print(f"[+] Submitted {filepath} -> Task ID: {task_id}")
        return task_id
 
    def get_status(self, task_id):
        """Check task analysis status."""
        url = f"{self.base_url}/apiv2/tasks/status/{task_id}/"
        resp = requests.get(url, headers=self.headers)
        return resp.json().get("data", "unknown")
 
    def wait_for_completion(self, task_id, poll_interval=15, max_wait=600):
        """Wait for analysis to complete."""
        elapsed = 0
        while elapsed < max_wait:
            status = self.get_status(task_id)
            if status == "reported":
                print(f"[+] Task {task_id} completed")
                return True
            time.sleep(poll_interval)
            elapsed += poll_interval
            print(f"  Waiting... ({elapsed}s, status: {status})")
        return False
 
    def get_report(self, task_id):
        """Retrieve full analysis report."""
        url = f"{self.base_url}/apiv2/tasks/get/report/{task_id}/"
        resp = requests.get(url, headers=self.headers)
        return resp.json()
 
    def get_config(self, task_id):
        """Get extracted malware configuration."""
        report = self.get_report(task_id)
        configs = report.get("CAPE", {}).get("configs", [])
        return configs
 
    def get_dropped_files(self, task_id):
        """List files dropped during analysis."""
        report = self.get_report(task_id)
        return report.get("dropped", [])
 
    def get_network_iocs(self, task_id):
        """Extract network IOCs from analysis."""
        report = self.get_report(task_id)
        network = report.get("network", {})
        iocs = {
            "dns": [d.get("request") for d in network.get("dns", [])],
            "http": [h.get("uri") for h in network.get("http", [])],
            "tcp": [f"{h.get('dst')}:{h.get('dport')}"
                    for h in network.get("tcp", [])],
        }
        return iocs
 
    def analyze_sample(self, filepath):
        """Full automated analysis pipeline."""
        task_id = self.submit_file(filepath)
        if not task_id:
            return None
 
        if self.wait_for_completion(task_id):
            report = {
                "task_id": task_id,
                "config": self.get_config(task_id),
                "network_iocs": self.get_network_iocs(task_id),
                "dropped_files": len(self.get_dropped_files(task_id)),
            }
            return report
        return None
 
 
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <malware_sample> [cape_url]")
        sys.exit(1)
 
    url = sys.argv[2] if len(sys.argv) > 2 else "http://localhost:8000"
    client = CAPEClient(url)
    result = client.analyze_sample(sys.argv[1])
    if result:
        print(json.dumps(result, indent=2))

Validation Criteria

  • Samples submitted and analyzed within configured timeout
  • Behavioral signatures triggered for known malware families
  • Malware configurations extracted by cape-parsers
  • Network traffic captured and IOCs extracted
  • Dropped files and payloads collected for further analysis
  • Anti-evasion bypasses effective against sandbox-aware malware

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md5.7 KB

API Reference: CAPE Sandbox Automated Malware Analysis

Libraries Used

Library Purpose
requests HTTP client for CAPE REST API v2
json Parse analysis reports and task metadata
os Read CAPE_URL and CAPE_API_KEY environment variables
time Poll task status until analysis completes

Installation

pip install requests

Authentication

import requests
import os
 
CAPE_URL = os.environ.get("CAPE_URL", "http://cape.example.com:8000")
CAPE_KEY = os.environ.get("CAPE_API_KEY", "")
headers = {"Authorization": f"Token {CAPE_KEY}"} if CAPE_KEY else {}

REST API v2 Endpoints

Method Endpoint Description
POST /apiv2/tasks/create/file/ Submit a file for analysis
POST /apiv2/tasks/create/url/ Submit a URL for analysis
GET /apiv2/tasks/list/ List all analysis tasks
GET /apiv2/tasks/view/{id}/ Get task status and metadata
GET /apiv2/tasks/report/{id}/ Get full analysis report
GET /apiv2/tasks/report/{id}/lite/ Get lightweight report
DELETE /apiv2/tasks/delete/{id}/ Delete a task and its data
GET /apiv2/tasks/screenshots/{id}/ Get analysis screenshots
GET /apiv2/tasks/procmemory/{id}/ Get process memory dumps
GET /apiv2/files/view/sha256/{hash}/ Look up file by SHA-256
GET /apiv2/files/get/{sha256}/ Download the sample binary
GET /apiv2/pcap/get/{id}/ Download PCAP network capture
GET /apiv2/machines/list/ List analysis VMs
GET /apiv2/cuckoo/status/ Server status and version

Core Operations

Submit a File for Analysis

def submit_file(file_path, timeout_mins=5, machine=None):
    files = {"file": open(file_path, "rb")}
    data = {
        "timeout": timeout_mins * 60,
        "enforce_timeout": True,
        "options": "procmemdump=yes,import_reconstruction=yes",
    }
    if machine:
        data["machine"] = machine
 
    resp = requests.post(
        f"{CAPE_URL}/apiv2/tasks/create/file/",
        files=files,
        data=data,
        headers=headers,
        timeout=60,
    )
    resp.raise_for_status()
    result = resp.json()
    return result["data"]["task_ids"][0]

Submit a URL for Analysis

def submit_url(url, timeout_mins=3):
    resp = requests.post(
        f"{CAPE_URL}/apiv2/tasks/create/url/",
        data={
            "url": url,
            "timeout": timeout_mins * 60,
            "options": "procmemdump=yes",
        },
        headers=headers,
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()["data"]["task_ids"][0]

Poll Task Until Complete

import time
 
def wait_for_task(task_id, poll_interval=30, max_wait=600):
    elapsed = 0
    while elapsed < max_wait:
        resp = requests.get(
            f"{CAPE_URL}/apiv2/tasks/view/{task_id}/",
            headers=headers,
            timeout=30,
        )
        status = resp.json()["data"]["status"]
        if status == "reported":
            return True
        if status in ("failed_analysis", "failed_processing"):
            raise RuntimeError(f"Task {task_id} failed: {status}")
        time.sleep(poll_interval)
        elapsed += poll_interval
    raise TimeoutError(f"Task {task_id} did not complete within {max_wait}s")

Retrieve Analysis Report

def get_report(task_id, lite=False):
    endpoint = "lite" if lite else ""
    resp = requests.get(
        f"{CAPE_URL}/apiv2/tasks/report/{task_id}/{endpoint}",
        headers=headers,
        timeout=120,
    )
    resp.raise_for_status()
    return resp.json()

Extract Key Findings from Report

def extract_findings(report):
    info = report.get("info", {})
    findings = {
        "score": info.get("score", 0),
        "duration": info.get("duration", 0),
        "signatures": [],
        "network_iocs": {"domains": [], "ips": [], "urls": []},
        "dropped_files": [],
        "yara_matches": [],
    }
 
    # Behavioral signatures
    for sig in report.get("signatures", []):
        findings["signatures"].append({
            "name": sig["name"],
            "severity": sig["severity"],
            "description": sig["description"],
        })
 
    # Network IOCs
    network = report.get("network", {})
    findings["network_iocs"]["domains"] = [
        d["domain"] for d in network.get("domains", [])
    ]
    findings["network_iocs"]["ips"] = [
        h["ip"] for h in network.get("hosts", [])
    ]
 
    # YARA matches
    for target_yara in report.get("target", {}).get("file", {}).get("yara", []):
        findings["yara_matches"].append(target_yara["name"])
 
    return findings

Download Network PCAP

def download_pcap(task_id, output_path):
    resp = requests.get(
        f"{CAPE_URL}/apiv2/pcap/get/{task_id}/",
        headers=headers,
        timeout=60,
    )
    resp.raise_for_status()
    with open(output_path, "wb") as f:
        f.write(resp.content)

Output Format

{
  "info": {
    "id": 42,
    "score": 8.5,
    "duration": 120,
    "machine": {"name": "win10-01", "label": "win10-01"},
    "started": "2025-01-15T10:30:00",
    "ended": "2025-01-15T10:32:00"
  },
  "signatures": [
    {"name": "ransomware_bcdedit", "severity": 5, "description": "Modifies boot configuration"},
    {"name": "creates_exe", "severity": 3, "description": "Creates executable files on disk"}
  ],
  "network": {
    "hosts": [{"ip": "198.51.100.42", "country": "US"}],
    "domains": [{"domain": "c2.evil.example.com", "ip": "198.51.100.42"}]
  },
  "target": {
    "file": {
      "name": "sample.exe",
      "size": 245760,
      "sha256": "a1b2c3d4e5f6..."
    }
  }
}
standards.md0.3 KB

Standards Reference - performing-automated-malware-analysis-with-cape

Applicable Standards

  • MITRE ATT&CK Framework
  • NIST SP 800-83 Guide to Malware Incident Prevention
  • NIST SP 800-86 Guide to Integrating Forensic Techniques

Related MITRE ATT&CK Techniques

See SKILL.md for specific technique mappings.

workflows.md0.5 KB

Analysis Workflows - performing-automated-malware-analysis-with-cape

Primary Workflow

[Sample Collection] --> [Static Analysis] --> [Dynamic Analysis] --> [IOC Extraction]
                                                                          |
                                                                          v
                                                                 [Report Generation]

See SKILL.md for detailed step-by-step procedures.

Scripts 1

agent.py10.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""CAPE Sandbox automated malware analysis agent.

Submits malware samples to a CAPE Sandbox instance via its REST API,
polls for analysis completion, and retrieves behavioral reports including
process trees, network activity, dropped files, and extracted configs.
"""
import argparse
import json
import os
import sys
import time
from datetime import datetime, timezone

try:
    import requests
except ImportError:
    print("[!] 'requests' library required: pip install requests", file=sys.stderr)
    sys.exit(1)


def get_cape_config():
    """Return CAPE server URL and optional API token."""
    server = os.environ.get("CAPE_URL", "http://localhost:8090")
    token = os.environ.get("CAPE_API_TOKEN", "")
    return server.rstrip("/"), token


def submit_file(server, token, file_path, timeout_minutes=5, machine=None):
    """Submit a file to CAPE for analysis."""
    url = f"{server}/apiv2/tasks/create/file/"
    headers = {"Authorization": f"Token {token}"} if token else {}
    if not os.path.isfile(file_path):
        print(f"[!] File not found: {file_path}", file=sys.stderr)
        sys.exit(1)
    data = {"timeout": timeout_minutes * 60}
    if machine:
        data["machine"] = machine
    print(f"[*] Submitting {os.path.basename(file_path)} to CAPE...")
    with open(file_path, "rb") as f:
        resp = requests.post(
            url,
            files={"file": (os.path.basename(file_path), f)},
            data=data,
            headers=headers,
            timeout=120,
        )
    resp.raise_for_status()
    result = resp.json()
    if result.get("error"):
        print(f"[!] Submission error: {result.get('error_value', result['error'])}", file=sys.stderr)
        sys.exit(1)
    task_id = result.get("data", {}).get("task_ids", [None])[0]
    if not task_id:
        task_id = result.get("task_id") or result.get("data", {}).get("task_id")
    print(f"[+] Submitted successfully, task ID: {task_id}")
    return task_id


def submit_url(server, token, target_url, timeout_minutes=5):
    """Submit a URL to CAPE for analysis."""
    url = f"{server}/apiv2/tasks/create/url/"
    headers = {"Authorization": f"Token {token}"} if token else {}
    data = {"url": target_url, "timeout": timeout_minutes * 60}
    print(f"[*] Submitting URL: {target_url}")
    resp = requests.post(url, data=data, headers=headers, timeout=60)
    resp.raise_for_status()
    result = resp.json()
    task_id = result.get("data", {}).get("task_ids", [None])[0]
    if not task_id:
        task_id = result.get("task_id")
    print(f"[+] Submitted, task ID: {task_id}")
    return task_id


def poll_task_status(server, token, task_id, max_wait=600, interval=15):
    """Poll CAPE until the analysis task completes or times out."""
    url = f"{server}/apiv2/tasks/status/{task_id}/"
    headers = {"Authorization": f"Token {token}"} if token else {}
    print(f"[*] Waiting for analysis to complete (max {max_wait}s)...")
    elapsed = 0
    while elapsed < max_wait:
        try:
            resp = requests.get(url, headers=headers, timeout=30)
            resp.raise_for_status()
            data = resp.json()
            status = data.get("data", data.get("status", "unknown"))
            if isinstance(status, dict):
                status = status.get("status", "unknown")
            if status in ("reported", "completed"):
                print(f"[+] Analysis complete (status: {status})")
                return True
            if status in ("failed_analysis", "failed_processing"):
                print(f"[!] Analysis failed: {status}", file=sys.stderr)
                return False
            print(f"    Status: {status} ({elapsed}s elapsed)")
        except requests.RequestException as e:
            print(f"    Connection error: {e}")
        time.sleep(interval)
        elapsed += interval
    print("[!] Timed out waiting for analysis", file=sys.stderr)
    return False


def get_report(server, token, task_id):
    """Retrieve the full analysis report for a completed task."""
    url = f"{server}/apiv2/tasks/get/report/{task_id}/"
    headers = {"Authorization": f"Token {token}"} if token else {}
    resp = requests.get(url, headers=headers, timeout=120)
    resp.raise_for_status()
    return resp.json()


def extract_findings(report):
    """Extract structured findings from CAPE report."""
    findings = []

    # Signatures (behavioral detections)
    for sig in report.get("signatures", []):
        findings.append({
            "category": "signature",
            "name": sig.get("name", "Unknown"),
            "severity": sig.get("severity", 0),
            "description": sig.get("description", ""),
            "families": sig.get("families", []),
            "ttp": sig.get("ttp", {}),
        })

    # Network IOCs
    network = report.get("network", {})
    for dns_entry in network.get("dns", []):
        findings.append({
            "category": "network_dns",
            "request": dns_entry.get("request", ""),
            "type": dns_entry.get("type", ""),
            "answers": [a.get("data", "") for a in dns_entry.get("answers", [])],
        })
    for http_entry in network.get("http", []):
        findings.append({
            "category": "network_http",
            "method": http_entry.get("method", ""),
            "uri": http_entry.get("uri", ""),
            "host": http_entry.get("host", ""),
            "port": http_entry.get("port", 80),
        })

    # Dropped files
    for dropped in report.get("dropped", []):
        findings.append({
            "category": "dropped_file",
            "name": dropped.get("name", ""),
            "path": dropped.get("filepath", ""),
            "type": dropped.get("type", ""),
            "size": dropped.get("size", 0),
            "sha256": dropped.get("sha256", ""),
        })

    # CAPE extracted payloads and configs
    cape_data = report.get("CAPE", {})
    if isinstance(cape_data, dict):
        for cape_item in cape_data.get("payloads", []):
            findings.append({
                "category": "cape_payload",
                "name": cape_item.get("name", ""),
                "module": cape_item.get("module_path", ""),
                "sha256": cape_item.get("sha256", ""),
                "cape_type": cape_item.get("cape_type", ""),
            })
        for config in cape_data.get("configs", []):
            findings.append({
                "category": "cape_config",
                "family": config.get("family", "unknown"),
                "config_data": config,
            })

    return findings


def format_summary(report, findings):
    """Print human-readable analysis summary."""
    info = report.get("info", {})
    target = report.get("target", {})
    score = report.get("malscore", info.get("score", 0))

    print(f"\n{'='*60}")
    print(f"  CAPE Sandbox Analysis Report")
    print(f"{'='*60}")
    print(f"  Task ID     : {info.get('id', 'N/A')}")
    print(f"  Duration    : {info.get('duration', 0)}s")
    print(f"  Malscore    : {score}/10")

    file_info = target.get("file", {})
    if file_info:
        print(f"  File        : {file_info.get('name', 'N/A')}")
        print(f"  SHA256      : {file_info.get('sha256', 'N/A')}")
        print(f"  Type        : {file_info.get('type', 'N/A')}")

    sig_findings = [f for f in findings if f["category"] == "signature"]
    net_dns = [f for f in findings if f["category"] == "network_dns"]
    net_http = [f for f in findings if f["category"] == "network_http"]
    dropped = [f for f in findings if f["category"] == "dropped_file"]
    configs = [f for f in findings if f["category"] == "cape_config"]

    print(f"\n  Signatures  : {len(sig_findings)}")
    print(f"  DNS Queries : {len(net_dns)}")
    print(f"  HTTP Reqs   : {len(net_http)}")
    print(f"  Dropped     : {len(dropped)}")
    print(f"  CAPE Configs: {len(configs)}")

    if sig_findings:
        print(f"\n  Top Signatures:")
        for s in sorted(sig_findings, key=lambda x: x.get("severity", 0), reverse=True)[:10]:
            sev = s.get("severity", 0)
            label = "HIGH" if sev >= 3 else "MEDIUM" if sev >= 2 else "LOW"
            print(f"    [{label:6s}] {s['name']}: {s['description'][:80]}")

    if configs:
        print(f"\n  Extracted Configs:")
        for c in configs:
            print(f"    Family: {c.get('family', 'unknown')}")

    return score


def main():
    parser = argparse.ArgumentParser(
        description="CAPE Sandbox malware analysis agent"
    )
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument("--file", help="Path to malware sample to submit")
    group.add_argument("--url", help="URL to submit for analysis")
    group.add_argument("--task-id", type=int, help="Retrieve report for existing task")
    parser.add_argument("--output", "-o", help="Output JSON report path")
    parser.add_argument("--server", help="CAPE server URL (or set CAPE_URL env var)")
    parser.add_argument("--token", help="API token (or set CAPE_API_TOKEN env var)")
    parser.add_argument("--machine", help="Specific VM to use for analysis")
    parser.add_argument("--timeout", type=int, default=5, help="Analysis timeout in minutes")
    parser.add_argument("--wait", type=int, default=600, help="Max seconds to wait for completion")
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    if args.server:
        os.environ["CAPE_URL"] = args.server
    if args.token:
        os.environ["CAPE_API_TOKEN"] = args.token

    server, token = get_cape_config()

    if args.task_id:
        task_id = args.task_id
    elif args.file:
        task_id = submit_file(server, token, args.file, args.timeout, args.machine)
    else:
        task_id = submit_url(server, token, args.url, args.timeout)

    if not args.task_id:
        if not poll_task_status(server, token, task_id, args.wait):
            sys.exit(1)

    report = get_report(server, token, task_id)
    findings = extract_findings(report)
    score = format_summary(report, findings)

    output_report = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "tool": "CAPE Sandbox",
        "task_id": task_id,
        "malscore": score,
        "findings_count": len(findings),
        "findings": findings,
        "risk_level": (
            "CRITICAL" if score >= 8
            else "HIGH" if score >= 5
            else "MEDIUM" if score >= 3
            else "LOW"
        ),
    }

    if args.output:
        with open(args.output, "w") as f:
            json.dump(output_report, f, indent=2)
        print(f"\n[+] Report saved to {args.output}")
    elif args.verbose:
        print(json.dumps(output_report, indent=2))


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 0.4 KB
Keep exploring