时间是真的紧

源码

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

图片

分类: CTF

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code