
DreamHack - login-1 Web Challenge Write-up
Room / Challenge: login-1 (Web) Metadata Author: jameskaois CTF: DreamHack Challenge: login-1 (web) Link: https://dreamhack.io/wargame/challenges/47 Level: 2 Date: 10-11-2025 Goal Login as the account with admin rights to get the flag. My Solution The app.py has one route that we have to focus on /reset-password: @app.route('/forgot_password', methods=['GET', 'POST']) def forgot_password(): if request.method == 'GET': return render_template('forgot.html') else: userid = request.form.get("userid") newpassword = request.form.get("newpassword") backupCode = request.form.get("backupCode", type=int) conn = get_db() cur = conn.cursor() user = cur.execute('SELECT * FROM user WHERE id = ?', (userid,)).fetchone() if user: # security for brute force Attack. time.sleep(1) if user['resetCount'] == MAXRESETCOUNT: return "<script>alert('reset Count Exceed.');history.back(-1);</script>" if user['backupCode'] == backupCode: newbackupCode = makeBackupcode() updateSQL = "UPDATE user set pw = ?, backupCode = ?, resetCount = 0 where idx = ?" cur.execute(updateSQL, (hashlib.sha256(newpassword.encode()).hexdigest(), newbackupCode, str(user['idx']))) msg = f"<b>Password Change Success.</b><br/>New BackupCode : {newbackupCode}" else: updateSQL = "UPDATE user set resetCount = resetCount+1 where idx = ?" cur.execute(updateSQL, (str(user['idx']))) msg = f"Wrong BackupCode !<br/><b>Left Count : </b> {(MAXRESETCOUNT-1)-user['resetCount']}" conn.commit() return render_template("index.html", msg=msg) return "<script>alert('User Not Found.');history.back(-1);</script>"; Firstly, this code may appears safely without exploitation can be made however the time.sleep(1) is the key where Race Condition come in play. There are 100 possible backup code from 0 -> 100. Therefore, it can be brute-forced. In order to bypass the the max reset count check we can make 100 requests at a time with different backup code. From this we can change the password. ...




