でもguestから始めよう!
(抜粋)
function auth(req, res, next) {
const username = req.cookies.username;
if (!username) return res.redirect("/login");
const user = users.get(username);
if (!user) return res.redirect("/login");
req.user = user;
next();
}
~~
app.post("/register", (req, res) => {
const user_data = req.body;
if (
!user_data ||
!user_data.username ||
!user_data.password ||
!/^[a-z0-9]+$/.test(user_data.username) ||
(user_data.nickname && !/^[a-z0-9]+$/.test(user_data.nickname))
)
return res.send("invalid input");
if (users.has(user_data.username)) {
return res.send("user already exists");
}
users.set(user_data.username, {
role: "guest",
...user_data,
});
res.cookie("username", user_data.username);
res.redirect("/");
});
~~
app.post("/login", (req, res) => {
const { username, password } = req.body;
const user = users.get(username);
if (!user || user.password !== password) {
return res.send("invalid credentials");
}
res.cookie("username", username);
res.redirect("/");
});
app.get("/", auth, (req, res) => {
const { username, role, nickname } = req.user;
res.send(`
<h1>Hello ${username} (${nickname ?? "no nickname"})</h1>
<p>role: ${role}</p>
${role === "admin" ? `<p><b>${FLAG}</b></p>` : ""}
<a href="/logout">Logout</a>
`);
});
まずは次の入力検証をします。
user_data が存在するかusername が空じゃないかpassword が空じゃないかusername が 小文字英数字のみか(^[a-z0-9]+$)nickname は任意だけど、入れるなら 小文字英数字のみかその後、ユーザを生成します
users.set(user_data.username, {
role: "guest",
...user_data,
});
ここの処理に脆弱性があります。
フォームからの正規の入力では user_dataは usernameと passwordで構成されます。
しかし、ここに role を仕込むことで role: "guest" 部分を上書きできます。(後勝ちする仕様のため)
よって、 role=adminを含むPOSTを投げ、
登録ユーザ名のクッキーをセットし /を開けばフラグが表示されます