一个JS沙箱逃逸漏洞
字数 1380 2025-08-29 08:32:00

JavaScript沙箱逃逸漏洞分析与利用

1. 漏洞背景

本文详细分析了一个JavaScript沙箱逃逸漏洞,该漏洞存在于static-eval库中,允许攻击者绕过沙箱限制执行任意代码。这种漏洞在需要执行用户输入表达式的Web应用中尤其危险。

2. static-eval库简介

static-eval是一个用于安全评估JavaScript表达式的库,它通过以下方式工作:

  1. 使用esprima库将表达式解析为AST(抽象语法树)
  2. 分析AST结构,确保表达式是安全的
  3. 评估通过检查的表达式

虽然文档说明它"不是设计用作沙箱",但许多开发者仍错误地将其用于此目的。

3. 已知漏洞分析

3.1 第一个漏洞:Function构造器利用

漏洞原理

"".sub.constructor("console.log(process.env)")()
  • "".sub获取String构造器的substring方法(一个函数对象)
  • 访问其constructor属性获取Function构造器
  • 使用Function构造器创建新函数并执行

修复方式
禁止访问函数对象的属性:

if((obj === FAIL) || (typeof obj == 'function')){
    return FAIL;
}

3.2 第二个漏洞:匿名函数利用

漏洞原理

(function(){console.log(process.env)})()
  • 创建匿名函数并立即执行
  • 函数体被直接传递给Function构造器

修复方式
最初完全禁用匿名函数,后来改为分析函数体内容。

4. 新漏洞发现:参数析构绕过

4.1 漏洞原理

利用ECMAScript的参数析构特性绕过沙箱检查:

(function({book}){return book.constructor})({book:"".sub})("恶意代码")()

关键点

  1. 使用对象模式(ObjectPattern)参数而非标识符(Identifier)
  2. static-eval检查时不会覆盖对象模式参数的变量值
  3. 运行时传入函数对象,获取其constructor

4.2 技术细节

  1. AST分析差异

    • 普通参数:Identifier类型 → 被覆盖为null
    • 析构参数:ObjectPattern类型 → 不被覆盖
  2. 执行流程

    • 定义时:book被视为普通对象(允许访问constructor)
    • 调用时:传入"".sub(函数对象),成功获取Function构造器

4.3 完整利用链

(function({book}){
  return book.constructor
})({book:"".sub})(
  "console.log(global.process.mainModule.constructor._load(\"child_process\").execSync(\"id\").toString())"
)()

5. 实际应用限制

jsonpath库中使用时遇到限制:

  • jsonpath使用@作为特殊变量名
  • @不是合法ECMAScript标识符
  • 导致函数包装失败,利用链中断

6. 防御建议

  1. 不要使用static-eval作为沙箱:遵循库作者的明确警告
  2. 参数检查强化:应检查所有可能的参数类型,不仅是Identifier
  3. 运行时类型验证:对函数调用时的参数进行额外检查
  4. 严格限制可用对象:使用Proxy或冻结关键对象

7. 漏洞时间线

  • 2019/01/02:漏洞报告提交
  • 2019/01/03:NodeJS安全团队响应
  • 2019/02/14:漏洞正式发布
  • 2019/02/15:static-eval发布修复版本
  • 2019/02/18:README添加免责声明
  • 2019/02/26:修复补丁中的bug

8. 总结

此漏洞展示了JavaScript沙箱实现的复杂性,特别是当语言特性与安全模型交互时。关键教训包括:

  1. 参数解析不一致会导致严重安全漏洞
  2. AST分析必须覆盖所有可能的语法结构
  3. 运行时行为可能与静态分析时的假设不同
  4. 依赖非沙箱设计的库实现沙箱功能是危险的

安全开发人员应深入理解这类漏洞模式,在设计和实现安全敏感功能时进行全面的威胁建模。

JavaScript沙箱逃逸漏洞分析与利用 1. 漏洞背景 本文详细分析了一个JavaScript沙箱逃逸漏洞,该漏洞存在于 static-eval 库中,允许攻击者绕过沙箱限制执行任意代码。这种漏洞在需要执行用户输入表达式的Web应用中尤其危险。 2. static-eval库简介 static-eval 是一个用于安全评估JavaScript表达式的库,它通过以下方式工作: 使用 esprima 库将表达式解析为AST(抽象语法树) 分析AST结构,确保表达式是安全的 评估通过检查的表达式 虽然文档说明它"不是设计用作沙箱",但许多开发者仍错误地将其用于此目的。 3. 已知漏洞分析 3.1 第一个漏洞:Function构造器利用 漏洞原理 : "".sub 获取String构造器的substring方法(一个函数对象) 访问其 constructor 属性获取Function构造器 使用Function构造器创建新函数并执行 修复方式 : 禁止访问函数对象的属性: 3.2 第二个漏洞:匿名函数利用 漏洞原理 : 创建匿名函数并立即执行 函数体被直接传递给Function构造器 修复方式 : 最初完全禁用匿名函数,后来改为分析函数体内容。 4. 新漏洞发现:参数析构绕过 4.1 漏洞原理 利用ECMAScript的参数析构特性绕过沙箱检查: 关键点 : 使用对象模式(ObjectPattern)参数而非标识符(Identifier) static-eval检查时不会覆盖对象模式参数的变量值 运行时传入函数对象,获取其constructor 4.2 技术细节 AST分析差异 : 普通参数: Identifier 类型 → 被覆盖为null 析构参数: ObjectPattern 类型 → 不被覆盖 执行流程 : 定义时: book 被视为普通对象(允许访问constructor) 调用时:传入 "".sub (函数对象),成功获取Function构造器 4.3 完整利用链 5. 实际应用限制 在 jsonpath 库中使用时遇到限制: jsonpath使用 @ 作为特殊变量名 @ 不是合法ECMAScript标识符 导致函数包装失败,利用链中断 6. 防御建议 不要使用static-eval作为沙箱 :遵循库作者的明确警告 参数检查强化 :应检查所有可能的参数类型,不仅是Identifier 运行时类型验证 :对函数调用时的参数进行额外检查 严格限制可用对象 :使用Proxy或冻结关键对象 7. 漏洞时间线 2019/01/02:漏洞报告提交 2019/01/03:NodeJS安全团队响应 2019/02/14:漏洞正式发布 2019/02/15:static-eval发布修复版本 2019/02/18:README添加免责声明 2019/02/26:修复补丁中的bug 8. 总结 此漏洞展示了JavaScript沙箱实现的复杂性,特别是当语言特性与安全模型交互时。关键教训包括: 参数解析不一致会导致严重安全漏洞 AST分析必须覆盖所有可能的语法结构 运行时行为可能与静态分析时的假设不同 依赖非沙箱设计的库实现沙箱功能是危险的 安全开发人员应深入理解这类漏洞模式,在设计和实现安全敏感功能时进行全面的威胁建模。