[DreamHack] ๋“œ๋ฆผ ๋ณ‘์› ๐ŸŒฑ ํ’€์ด

์ฝ”๋”ฉ ํŒŒ์ธ์• ํ”Œยท2026๋…„ 7์›” 14์ผ

CHALLENGE SOLVING

๋ชฉ๋ก ๋ณด๊ธฐ
4/5
post-thumbnail

์•ˆ๋…•ํ•˜์‹ญ๋‹ˆ๊นŒ ์ฝ”๋”ฉ ํŒŒ์ธ์• ํ”Œ์ž…๋‹ˆ๋‹ค
์ด๋ฒˆ์—๋Š” ๋“œ๋ฆผ ๋ณ‘์› ๐ŸŒฑ์— ๊ด€ํ•ด ํ’€์–ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋“œ๋ฆผ ๋ณ‘์› ๐ŸŒฑ

https://dreamhack.io/wargame/challenges/2814

์ €ํฌ ๋“œ๋ฆผ ๋ณ‘์›์€ ํ™˜์ž ์ •๋ณด๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณดํ˜ธํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๐Ÿฅ
...

import hashlib
import os
from flask import Flask, render_template, request, redirect, url_for, session, abort

app = Flask(__name__)
app.secret_key = os.urandom(24).hex()

health_records = {
    1: {
        "name": "๊น€๊นƒ๋ฐœ",
        "date": "2024-03-10",
        "height": "175cm",
        "weight": "70kg",
        "blood_pressure": "120/80",
        "diagnosis": "ํŠน์ด์‚ฌํ•ญ ์—†์Œ (๊ฒ€์ง„์™„๋ฃŒ)",
        "summary": "DH{This_is_fake_flag}", #โ•ํ”Œ๋ž˜๊ทธ ์œ„์น˜
        "owner": "admin"
    },
    125: {
        "name": "๊น€๋“œ๋ฆผ",
        "date": "2024-03-25",
        "height": "180cm",
        "weight": "75kg",
        "blood_pressure": "115/75",
        "diagnosis": "๋น„ํƒ€๋ฏผ D ๋ถ€์กฑ ๊ถŒ๊ณ ",
        "summary": "์ „๋ฐ˜์ ์œผ๋กœ ์–‘ํ˜ธํ•˜๋ฉฐ ๊พธ์ค€ํ•œ ์šด๋™์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.",
        "owner": "guest"
    }
}

def get_hash(uid):
    return hashlib.md5(str(uid).encode()).hexdigest()

@app.route('/')
def index():
    if 'user' in session:
        return redirect(url_for('dashboard'))
    return render_template('index.html')

@app.route('/auth/login', methods=['POST'])
def login():
    if request.form.get('username') == 'guest' and request.form.get('password') == 'guest':
        session['user'] = 'guest'
        return redirect(url_for('dashboard'))
    return "Login Failed", 401

@app.route('/auth/logout')
def logout():
    session.pop('user', None)
    return redirect(url_for('index'))

@app.route('/dashboard')
def dashboard():
    if 'user' not in session:
        return redirect(url_for('index'))
    
    user_data = health_records[125]
    return render_template('dashboard.html', user_hash=get_hash(125), name=user_data['name'])

@app.route('/report/view/<user_hash>')
def view_report(user_hash):
    if 'user' not in session:
        return redirect(url_for('index'))
    
    for uid, data in health_records.items():
        if get_hash(uid) == user_hash:
            if data['owner'] != session['user']:
                return "<h1>Access Denied</h1>", 403
            return render_template('report.html', data=data, user_hash=user_hash)
            
    return "Not Found", 404

@app.route('/report/download/<user_hash>')
def download_report(user_hash):
    if 'user' not in session:
        return redirect(url_for('index'))
    
    for uid, data in health_records.items():
        if get_hash(uid) == user_hash:
            return render_template('print.html', data=data)
            
    return "Not Found", 404

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)

์ผ๋‹จ guest/guest๋ฅผ ํ†ตํ•ด guest๋กœ ๋กœ๊ทธ์ธ ํ•ด๋ด…์‹œ๋‹ค

์Œ, ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ด…์‹œ๋‹ค

์ฝ”๋“œ๋ฅผ ๋ณด์‹œ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜์‹ฌ์Šค๋Ÿฌ์šด ๋ถ€๋ถ„์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค

def get_hash(uid):
    return hashlib.md5(str(uid).encode()).hexdigest()
    
    
...


@app.route('/report/view/<user_hash>')
def view_report(user_hash):
    if 'user' not in session:
        return redirect(url_for('index'))
    
    for uid, data in health_records.items():
        if get_hash(uid) == user_hash:
            if data['owner'] != session['user']:
                return "<h1>Access Denied</h1>", 403
            return render_template('report.html', data=data, user_hash=user_hash)
            
    return "Not Found", 404

์ด๋ ‡๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™์ด uid๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค MD5 ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ํ•ด์‹ฑํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ 16์ง„์ˆ˜ ๋ฌธ์ž์—ด๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ตฌ๋ฌธ์„ url์— ๋„ฃ์–ด IDOR์ด ๊ฐ€๋Šฅํ•จ์„ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

report/view/3def184ad8f4755ff269862ea77393dd

guest๋กœ ๋กœ๊ทธ์ธํ•˜์‹œ๋ฉด url์ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜์˜ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด๋Š” 125์˜(health_record์˜ guest๋ฒˆํ˜ธ) get_hash() ๊ฐ’์ž„์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๋”ฐ๋ผ์„œ ์ €ํฌ๋Š” owner๊ฐ€ admin์˜ ๋ฒˆํ˜ธ๊ฐ€ ๋˜๋Š” 1์„ get_hash()์— ๋„ฃ์–ด IDOR๋ฅผ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด๊ฑธ ๊ณง์ด๊ณง๋Œ€๋กœ /report/view/<user_hash> ์— ๋„ฃ๊ฒŒ ๋˜๋ฉด ์ฝ”๋“œ์ฒ˜๋Ÿผ 'Access Denied'๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค

๊ฒฐ๊ณผํ‘œ๊ฐ€ ๋˜๋Š” /report/download/<user_hash> ๋Š” ์ข€ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค

@app.route('/report/download/<user_hash>')
def download_report(user_hash):
    if 'user' not in session:
        return redirect(url_for('index'))
    
    for uid, data in health_records.items():
        if get_hash(uid) == user_hash:
            return render_template('print.html', data=data)
            
    return "Not Found", 404

/report/view/<user_hash>์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ๋กœ๊ทธ์ธ ํ™•์ธ ํ›„, ์ฐพ์€ ๋ฐ์ดํ„ฐ์˜ ์ฃผ์ธ๊ณผ ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ์ผ์น˜ํ•˜๋Š”์ง€ ๋น„๊ตํ•˜์ง€ ์•Š๊ธฐ์—
(data['owner'] != session['user'])

/report/view/c4ca4238a0b923820dcc509a6f75849b
์„ ํ†ตํ•ด ์ ‘๊ทผํ•˜๋ฉด ํ”Œ๋ž˜๊ทธ๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

FLAG :

ํ‰๊ฐ€ :

profile
์•ˆ๋…•ํ•˜๋–ผ์š”์ทจ๋ฏธ๋กœ์ฝคํ‘ธํƒ€๋ฐฐ์šฐ๋Š”๊ณ ๋“ ํ•™๊ต6ํ•™๋…„์œ ์น˜์›์ƒ์ด๋นˆ๋‹ค์ฃผ๋ง์žฌ์™ธํ•˜๊ณ (๋Ÿฌ๋ธ”๋Ÿญ์Šคํ•ด์•ผ๋Œ€์š”๐Ÿ’ข๐Ÿ’ข)์ฃผ์ค‘์—ฐ์žฌํ•˜๊ณ ์•ˆํ•œ๋‚ ์• ๋ˆˆ์ด์›”(February)๋จ๋‹ˆ๋‹ค

0๊ฐœ์˜ ๋Œ“๊ธ€