๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
[Dreamhack]WebHacking/Wargame&CTF

[Dreamhack] Level2: Dream Gallery

by Yun2๐Ÿ‘ 2024. 2. 3.
๋ฐ˜์‘ํ˜•

๐Ÿ›Ž๏ธ Access

๋“œ๋ฆผ์ด๋Š” ๊ฐค๋Ÿฌ๋ฆฌ ์‚ฌ์ดํŠธ๋ฅผ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฐ๋ฐ ์™ธ๋ถ€๋กœ ์š”์ฒญํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์•ˆ์ „ํ•œ ๊ฑด์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค๊ณ  ํ•˜๋„ค์š”...
๊ฐค๋Ÿฌ๋ฆฌ ์‚ฌ์ดํŠธ์—์„œ ์ทจ์•ฝ์ ์„ ์ฐพ๊ณ  flag๋ฅผ ํš๋“ํ•˜์„ธ์š”!
flag๋Š” /flag.txt์— ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

๐Ÿ‘พ Exploit Algorithm & Payload

> app.py

๋”๋ณด๊ธฐ
from flask import Flask, request, render_template, url_for, redirect
from urllib.request import urlopen
import base64, os

app = Flask(__name__)
app.secret_key = os.urandom(32)

mini_database = []


@app.route('/')
def index():
    return redirect(url_for('view'))


@app.route('/request')
def url_request():
    url = request.args.get('url', '').lower()
    title = request.args.get('title', '')
    if url == '' or url.startswith("file://") or "flag" in url or title == '':
        return render_template('request.html')

    try:
        data = urlopen(url).read()
        mini_database.append({title: base64.b64encode(data).decode('utf-8')})
        return redirect(url_for('view'))
    except:
        return render_template("request.html")


@app.route('/view')
def view():
    return render_template('view.html', img_list=mini_database)


@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        f = request.files['file']
        title = request.form.get('title', '')
        if not f or title == '':
            return render_template('upload.html')

        en_data = base64.b64encode(f.read()).decode('utf-8')
        mini_database.append({title: en_data})
        return redirect(url_for('view'))
    else:
        return render_template('upload.html')


if __name__ == "__main__":
    img_list = [
        {'์ดˆ๋ก์ƒ‰ ์„ ๊ธ€๋ผ์Šค': "static/assetA#03.jpg"}, 
        {'๋ถ„ํ™์ƒ‰ ์„ ๊ธ€๋ผ์Šค': "static/assetB#03.jpg"},
        {'๋ณด๋ผ์ƒ‰ ์„ ๊ธ€๋ผ์Šค': "static/assetC#03.jpg"}, 
        {'ํŒŒ๋ž€์ƒ‰ ์„ ๊ธ€๋ผ์Šค': "static/assetD#03.jpg"}
    ]
    for img in img_list:
        for k, v in img.items():
            data = open(v, 'rb').read()
            mini_database.append({k: base64.b64encode(data).decode('utf-8')})
    
    app.run(host="0.0.0.0", port=80, debug=False)

 

 

#1


: '/' ํŽ˜์ด์ง€๋Š” '/view' ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰์…˜๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

: '/view' ํŽ˜์ด์ง€์—์„œ๋Š” ์‚ฌ์ง„์ด 4๊ฐ€์ง€ ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

: '/request' ํŽ˜์ด์ง€๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” URL, ์ด๋ฏธ์ง€ ์ œ๋ชฉ์ด๋ผ๋Š” ๊ฒƒ์„ ํ™œ์šฉํ•˜๋ฉด ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ์„ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๋‹ค.

: ํ•ด๋‹น ๊ฐ’์„ ์ž…๋ ฅํ•˜๋ฉด GETํ˜•ํƒœ๋กœ /request?url=&title= ์ด๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ๋™์ž‘ํ•จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

: ์ถ”๊ฐ€๋กœ ์ฝ”๋“œ๋ฅผ ์‚ดํˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ URL์˜ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์™€ base64๋กœ ์ธ์ฝ”๋”ฉํ•˜๊ณ  mini_database์— ์ €์žฅํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋‹จ, URL์ด ๋น„์–ด์žˆ๊ฑฐ๋‚˜, "file://"๋กœ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜, "flag"๋ผ๋Š” ๋ฌธ์ž์—ด์ด ํฌํ•จ๋œ ๊ฒฝ์šฐ๋Š” ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

: '/upload' ํŽ˜์ด์ง€๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๋ฉด ํ•ด๋‹น ํŒŒ์ผ์ด ์—…๋กœ๋“œ๋จ์„ ์œ ์ถ”ํ•  ์ˆ˜ ์žˆ๋‹ค.

: ์ถ”๊ฐ€๋กœ ์ฝ”๋“œ๋ฅผ ์‚ดํˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•˜๋ฉด ํ•ด๋‹น ํŒŒ์ผ์˜ ๋‚ด์šฉ์„  base64๋กœ ์ธ์ฝ”๋”ฉํ•˜๊ณ  mini_database์— ์ €์žฅํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  '/view' ํŽ˜์ด์ง€์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

: '/request' ๊ฒฝ๋กœ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ URL์„ ์ด์šฉํ•˜์—ฌ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋Š”๋ฐ, ์ด๋•Œ ์‚ฌ์šฉ์ž๊ฐ€ ์„œ๋ฒ„ ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ์˜ URL์„ ์ž…๋ ฅํ•˜๋ฉฐ, ์„œ๋ฒ„๊ฐ€ ๊ทธ URL๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜์–ด์„œ ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ์— ์ ‘๊ทผํ•˜๊ฒŒ ๋  ์ˆ˜ ์žˆ๋‹ค.

: ์ด๋Š” SSRF ์ทจ์•ฝ์ ์„ ์œ ๋ฐœํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋œ๋‹ค.

: ์ถ”๊ฐ€๋กœ ์ฝ”๋“œ์—์„œ URL์ด "file://'๋กœ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ '/flag' ๋ฌธ์ž์—ด์ด ์žˆ์„ ๊ฒฝ์šฐ ๊ณต๋ฐฑ์ฒ˜๋ฆฌ ํ–ˆ์ง€๋งŒ ์ถฉ๋ถ„ํžˆ ์šฐํšŒํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์œผ๋กœ ํŒ๋‹จ๋œ๋‹ค.

 

 

#2


: ์šฐ์„  ๊ฐค๋Ÿฌ๋ฆฌ ๋ทฐ์–ด์—์„œ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ(F12)๋กœ ํ•ด๋‹น ์‚ฌ์ง„ ๋‚ด์šฉ์„ ํ™•์ธํ–ˆ์„ ๋•Œ, base64๋กœ ์ธ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

:

: ์ค€๋น„๋˜์–ด ์žˆ๋˜ cmd ํŒŒ์ผ์„ ์—…๋กœ๋“œ ํ•ด๋ดค๋”๋‹ˆ ๊ฐค๋Ÿฌ๋ฆฌ ๋ทฐ์–ด์—์„œ ํŒŒ์ผ ๋‚ด์šฉ์ด encoding๋œ ๊ฐ’์œผ๋กœ ํ™•์ธ ๊ฐ€๋Šฅํ•˜๋‹ค.

: ์ด๋ฅผ ๋””์ฝ”๋”ฉ ์‹œ์ผฐ๋”๋‹ˆ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํŒŒ์ผ ๋‚ด์šฉ์ด ๊ทธ๋Œ€๋กœ ๋“œ๋Ÿฌ๋‚ฌ๋‹ค.

 

 

#3


: ์œ„ ๊ฒฐ๊ณผ๋“ค์„ ์ข…ํ•ฉ์ ์œผ๋กœ ๋ถ„์„ํ–ˆ์„๋•Œ, ์„œ๋ฒ„ ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ์˜ URL์„ ์ž…๋ ฅํ•˜๊ณ  ์„œ๋ฒ„๊ฐ€ ๊ทธ URL๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๊ฒŒ ๋˜์–ด์„œ ๋‚ด๋ถ€์˜ ํŒŒ์ผ์ธ /flag.txt๋ฅผ ์š”์ฒญํ•˜๊ณ  base64์ธ์ฝ”๋”ฉ๋œ ํŒŒ์ผ ๋‚ด์šฉ์„ ๋””์ฝ”๋”ฉํ•˜๋ฉด FLAG๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

file:// -> file:/// , file:///, ...
flag -> fl%2561g, fla%2567, ...

/request?url=file:/fla%2567.txt&title=flag1
...

: ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์šฐํšŒํ•ด์„œ GET ๋ฐฉ์‹์œผ๋กœ ์ „์†กํ•ด์ค€๋‹ค.

 

 

๐Ÿ”‘Analysis and results for obtaining the Flag DH{…}


flag.txt ์ธ์ฝ”๋”ฉ ํŒŒ์ผ ๋‚ด์šฉ
flag.txt ๋””์ฝ”๋”ฉ๋œ ํŒŒ์ผ ๋‚ด์šฉ

: ๊ฐค๋Ÿฌ๋ฆฌ ๋ทฐ์–ด์—์„œ ์ธ์ฝ”๋”ฉ๋œ ํŒŒ์ผ ๋‚ด์šฉ์„ ํ™•์ธํ•˜๊ณ  ๋””์ฝ”๋”ฉํ•˜๋ฉด FLAG๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

๐Ÿ“ŒSummary


 SSRF(Server-Side Rquest Forgery)๋Š” ๊ณต๊ฒฉ์ž๊ฐ€ ์„œ๋ฒ„๋ฅผ ์กฐ์ž‘ํ•˜์—ฌ ๋‚ด๋ถ€ ์‹œ์Šคํ…œ์— ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ณต๊ฒฉ. ๊ณต๊ฒฉ์ž๋Š” ์ด๋ฅผ ํ†ตํ•ด ๋‚ด๋ถ€ ์‹œ์Šคํ…œ์— ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜, ๋‚ด๋ถ€ ์‹œ์Šคํ…œ์˜ ์ •๋ณด๋ฅผ ๋…ธ์ถœ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Œ.

 

-๋Œ€์‘๋ฐฉ์•ˆ-

- ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๊ฐ’ ๊ฒ€์ฆ

- ์ถœ๋ ฅ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

- ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ์— ๋Œ€ํ•œ ์š”์ฒญ ์ฐจ๋‹จ

- ์ตœ์†Œ ๊ถŒํ•œ ์›์น™ ์ ์šฉ

...

๋ฐ˜์‘ํ˜•

'[Dreamhack]WebHacking > Wargame&CTF' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Dreamhack] Level4: KeyCat  (0) 2024.02.23
[Dreamhack] Level1:Beginner blue-whale  (1) 2024.02.07
[Dreamhack] Level2: filestorage  (0) 2024.02.02
[Dreamhack] Level1: Type c-j  (2) 2024.02.02
[Dreamhack] Level1: baby-union  (2) 2024.02.02