인터페이스는 현지화되어 있으며, Skill 소개와 설명 문서는 선택한 언어로 AI가 자동 번역합니다. 코드·명령어·경로·전문 용어는 원문을 유지하며 원작자 문서가 우선합니다.
Skills 둘러보기
생산성 및 협업 · SKILL.md

building-automated-malware-submission-pipeline

building-automated-malware-submission-pipeline

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

Independent GitHub snapshot · Automated classification · Original documentation governs

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