Bài password disclosure nói về lỗi bảo mật khi 1 endpoint expose username
parameter. Mà application lại không có verify user có phải là loggedin user không.
Khi bạn mở challenge lên, phần debug sẽ cho bạn xem source code. Nhìn sơ qua, chúng ta thấy có chỗ setup database cho bài:
conn.execute("INSERT INTO users (username, password, description) VALUES (?, ?, ?)",
('admin', get_random_string(30), f"{FLAG}"))
conn.execute("INSERT INTO users (username, password) VALUES (?, ?)", ('dog', "bowwow"))
conn.execute("INSERT INTO users (username, password) VALUES (?, ?)", ('cat', 'meow'))
conn.execute("INSERT INTO users (username, password) VALUES (?, ?)", ('guest', 'guest'))
Cái chúng ta muốn lấy ở đây là password của admin, là 1 string 30 mẫu tự ngẫu nhiên. Vì chúng ta không thể đoán được 30 mẫu tự ngẫu nhiên đó là gì, chúng ta check tiếp xem những endpoint nào khác có trong code mà chúng ta có thể expose: login
, account
, reset_password
. Trong reset_password
, chúng ta thấy cho request method GET
, request argument có nhận username
.
def reset_password():
if request.method == "GET":
username = request.args.get("username", session.get("username"))
if not username:
return redirect("/login")
conn = get_db()
user = conn.execute(
"""
SELECT *
FROM users
WHERE username = ?
""",
(username,)
).fetchone()
Vậy là chúng ta chỉ cần tạo cái path mà pass in route reset_password
và username
parameter. Ví dụ:
http://123.45.678.91:01200/reset_password?username=admin
Là chúng ta thấy được:
1 vấn đề bảo mật của hệ thống này nữa là password được return to client dạng plain text! Vì vậy chúng ta chỉ cần inspect element là thấy được password plain text của admin.
<input class="form-control" value="iqezkxwuqqkevktxwpclbxmzwtslmw" type="password" id="new-password" name="new_password">
Chúng ta chỉ cần copy password này vào trang login, username là admin
, là chúng ta có thể thấy được flag.