can you login as admin?
NOTE: no bruteforcing is required for this challenge! please do not bruteforce the challenge.
const app = express();
const db = require('better-sqlite3')('db.sqlite3');
db.exec(`DROP TABLE IF EXISTS users;`);
db.exec(`CREATE TABLE users(
id INTEGER PRIMARY KEY,
username TEXT,
password TEXT
);`);
const FLAG = process.env.FLAG || "dice{test_flag}";
const PORT = process.env.PORT || 3000;
const users = [...Array(100_000)].map(() => ({
user: `user-${crypto.randomUUID()}`,
pass: crypto.randomBytes(8).toString("hex")
}));
db.exec(`INSERT INTO users (id, username, password) VALUES ${
users.map((u,i) => `(${i}, '${u.user}', '${u.pass}')`).join(", ")
}`);
const isAdmin = {};
const newAdmin = users[Math.floor(Math.random() * users.length)];
isAdmin[newAdmin.user] = true;
문제에서 제공된 코드를 보면 특이한 방식으로 admin을 구성한다.
1. 100,000개의 user-X를 만든다.
2. 랜덤하게 1명의 user를 뽑는다.
3. isAdmin 리스트에 뽑힌 user를 추가한다.
위 과정을 거쳐 최종적으로 isAdmin 리스트에 딱 1명의 user가 존재하게 된다.
app.post("/api/login", (req, res) => {
const { user, pass } = req.body;
const query = `SELECT id FROM users WHERE username = '${user}' AND password = '${pass}';`;
try {
const id = db.prepare(query).get()?.id;
if (!id) {
return res.redirect("/?message=Incorrect username or password");
}
if (users[id] && isAdmin[user]) {
return res.redirect("/?flag=" + encodeURIComponent(FLAG));
}
return res.redirect("/?message=This system is currently only available to admins...");
}
catch {
return res.redirect("/?message=Nice try...");
}
});
/api/login 로직을 살펴보면 SQL injection에 취약함을 알 수 있다.
db.prepare라는 sql injeciton으로부터 보호할 수 있는 메소드를 사용하고는 있지만, 이미 사용자의 input을 query 문자열에 대입하여 완성된 query를 이용해 prepare()를 사용했기 때문에 취약하다.
그러나 누가 admin인지 알 수 없으므로, 100,000번의 시도를 해야 하는 것 처럼 보인다.
if (users[id] && isAdmin[user]) {
return res.redirect("/?flag=" + encodeURIComponent(FLAG));
}
`id`는 db의 result다. `user`는 http body로 넘긴 client request다.
users[id]는 존재해야하며, isAdmin[user]도 존재해야 한다.
id : __proto__
pw : ' union select 1 --
위 페이로드를 통해 공격하면 조건을 통과할 수 있다.
id는 `isAdmin[user]`에 사용되기 때문에 injection payload를 넣으면 안된다.
pw를 통해 sql injeciton 공격을 진행하고, id에는 __proto__를 넣어서 isAdmin[user]가 참이 되도록 만들어주면 로그인에 성공한다.
댓글
댓글 쓰기