๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๐Ÿด‍โ˜ ๏ธ CTF ๐Ÿด‍โ˜ ๏ธ/โœˆ๏ธ ์›น โœˆ๏ธ

[Dream Hack - Web] csrf-2

๋ฐ˜์‘ํ˜•

๋ฌธ์ œ ํ•ด์„

/ ํŽ˜์ด์ง€

@app.route("/")
def index():
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')
    return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not an admin"}')

์ฟ ํ‚ค์—์„œ session id๋ฅผ ํ™•์ธํ•˜๊ณ  ๊ทธ ๊ฐ’์— ๋”ฐ๋ผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ถœ๋ ฅํ•œ๋‹ค. None์ธ๊ฒฝ์šฐ → “please login” guest์ธ ๊ฒฝ์šฐ → “you are not an admin” admin์ธ ๊ฒฝ์šฐ → "flag is " + FLAG

 

 

/login ํŽ˜์ด์ง€

users = {
    'guest': 'guest',
    'admin': FLAG
}

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    elif request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        try:
            pw = users[username]
        except:
            return '<script>alert("not found user");history.go(-1);</script>'
        if pw == password:
            resp = make_response(redirect(url_for('index')) )
            session_id = os.urandom(8).hex()
            session_storage[session_id] = username
            resp.set_cookie('sessionid', session_id)
            return resp 
        return '<script>alert("wrong password");history.go(-1);</script>'

์ผ๋ฐ˜์ ์ธ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์ด๋‹ค. ๋กœ๊ทธ์ธ์— ์„ฑ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ, session_id๋ฅผ ์ฟ ํ‚ค์— ์ €์žฅํ•œ๋‹ค.

 

 

/flag ํŽ˜์ด์ง€

@app.route("/flag", methods=["GET", "POST"])
def flag():
    if request.method == "GET":
        return render_template("flag.html")
    elif request.method == "POST":
        param = request.form.get("param", "")
        session_id = os.urandom(16).hex()
        session_storage[session_id] = 'admin'
        if not check_csrf(param, {"name":"sessionid", "value": session_id}):
            return '<script>alert("wrong??");history.go(-1);</script>'
        return '<script>alert("good");history.go(-1);</script>'

GET /flag ์ธ ๊ฒฝ์šฐ ์„œ๋ฒ„ ๋กœ์ปฌ ํ˜ธ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ /vuln ํŽ˜์ด์ง€์— param์„ ๋„ฃ์–ด ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ž…๋ ฅ์ฐฝ์ด ๋ณด์ธ๋‹ค.

POST /flag์ธ ๊ฒฝ์šฐ ์ž…๋ ฅ์ฐฝ์„ ‘์ œ์ถœ’ํ–ˆ์„ ๋•Œ ๋™์ž‘ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค. ‘admin’์— ํ•ด๋‹นํ•˜๋Š” session_id(๋žจ๋˜)๊ฐ’์ด ๋งŒ๋“ค์–ด์ ธ {session_id, ‘admin’}์œผ๋กœ session_storage์— ์ €์žฅ๋œ๋‹ค. ๋˜, check_csrf ์—์„œ ์ฟ ํ‚ค ๊ฐ’์— session_id๋ฅผ ์ €์žฅํ•œ๋‹ค.

 

 

/change_password API

@app.route("/change_password")
def change_password():
    pw = request.args.get("pw", "")
    session_id = request.cookies.get('sessionid', None)
    try:
        username = session_storage[session_id]
    except KeyError:
        return render_template('index.html', text='please login')

    users[username] = pw
    return 'Done'

ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ ๋˜๋ฉด, session_storeage์—์„œ session_id๋ฅผ ํ†ตํ•ด username์„ ํ™•์ธํ•œ๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์€ pw ๊ฐ’์„ ์‚ฌ์šฉํ•ด์„œ username์— ํ•ด๋‹นํ•˜๋Š” user์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค.

 

ํ’€์ด ๋ฐฉ๋ฒ•

๋Œ€๋žต์ ์œผ๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ณ€๊ฒฝํ•ด์„œ admin์œผ๋กœ ๋กœ๊ทธ์ธํ•ด FLAG๋ฅผ ํ™•์ธํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

  1. POST /flag ์š”์ฒญ์œผ๋กœ ํ†ตํ•ด, session_storage์— {session_id, ‘admin’}๋ฅผ ์ €์žฅํ•œ๋‹ค. ์ฟ ํ‚ค์— {”session_id”, session_id}๋ฅผ ์ €์žฅํ•œ๋‹ค.
  2. /change_password ํ˜ธ์ถœ์„ ํ†ตํ•ด, ์ฟ ํ‚ค์—์„œ session_id ์กฐํšŒํ•œ๋‹ค. ์กฐํšŒํ•œ session_id๋กœ session_storage์—์„œ ‘admin’์„ ๊ฐ€์ ธ์™€ user[’admin’]์— ์ƒˆ๋กœ์šด pw๋ฅผ ์—…๋ฐ์ดํŠธ ํ•œ๋‹ค.

<img src="/change_password?pw=admin"/>

 

 

3. /login ํŽ˜์ด์ง€์—์„œ admin ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•œ ํ›„, / ํŽ˜์ด์ง€์—์„œ FLAG๋ฅผ ํ™•์ธํ•œ๋‹ค.

๋ฐ˜์‘ํ˜•