L’interface est localisée ; les présentations et documents des Skills sont traduits automatiquement par IA dans votre langue. Le code, les commandes, chemins et termes techniques restent en version originale, et le document de l’auteur fait foi.
Explorer les Skills
Productivité · SKILL.md

building-automated-malware-submission-pipeline

building-automated-malware-submission-pipeline

构建自动化恶意软件提交和分析流水线,从终端和邮件网关收集可疑文件, 将其提交至沙箱环境和多引擎扫描器,并生成带有失陷指标(IOC)的研判结论以集成到 SIEM。 适用于 SOC 团队需要将恶意软件分析扩展到高容量告警分诊的场景,超越手动沙箱提交的限制。

Independent GitHub snapshot · Automated classification · Original documentation governs

Documentation de la Skill

GitHub

name: building-automated-malware-submission-pipeline description: > 构建自动化恶意软件提交和分析流水线,从终端和邮件网关收集可疑文件, 将其提交至沙箱环境和多引擎扫描器,并生成带有失陷指标(IOC)的研判结论以集成到 SIEM。 适用于 SOC 团队需要将恶意软件分析扩展到高容量告警分诊的场景,超越手动沙箱提交的限制。 domain: cybersecurity subdomain: soc-operations tags: [soc, malware-analysis, sandbox, automation, virustotal, cuckoo, any-run, pipeline] version: "1.0" author: mahipal license: Apache-2.0

构建自动化恶意软件提交流水线

适用场景

以下情况使用本技能:

  • SOC 团队面临大量可疑文件告警,需要沙箱分析
  • 手动沙箱提交在告警分诊工作流中造成瓶颈
  • 终端和邮件安全工具隔离了需要自动研判的文件
  • 事件响应(Incident Response)需要快速识别恶意软件家族并提取失陷指标(IOC)

不适用于在生产环境中分析实时恶意软件样本——请始终使用隔离的沙箱基础设施。

前置条件

  • 沙箱环境:Cuckoo Sandbox、Joe Sandbox、Any.Run 或 VMRay
  • VirusTotal API 密钥(企业版用于提交,免费版用于查询)
  • MalwareBazaar API 访问权限,用于已知恶意软件查询
  • 文件收集机制:EDR 隔离 API、邮件网关导出、网络捕获
  • Python 3.8+ 及 requests、vt-py、pefile 库
  • 与生产网络完全隔离的分析网络

工作流程

步骤 1:构建文件收集流水线

从多个来源收集可疑文件:

import requests
import hashlib
import os
from pathlib import Path
from datetime import datetime

class MalwareCollector:
    def __init__(self, quarantine_dir="/opt/malware_quarantine"):
        self.quarantine_dir = Path(quarantine_dir)
        self.quarantine_dir.mkdir(exist_ok=True)

    def collect_from_edr(self, edr_api_url, api_token):
        """从 CrowdStrike Falcon 拉取隔离文件"""
        headers = {"Authorization": f"Bearer {api_token}"}

        # 获取最近的隔离事件
        response = requests.get(
            f"{edr_api_url}/quarantine/queries/quarantined-files/v1",
            headers=headers,
            params={"filter": "state:'quarantined'", "limit": 50}
        )
        file_ids = response.json()["resources"]

        for file_id in file_ids:
            # 下载隔离文件
            dl_response = requests.get(
                f"{edr_api_url}/quarantine/entities/quarantined-files/v1",
                headers=headers,
                params={"ids": file_id}
            )
            file_data = dl_response.content
            sha256 = hashlib.sha256(file_data).hexdigest()

            filepath = self.quarantine_dir / f"{sha256}.sample"
            filepath.write_bytes(file_data)
            yield {"sha256": sha256, "path": str(filepath), "source": "edr"}

    def collect_from_email_gateway(self, smtp_quarantine_path):
        """从邮件网关隔离区拉取附件"""
        import email
        from email import policy

        for eml_file in Path(smtp_quarantine_path).glob("*.eml"):
            msg = email.message_from_binary_file(
                eml_file.open("rb"), policy=policy.default
            )
            for attachment in msg.iter_attachments():
                content = attachment.get_content()
                if isinstance(content, str):
                    content = content.encode()
                sha256 = hashlib.sha256(content).hexdigest()
                filename = attachment.get_filename() or "unknown"

                filepath = self.quarantine_dir / f"{sha256}.sample"
                filepath.write_bytes(content)
                yield {
                    "sha256": sha256,
                    "path": str(filepath),
                    "source": "email",
                    "original_filename": filename,
                    "sender": msg["From"],
                    "subject": msg["Subject"]
                }

    def compute_hashes(self, filepath):
        """计算文件的 MD5、SHA1、SHA256"""
        with open(filepath, "rb") as f:
            content = f.read()
        return {
            "md5": hashlib.md5(content).hexdigest(),
            "sha1": hashlib.sha1(content).hexdigest(),
            "sha256": hashlib.sha256(content).hexdigest(),
            "size": len(content)
        }

步骤 2:哈希查询预筛选

在提交沙箱前检查文件是否已知:

import vt

class MalwarePreScreener:
    def __init__(self, vt_api_key, mb_api_url="https://mb-api.abuse.ch/api/v1/"):
        self.vt_client = vt.Client(vt_api_key)
        self.mb_api_url = mb_api_url

    def check_virustotal(self, sha256):
        """在 VirusTotal 中查询哈希"""
        try:
            file_obj = self.vt_client.get_object(f"/files/{sha256}")
            stats = file_obj.last_analysis_stats
            return {
                "found": True,
                "malicious": stats.get("malicious", 0),
                "suspicious": stats.get("suspicious", 0),
                "undetected": stats.get("undetected", 0),
                "total": sum(stats.values()),
                "threat_label": getattr(file_obj, "popular_threat_classification", {}).get(
                    "suggested_threat_label", "Unknown"
                ),
                "type": getattr(file_obj, "type_description", "Unknown")
            }
        except vt.APIError:
            return {"found": False}

    def check_malwarebazaar(self, sha256):
        """在 MalwareBazaar 中查询哈希"""
        response = requests.post(
            self.mb_api_url,
            data={"query": "get_info", "hash": sha256}
        )
        data = response.json()
        if data["query_status"] == "ok":
            entry = data["data"][0]
            return {
                "found": True,
                "signature": entry.get("signature", "Unknown"),
                "tags": entry.get("tags", []),
                "file_type": entry.get("file_type", "Unknown"),
                "first_seen": entry.get("first_seen", "Unknown")
            }
        return {"found": False}

    def pre_screen(self, sha256):
        """执行所有预筛选检查"""
        vt_result = self.check_virustotal(sha256)
        mb_result = self.check_malwarebazaar(sha256)

        verdict = "UNKNOWN"
        if vt_result["found"] and vt_result.get("malicious", 0) > 10:
            verdict = "KNOWN_MALICIOUS"
        elif vt_result["found"] and vt_result.get("malicious", 0) == 0:
            verdict = "LIKELY_CLEAN"

        return {
            "sha256": sha256,
            "virustotal": vt_result,
            "malwarebazaar": mb_result,
            "pre_screen_verdict": verdict,
            "needs_sandbox": verdict == "UNKNOWN"
        }

    def close(self):
        self.vt_client.close()

步骤 3:提交沙箱进行动态分析

Cuckoo Sandbox 提交:

class SandboxSubmitter:
    def __init__(self, cuckoo_url="http://cuckoo.internal:8090"):
        self.cuckoo_url = cuckoo_url

    def submit_to_cuckoo(self, filepath, timeout=300):
        """提交文件到 Cuckoo Sandbox"""
        with open(filepath, "rb") as f:
            response = requests.post(
                f"{self.cuckoo_url}/tasks/create/file",
                files={"file": f},
                data={
                    "timeout": timeout,
                    "options": "procmemdump=yes,route=none",
                    "priority": 2,
                    "machine": "win10_x64"
                }
            )
        task_id = response.json()["task_id"]
        return task_id

    def wait_for_analysis(self, task_id, poll_interval=30, max_wait=600):
        """等待沙箱分析完成"""
        import time
        elapsed = 0
        while elapsed < max_wait:
            response = requests.get(f"{self.cuckoo_url}/tasks/view/{task_id}")
            status = response.json()["task"]["status"]
            if status == "reported":
                return self.get_report(task_id)
            elif status == "failed_analysis":
                return {"error": "Analysis failed"}
            time.sleep(poll_interval)
            elapsed += poll_interval
        return {"error": "Analysis timed out"}

    def get_report(self, task_id):
        """获取分析报告"""
        response = requests.get(f"{self.cuckoo_url}/tasks/report/{task_id}")
        report = response.json()

        # 提取关键指标
        return {
            "task_id": task_id,
            "score": report.get("info", {}).get("score", 0),
            "signatures": [
                {"name": s["name"], "severity": s["severity"], "description": s["description"]}
                for s in report.get("signatures", [])
            ],
            "network": {
                "dns": [d["request"] for d in report.get("network", {}).get("dns", [])],
                "http": [
                    {"url": h["uri"], "method": h["method"]}
                    for h in report.get("network", {}).get("http", [])
                ],
                "hosts": report.get("network", {}).get("hosts", [])
            },
            "dropped_files": [
                {"name": f["name"], "sha256": f["sha256"], "size": f["size"]}
                for f in report.get("dropped", [])
            ],
            "processes": [
                {"name": p["process_name"], "pid": p["pid"], "command_line": p.get("command_line", "")}
                for p in report.get("behavior", {}).get("processes", [])
            ],
            "registry_keys": [
                k for k in report.get("behavior", {}).get("summary", {}).get("regkey_written", [])
            ]
        }

    def submit_to_joesandbox(self, filepath, joe_api_key, joe_url="https://jbxcloud.joesecurity.org/api"):
        """提交到 Joe Sandbox Cloud"""
        with open(filepath, "rb") as f:
            response = requests.post(
                f"{joe_url}/v2/submission/new",
                headers={"Authorization": f"Bearer {joe_api_key}"},
                files={"sample": f},
                data={
                    "systems": "w10_64",
                    "internet-access": False,
                    "report-cache": True
                }
            )
        return response.json()["data"]["webid"]

步骤 4:提取 IOC 并生成研判结论

class VerdictGenerator:
    def __init__(self):
        self.malicious_threshold = 7  # Cuckoo 评分阈值

    def generate_verdict(self, pre_screen, sandbox_report):
        """综合预筛选和沙箱结果生成最终研判"""
        iocs = {
            "ips": [],
            "domains": [],
            "urls": [],
            "hashes": [],
            "registry_keys": [],
            "files_dropped": []
        }

        # 从沙箱报告提取 IOC
        if sandbox_report:
            iocs["domains"] = sandbox_report.get("network", {}).get("dns", [])
            iocs["ips"] = sandbox_report.get("network", {}).get("hosts", [])
            iocs["urls"] = [
                h["url"] for h in sandbox_report.get("network", {}).get("http", [])
            ]
            iocs["hashes"] = [
                f["sha256"] for f in sandbox_report.get("dropped_files", [])
            ]
            iocs["registry_keys"] = sandbox_report.get("registry_keys", [])[:10]
            iocs["files_dropped"] = sandbox_report.get("dropped_files", [])

        # 确定研判结论
        vt_malicious = pre_screen.get("virustotal", {}).get("malicious", 0)
        sandbox_score = sandbox_report.get("score", 0) if sandbox_report else 0
        sig_count = len(sandbox_report.get("signatures", [])) if sandbox_report else 0

        combined_score = (vt_malicious * 2) + (sandbox_score * 10) + (sig_count * 5)

        if combined_score >= 100:
            verdict = "MALICIOUS"
            confidence = "HIGH"
        elif combined_score >= 50:
            verdict = "SUSPICIOUS"
            confidence = "MEDIUM"
        elif combined_score >= 20:
            verdict = "POTENTIALLY_UNWANTED"
            confidence = "LOW"
        else:
            verdict = "CLEAN"
            confidence = "HIGH"

        return {
            "verdict": verdict,
            "confidence": confidence,
            "combined_score": combined_score,
            "iocs": iocs,
            "vt_detections": vt_malicious,
            "sandbox_score": sandbox_score,
            "signatures": sandbox_report.get("signatures", []) if sandbox_report else []
        }

步骤 5:将结果推送至 SIEM

def push_to_splunk(verdict_result, splunk_url, splunk_token):
    """通过 Splunk HEC 发送恶意软件分析研判结论"""
    import json

    event = {
        "sourcetype": "malware_analysis",
        "source": "malware_pipeline",
        "event": {
            "sha256": verdict_result["sha256"],
            "verdict": verdict_result["verdict"],
            "confidence": verdict_result["confidence"],
            "score": verdict_result["combined_score"],
            "vt_detections": verdict_result["vt_detections"],
            "sandbox_score": verdict_result["sandbox_score"],
            "malware_family": verdict_result.get("threat_label", "Unknown"),
            "iocs": verdict_result["iocs"],
            "signatures": [s["name"] for s in verdict_result["signatures"]]
        }
    }

    response = requests.post(
        f"{splunk_url}/services/collector/event",
        headers={
            "Authorization": f"Splunk {splunk_token}",
            "Content-Type": "application/json"
        },
        json=event,
        verify=False
    )
    return response.status_code == 200

def push_iocs_to_blocklist(iocs, firewall_api):
    """将提取的 IOC 推送至封锁基础设施"""
    for ip in iocs.get("ips", []):
        requests.post(
            f"{firewall_api}/block",
            json={"type": "ip", "value": ip, "action": "block", "source": "malware_pipeline"}
        )
    for domain in iocs.get("domains", []):
        requests.post(
            f"{firewall_api}/block",
            json={"type": "domain", "value": domain, "action": "sinkhole", "source": "malware_pipeline"}
        )

步骤 6:编排完整流水线

def run_malware_pipeline(sample_path, config):
    """执行完整的恶意软件分析流水线"""
    collector = MalwareCollector()
    screener = MalwarePreScreener(config["vt_key"])
    submitter = SandboxSubmitter(config["cuckoo_url"])
    generator = VerdictGenerator()

    # 第一步:哈希计算和预筛选
    hashes = collector.compute_hashes(sample_path)
    pre_screen = screener.pre_screen(hashes["sha256"])

    # 第二步:如未知则提交沙箱
    sandbox_report = None
    if pre_screen["needs_sandbox"]:
        task_id = submitter.submit_to_cuckoo(sample_path)
        sandbox_report = submitter.wait_for_analysis(task_id)

    # 第三步:生成研判结论
    verdict = generator.generate_verdict(pre_screen, sandbox_report)
    verdict["sha256"] = hashes["sha256"]
    verdict["threat_label"] = pre_screen.get("virustotal", {}).get("threat_label", "Unknown")

    # 第四步:推送至 SIEM
    push_to_splunk(verdict, config["splunk_url"], config["splunk_token"])

    # 第五步:如为恶意则封锁
    if verdict["verdict"] == "MALICIOUS":
        push_iocs_to_blocklist(verdict["iocs"], config["firewall_api"])

    screener.close()
    return verdict

核心概念

术语定义
动态分析(Dynamic Analysis)在沙箱中执行恶意软件以观察运行时行为(进程创建、网络、文件系统变更)
静态分析(Static Analysis)不执行恶意软件的检查(哈希查询、字符串分析、PE 头检查)
沙箱规避(Sandbox Evasion)恶意软件用于检测沙箱环境并改变行为以规避分析的技术
IOC 提取(IOC Extraction)从沙箱报告自动识别网络指标、文件取证痕迹和注册表变更的过程
多 AV 扫描(Multi-AV Scanning)将样本提交至多个杀毒引擎(VirusTotal)以进行基于共识的检测
研判结论(Verdict)样本的最终分类:Malicious(恶意)、Suspicious(可疑)、Potentially Unwanted(可能不需要)或 Clean(干净)

工具与系统

  • Cuckoo Sandbox:开源自动化恶意软件分析平台,具备行为分析和网络捕获功能
  • Joe Sandbox:商业沙箱,具有深度行为分析、YARA 匹配和 MITRE ATT&CK 映射
  • Any.Run:交互式沙箱服务,允许在分析过程中实时操作,用于调试规避型恶意软件
  • VirusTotal:多引擎扫描服务,提供 70+ 杀毒引擎结果和行为分析报告
  • CAPE Sandbox:社区维护的 Cuckoo 分支,增强了载荷提取和配置转储功能

常见场景

  • 邮件附件分诊:自动提交隔离的邮件附件,在 5 分钟内生成研判结论
  • EDR 隔离文件处理:批量处理终端安全隔离的文件以进行详细分析
  • 事件调查:提交 IR 过程中发现的可疑二进制文件以识别恶意软件家族并提取 IOC
  • 威胁情报富化:分析来自威胁情报订阅的样本以提取 C2 基础设施并更新封锁
  • 零日检测:沙箱通过行为分析捕获基于签名的 AV 遗漏的新型恶意软件

输出格式

MALWARE ANALYSIS REPORT — Pipeline Submission
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Sample:       invoice_march.docx
SHA256:       a1b2c3d4e5f6a7b8...
File Type:    Microsoft Word Document (macro-enabled)

Pre-Screening:
  VirusTotal:    34/72 malicious (Emotet.Downloader)
  MalwareBazaar: Tags: emotet, macro, downloader

Sandbox Analysis (Cuckoo):
  Score:         9.2/10 (MALICIOUS)
  Signatures:
    - Macro executes PowerShell download cradle (severity: 8)
    - Process injection into explorer.exe (severity: 9)
    - Connects to known Emotet C2 server (severity: 9)

Extracted IOCs:
  C2 IPs:       185.234.218[.]50:8080, 45.77.123[.]45:443
  Domains:       update-service[.]evil[.]com
  Dropped Files: payload.dll (SHA256: b2c3d4e5...)
  Registry:      HKCU\Software\Microsoft\Windows\CurrentVersion\Run\Update

VERDICT: MALICIOUS (Emotet Downloader) — Confidence: HIGH
ACTIONS:
  [DONE] IOCs pushed to Splunk threat intel
  [DONE] C2 IPs blocked on firewall
  [DONE] Domain sinkholed on DNS
  [DONE] Hash blocked on endpoint
Skills enregistrées