Office 文件恶意检测:从静态分析到行为溯源

Published on with 0 views and 0 comments

Office 文件恶意检测:从静态分析到行为溯源

适用对象:安全运营(SOC)、恶意样本分析(MA)、邮件网关/终端防护研发。本文兼顾可读性与落地性,提供可直接运行的检测思路和脚本。


1. 为什么 Office 文件仍是恶意软件重灾区

Microsoft Office(Word、Excel、PowerPoint)及其兼容格式(WPS、OpenOffice)是企业办公的默认入口,天然具备以下攻击优势:

  • 高信任度:用户习惯双击打开 .docx/.xlsx,安全软件对办公文档的拦截阈值较高。
  • 强大的可编程能力:VBA 宏、Office JS、Excel 4.0(XLM)宏、DDE 字段均可执行代码。
  • 复合文件格式:OLE2 / OOXML 结构复杂,攻击者可将 Shellcode、PE、脚本、URL 嵌入到非预期位置(如批注、超链接、ActiveX、远程模板)。
  • 社工温床:伪造的发票、简历、通知、会议纪要等主题极易诱导启用宏。

因此,Office 文档是 APT、勒索软件、钓鱼邮件的第一载荷选择之一。


2. 典型 Office 恶意文档技术画像

技术说明常见场景
VBA 宏文档打开或关闭时自动运行 AutoOpenDocument_Open 等过程Emotet、TrickBot、Qakbot 历史分发
Excel 4.0 (XLM) 宏旧版宏语言,无需 VBA 环境,混淆后检测难度大近五年金融恶意邮件高频出现
DDE / OLE 自动更新Word 字段 DDEAUTO 或 Excel 公式执行外部程序无需启用宏即可启动程序
OLE 嵌入对象在文档内嵌入可执行文件、脚本、LNK、Package 对象点击图标后执行
远程模板加载 (.dotm/.xlam)主文档本身干净,打开时加载远程恶意模板绕过静态文件查杀
CVE 漏洞利用CVE-2017-11882(EQNEDT32)、CVE-2017-8570、CVE-2018-0802 等无需交互,打开即触发
OOXML 结构异常重打包 zip、隐藏工作表、异常 XML 关系、加密的 VBA 工程反取证、反静态分析
PowerPoint 动作/超链接幻灯片切换动作或 mouseover 触发下载会议投屏钓鱼

3. 检测思路:三层漏斗模型

建议把 Office 文件检测拆成 三层漏斗,逐层提高成本,降低误报:

3.1 第一层:文件级快速过滤(低成本、高吞吐)

  • 文件类型识别:检查真实魔数(Magic Number),识别 .docx 伪装成 .pdf、重命名过的 .rtf 等。
  • 熵值检测:加密/混淆后的宏或 Shellcode 熵值显著高于正常文本。
  • 文件名/来源信誉:发件人域名、URL、附件哈希是否在威胁情报库命中。
  • 沙箱信誉:文件哈希同步 VirusTotal、MalwareBazaar、AlienVault OTX。

3.2 第二层:静态结构分析(中等成本、可解释)

  • OLE / OOXML 结构解析
    • 是否包含宏?`
    • 是否有 OLE 对象?
    • 是否有远程关系(external relationship)?
    • Excel 是否有隐藏/非常隐藏工作表?
  • 宏代码提取与语义分析
    • 提取 VBA / XLM 源码;
    • 检测危险 API:CreateObjectShellWScript.ShellRunDownloadStringEnviron、注册表操作等;
    • 混淆指标:变量名随机化、大量拼接字符串、Base64、Chr/Hex 编码。
  • IOC 匹配:URL、IP、域名、文件路径、Base64 编码的可执行文件签名。

3.3 第三层:动态行为分析(高成本、高置信)

  • 将可疑文档投入沙箱(如 Cuckoo、ANY.RUN、FireEye、深信服、奇安信)。
  • 观察子进程、网络连接、文件写入、注册表修改、UAC 提权等。
  • 对 Office 进程异常行为进行告警:Winword 启动 PowerShell、WMI、regsvr32、mshta 等。

4. 常用工具矩阵

工具/库用途推荐场景
oletoolsPython 套件,含 oleidolemetaolevbamraptorrtfobjoleobjolefile宏提取、OLE 对象分析、结构检查
olefile读写 OLE 文件自定义解析
python-docx / openpyxl解析 OOXML Word/Excel自定义提取隐藏内容
YARA规则匹配IOC、家族特征、混淆模式
VirusTotal / MalwareBazaar云端查杀与情报哈希查询、多引擎检测
Cuckoo / ANY.RUN沙箱动态分析行为确认
xlrd / xlrd2读取 Excel 二进制/XMLXLM 宏分析
pcodedmp反编译 VBA P-code源码被删除或加密时

5. 实战:一个可落地的静态检测脚本

以下脚本演示如何批量检查 Office 文档中的宏、危险 API、OLE 对象和外部关系。依赖 oletoolsolefilepython-docxopenpyxl

#!/usr/bin/env python3
# office_scan.py
import os
import re
import sys
import zipfile
from pathlib import Path
from oletools.olevba import VBA_Parser, TYPE_OLE, TYPE_OpenXML, TYPE_Word2003_XML
from oletools.oleobj import OleObject
from oletools.oleid import OleID
from oletools.rtfobj import RtfObjParser

DANGEROUS_KEYWORDS = [
    r"Shell\s*\(", r"CreateObject\s*\(", r"WScript\.Shell",
    r"DownloadString", r"Invoke-Expression", r"IEX",
    r"powershell", r"cmd\.exe", r"mshta", r"regsvr32",
    r"rundll32", r"Environ\s*\(", r"CreateProcess",
    r"VirtualAlloc", r"WriteProcessMemory", r"Net\.WebClient",
    r"Scripting\.FileSystemObject", r"ADODB\.Stream",
]

RE_SUSPICIOUS = re.compile("|".join(DANGEROUS_KEYWORDS), re.IGNORECASE)

def check_ole(file_path):
    """对 OLE/OOXML 文档做 VBA 和 OLE 对象检查"""
    results = []
    try:
        vba = VBA_Parser(file_path)
        if vba.detect_vba_macros():
            results.append("[WARNING] 检测到 VBA/XLM 宏")
            for (subfilename, stream_path, vba_filename, vba_code) in vba.extract_macros():
                # 危险 API 检测
                matches = RE_SUSPICIOUS.findall(vba_code)
                if matches:
                    results.append(f"  [DANGER] 宏中危险函数: {set(matches)}")
                # 疑似下载/执行
                if any(k in vba_code.lower() for k in ["http", "https", "ftp", "\\", "cmd"]):
                    results.append("  [WARNING] 宏中出现网络/命令相关字符串")
        vba.close()
    except Exception as e:
        results.append(f"[ERROR] VBA 解析失败: {e}")

    try:
        oid = OleID(file_path)
        indicators = oid.check()
        for ind in indicators:
            if ind.value and ind.risk == "HIGH":
                results.append(f"[HIGH] {ind.name}: {ind.description}")
    except Exception as e:
        results.append(f"[ERROR] OleID 失败: {e}")
    return results

def check_ooxml_relationships(file_path):
    """检查 OOXML 中的外部关系(远程模板、图片、链接)"""
    results = []
    if not zipfile.is_zipfile(file_path):
        return results
    try:
        with zipfile.ZipFile(file_path) as zf:
            for name in zf.namelist():
                if name.endswith(".rels"):
                    content = zf.read(name).decode("utf-8", errors="ignore")
                    # TargetMode="External" 表示外部关系
                    external = re.findall(r'TargetMode="External"[^>]*Target="([^"]+)"', content)
                    for url in external:
                        if url.startswith("http"):
                            results.append(f"[WARNING] 外部关系: {url}")
    except Exception as e:
        results.append(f"[ERROR] OOXML 关系解析失败: {e}")
    return results

def check_rtf(file_path):
    """对 RTF 文件做 OLE 对象检查"""
    results = []
    try:
        with open(file_path, "rb") as f:
            parser = RtfObjParser(f)
            parser.parse()
            for obj in parser.objects:
                if obj.is_ole:
                    results.append(f"[WARNING] RTF 内嵌 OLE 对象: {obj.oleformat_id}")
    except Exception as e:
        results.append(f"[ERROR] RTF 解析失败: {e}")
    return results

def scan_file(file_path):
    print(f"\n=== 扫描: {file_path} ===")
    ext = Path(file_path).suffix.lower()
    all_results = []
    if ext in {".doc", ".xls", ".ppt", ".docx", ".xlsx", ".pptx", ".docm", ".xlsm", ".pptm"}:
        all_results += check_ole(file_path)
        all_results += check_ooxml_relationships(file_path)
    elif ext == ".rtf":
        all_results += check_rtf(file_path)
    else:
        print("[SKIP] 非 Office 文件")
        return

    if all_results:
        for r in all_results:
            print(r)
    else:
        print("[OK] 未检出明显恶意特征")

if __name__ == "__main__":
    for path in sys.argv[1:]:
        if os.path.isfile(path):
            scan_file(path)

使用示例

python office_scan.py 发票.docx 工资表.xlsm

5.1 进阶检测点(脚本可扩展)

  • Excel 4.0 宏:使用 xlrd2xlsm2 解析宏表,检查 FORMULAGOTOHALT 等函数。
  • CVE-2017-11882 公式:检测 OLE 中是否包含 Equation Native 流且伴有异常字体记录。
  • P-code 反编译:当 VBA 源码被清除但 P-code 残留时,用 pcodedmp 还原。
  • Encrypted VBA 工程oletoolsmsoffcrypto 插件可检测并尝试弱密码爆破。

6. YARA 规则思路(可落地)

YARA 适合作为第二层/第三层的规则补充。例如:

rule Office_Macro_Download_Execute {
    strings:
        $a = "WScript.Shell" nocase
        $b = "Microsoft.XMLHTTP" nocase
        $c = "ADODB.Stream" nocase
        $d = "Shell(" nocase
    condition:
        uint32(0) == 0xE011CFD0 or  // OLE2
        filesize < 10MB and
        2 of them
}

rule Office_XLM_Exec {
    strings:
        $a = "=EXEC(" nocase
        $b = "=RUN(" nocase
        $c = "=REGISTER(" nocase
    condition:
        any of them
}

7. 检测体系建设建议

  1. 邮件网关/入口:对附件先算哈希→VT 查杀→解压 OOXML→结构规则过滤→宏提取→危险 API 匹配;命中高危直接隔离。
  2. 终端 EDR:对 Office 进程派生子进程(如 powershellcmdmshtawscript)的行为建模,实时告警。
  3. 情报联动:把本地检出的 IOC(URL、域名、文件哈希)回收到 TI 平台,实现家族归因和同源扩线。
  4. 攻防演练:定期用 benign 宏做红队测试,验证检测规则是否漏报;用白名单减少误报。

8. 常见误区

  • 只看文件后缀:攻击者会把 .exe 重命名为 .docx,或把 OLE 文档伪装成 PDF。必须魔数校验。
  • 只依赖 VT 多引擎查杀:新样本 0day 阶段往往报毒率很低,要结合静态结构和行为分析。
  • 宏一禁了之:企业业务宏(如财务、HR 报表)往往真实存在,需要按签名、路径、域名做白名单管理,而非一刀切。

9. 总结

Office 文件恶意检测的核心不是“有没有宏”,而是“文件结构和代码语义是否有异常执行意图”。建议采用:

哈希信誉 → 文件结构解析 → 宏/对象静态分析 → 危险函数/IOC 匹配 → 沙箱动态行为确认

这五层漏斗,配合 YARA 规则与 EDR 行为监控,能覆盖大多数 Office 文档类攻击。


参考资源