mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-02 17:07:42 +03:00
Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands - Fix 49 verify=False in defensive skills (env-var override) - Add timeout to 231 HTTP/subprocess/socket calls - Fix 6 SQL injection patterns with whitelist validation - Replace 8 __import__() with standard imports - Remove 701 unused imports across 442 files - Add authorized-testing disclaimers to all offensive skills - Complete 11 incomplete skill directories - Expand 10 stub SKILL.md files with full content - Fix 2 YAML parse errors in frontmatter - Fix 5 pre-existing syntax errors - Convert 22 hardcoded paths/ports to environment variables - Back up 21 redundant skill pairs to .bak - Fix 2 global declaration errors - 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE) - 0 compile errors across all 724 agent.py files
This commit is contained in:
@@ -1,60 +1,288 @@
|
||||
#!/usr/bin/env python3
|
||||
"""CAPE sandbox malware analysis agent."""
|
||||
import argparse, json, sys
|
||||
"""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:
|
||||
requests = None
|
||||
print("[!] 'requests' library required: pip install requests", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def run_scan(target, token=None):
|
||||
findings = []
|
||||
if not requests: return [{"error": "requests required"}]
|
||||
headers = {"Authorization": f"Bearer {token}"} if token else {}
|
||||
try:
|
||||
resp = requests.get(f"{target}", headers=headers, timeout=15)
|
||||
if resp.status_code == 200:
|
||||
findings.append({"check": "Target Accessible", "status": "OK", "severity": "INFO"})
|
||||
else:
|
||||
findings.append({"check": "Target Access", "status": f"HTTP {resp.status_code}", "severity": "MEDIUM"})
|
||||
except requests.RequestException as e:
|
||||
findings.append({"error": str(e)})
|
||||
return findings
|
||||
|
||||
def analyze_results(target, token=None):
|
||||
findings = []
|
||||
if not requests: return []
|
||||
headers = {"Authorization": f"Bearer {token}"} if token else {}
|
||||
try:
|
||||
resp = requests.get(f"{target}/api/v1/results", headers=headers, timeout=15)
|
||||
if resp.status_code == 200:
|
||||
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()
|
||||
for item in data.get("findings", data.get("results", [])):
|
||||
severity = item.get("severity", item.get("risk", "MEDIUM"))
|
||||
findings.append({"check": item.get("name", item.get("title", "unknown")),
|
||||
"severity": severity.upper() if isinstance(severity, str) else "MEDIUM"})
|
||||
except requests.RequestException:
|
||||
pass
|
||||
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():
|
||||
p = argparse.ArgumentParser(description="CAPE sandbox malware analysis agent")
|
||||
p.add_argument("--target", required=True, help="Target URL or IP")
|
||||
p.add_argument("--token", help="API token")
|
||||
p.add_argument("--output", "-o", help="Output JSON report")
|
||||
p.add_argument("--verbose", "-v", action="store_true")
|
||||
a = p.parse_args()
|
||||
print("[*] CAPE sandbox malware analysis agent")
|
||||
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "target": a.target, "findings": []}
|
||||
report["findings"].extend(run_scan(a.target, a.token))
|
||||
report["findings"].extend(analyze_results(a.target, a.token))
|
||||
high = sum(1 for f in report["findings"] if f.get("severity") in ("HIGH", "CRITICAL"))
|
||||
report["risk_level"] = "CRITICAL" if high > 2 else "HIGH" if high else "MEDIUM" if report["findings"] else "LOW"
|
||||
print(f"[*] {len(report['findings'])} findings, risk: {report['risk_level']}")
|
||||
if a.output:
|
||||
with open(a.output, "w") as f: json.dump(report, f, indent=2)
|
||||
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:
|
||||
print(json.dumps(report, indent=2))
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user