2020天翼杯--APITest

时间是真的紧

源码

const express = require("express"); 
const cors = require("cors"); 
const app = express(); 
const uuidv4 = require("uuid/v4"); 
const md5 = require("md5"); 
const jwt = require("express-jwt"); 
const jsonwebtoken = require("jsonwebtoken"); 
const server = require("http").createServer(app); 
const { flag, secret, jwtSecret } = require("./flag"); 
const config = { 
    port: process.env.PORT || 8081, 
    adminValue: 1000, 
    message: "Can you get flag?", 
    secret: secret, 
    adminUsername: "kirakira_dokidoki", 
    whitelist: ["/", "/login", "/init", "/source"], 
}; 
let users = { 
    0: { 
        username: config.adminUsername, 
        isAdmin: true, 
        rights: Object.keys(config) 
    } 
}; 
app.use(express.json()); 
app.use(cors()); 
app.use( 
    jwt({ secret: jwtSecret }).unless({ 
        path: config.whitelist 
    }) 
); 
app.use(function(error, req, res, next) { 
    if (error.name === "UnauthorizedError") { 
        res.json(err("Invalid token or not logged in.")); 
    } 
}); 
function sign(o) { 
    return jsonwebtoken.sign(o, jwtSecret); 
} 
function ok(data = {}) { 
    return { status: "ok", data: data }; 
} 
function err(msg = "Something went wrong.") { 
    return { status: "error", message: msg }; 
} 
function isValidUser(u) { 
    return ( 
        u.username.length >= 6 && 
        u.username.toUpperCase() !== config.adminUsername.toUpperCase() && u.username.toUpperCase() !== config.adminUsername.toLowerCase() 
    ); 
} 
function isAdmin(u) { 
    return (u.username.toUpperCase() === config.adminUsername.toUpperCase() && u.username.toUpperCase() === config.adminUsername.toLowerCase()) || u.isAdmin; 
} 
function checkRights(arr) { 
    let blacklist = ["secret", "port"]; 
    if(blacklist.includes(arr)) { 
        return false; 
    } 
    for (let i = 0; i < arr.length; i++) { 
        const element = arr[i]; 
        if (blacklist.includes(element)) { 
            return false; 
        } 
    } 
    return true; 
} 
app.get("/", (req, res) => { 
    res.json(ok({ hint:  "You can get source code from /source"})); 
}); 
app.get("/source", (req, res) => { 
    res.sendFile( __dirname + "/" + "app.js"); 
}); 
app.post("/login", (req, res) => { 
    let u = { 
        username: req.body.username, 
        id: uuidv4(), 
        value: Math.random() < 0.0000001 ? 100000000 : 100, 
        isAdmin: false, 
        rights: [ 
            "message", 
            "adminUsername" 
        ] 
    }; 
    if (isValidUser(u)) { 
        users[u.id] = u; 
        res.send(ok({ token: sign({ id: u.id }) })); 
    } else { 
        res.json(err("Invalid creds")); 
    } 
}); 
app.post("/init", (req, res) => { 
    let { secret } = req.body; 
    let target = md5(config.secret.toString());//config 
    let adminId = md5(secret) 
        .split("") 
        .map((c, i) => c.charCodeAt(0) ^ target.charCodeAt(i)) 
        .reduce((a, b) => a + b); 
    res.json(ok({ token: sign({ id: adminId }) })); 
}); 

// Get server info
app.get("/serverInfo", (req, res) => {
let user = users[req.user.id] || { rights: [] };
let info = user.rights.map(i => ({ name: i, value: config[i] }));
res.json(ok({ info: info }));
});
app.post("/becomeAdmin", (req, res) => {
let {value} = req.body;
let uid = req.user.id;
let user = users[uid];
let maxValue = [value, config.adminValue].sort()[1];//0x30
if(value >= maxValue && user.value >= value) {
user.isAdmin = true;
res.send(ok({ isAdmin: true }));
}else{
res.json(err("You need pay more!"));
}
});
// only admin can update user
app.post("/updateUser", (req, res) => {
let uid = req.user.id;
let user = users[uid];
if (!user || !isAdmin(user)) {
res.json(err("You're not an admin!"));
return;
}
let rights = req.body.rights || [];
if (rights.length > 0 && checkRights(rights)) {
users[uid].rights = user.rights.concat(rights).filter((value, index, self)=>{
return self.indexOf(value) === index;
});
}
res.json(ok({ user: users[uid] }));
});
// only uid===0 can get the flag
app.get("/flag", (req, res) => {
if (req.user.id == 0) {
res.send(ok({ flag: flag }));
} else {
res.send(err("Unauthorized"));
}
});
server.listen(config.port, () =>
console.log(Server listening on port ${config.port}!)
);

/login路由用来登陆获取token,不过这里用户验证严格不能直接成为admin
/init 输入secret,并和config中的secret做异或操作,并赋值给id

/flag id=0即输出flag

/serverInfo用来输出用户信息,包括权限等,但是有waf不能查看secret

/updateUser用来向用户添加权限

/becomeAdmin输入value,满足条件就成为admin

思路:先随便用个名字登陆,拿token

图片

然后在becomeAdmin出传value=30,sort排序后会令maxvalue=30,绕过验证成为admin

图片

成为admin之后去updateUser添加secret权限

这里的rights可用如下来绕

{"rights": [["secret"]]} 

图片

serverInfo中使用的是config[i]

图片

如下依然能获取到值

图片

而 这样check函数中得到的就是[secret]

现在再去serverInfo就会看到config中secret的值了

图片

此时在init中我们只要让secret等于这个value,就能让异或结果为0,达到让用户id=0

图片

查看flag

图片

posted @ 2020-08-17 21:14  W4nder  阅读(62)  评论(0编辑  收藏