时间是真的紧
源码
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