[๐ŸŒฑ Sprout] GETยทPOST ์š”์ฒญ ์ฒด์ด๋‹ โ€” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ํ† ํฐ ์ด์–ด ๋ถ™์ด๊ธฐ โ€” DreamHack simple-web-request ํ’€์ด

2026-06-24ยท1๋ถ„ ์ฝ๊ธฐยท

[๐ŸŒฑ Sprout] GETยทPOST ์š”์ฒญ ์ฒด์ด๋‹ โ€” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ํ† ํฐ ์ด์–ด ๋ถ™์ด๊ธฐ โ€” DreamHack simple-web-request ํ’€์ด

STEP1 โ†’ STEP2 โ†’ FLAG ์„ธ ๋‹จ๊ณ„๋ฅผ ์ •ํ•ด์ง„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฐจ๋ก€๋กœ ํ˜ธ์ถœํ•ด์•ผ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋‚˜์˜ค๋Š” ์š”์ฒญ ์ž…๋ฌธ ๋ฌธ์ œ๋‹ค. ๊ฐ ๋‹จ๊ณ„๋Š” ์•ž ๋‹จ๊ณ„๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ๋‚œ์ˆ˜ ํ† ํฐ์„ ๋“ค๊ณ  ์™€์•ผ ํ†ต๊ณผํ•˜๊ณ , ๊ทธ ํ† ํฐ์€ ํ•œ ์š”์ฒญ ํ๋ฆ„ ์•ˆ์—์„œ๋งŒ ์œ ํšจํ•˜๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋กœ ํ•œ ๋ฒˆ, ๊ทธ๋ฆฌ๊ณ  requests.Session ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ์ž๋™ ์ถ”์ ํ•˜๋ฉฐ hidden ํ† ํฐ์„ ์ด์–ด ๋ถ™์—ฌ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ›์•˜๋‹ค.

๋ฌธ์ œ: DreamHack โ€” simple-web-request ๋ถ„๋ฅ˜: Web ๋‚œ์ด๋„: ๐ŸŒฑ Sprout FLAG: DH{c46b414ddba26adfa4606c59655bda0a18fbe260606b042b52d865e0160eea0e}

์ด๋ฆ„ ๊ทธ๋Œ€๋กœ "๊ฐ„๋‹จํ•œ ์›น ์š”์ฒญ" ๋ฌธ์ œ๋‹ค. ํ™”๋ฉด์—” STEP 1 / STEP 2 / FLAG ๋งํฌ ์„ธ ๊ฐœ๋งŒ ์žˆ๊ณ , ์ •ํ•ด์ง„ ์ˆœ์„œ๋Œ€๋กœ ์ •ํ•ด์ง„ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‹ค์–ด ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋‚˜์˜จ๋‹ค. HTTP ๋ฉ”์„œ๋“œ(GETยทPOST)์™€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์†์œผ๋กœ ๋‹ค๋ค„ ๋ณด๋Š” ๊ฒŒ ๋ชฉ์ ์ธ ์ž…๋ฌธ ๋ฌธ์ œ๋ผ, ์ฝ”๋“œ๋„ ์งง๊ณ  ์šฐํšŒ๋ž„ ๊ฒƒ๋„ ๊ฑฐ์˜ ์—†๋‹ค. ๋‹ค๋งŒ ๋‹จ๊ณ„ ์‚ฌ์ด๋ฅผ ์ž‡๋Š” ๋‚œ์ˆ˜ ํ† ํฐ ํ•˜๋‚˜๊ฐ€ ์žˆ์–ด์„œ, ๊ทธ๊ฑธ ์–ด๋–ป๊ฒŒ ์ด์–ด ๋ถ™์ด๋Š”์ง€๊ฐ€ ์ „๋ถ€๋‹ค.

๋ฉ”์ธ ํŽ˜์ด์ง€ โ€” STEP 1 / STEP 2 / FLAG ๋งํฌ ์„ธ ๊ฐœ
๋ฉ”์ธ ํŽ˜์ด์ง€ โ€” STEP 1 / STEP 2 / FLAG ๋งํฌ ์„ธ ๊ฐœ

๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…simple-web-request
๋‚œ์ด๋„๐ŸŒฑ Sprout
๋ถ„๋ฅ˜Web (HTTP ์š”์ฒญ)
์ŠคํƒPython + Flask
์ œ๊ณตapp.py + ์„œ๋ฒ„
ํ•ต์‹ฌ์„ธ ๋‹จ๊ณ„ GETยทPOST ์ฒด์ธ + ๋‹จ๊ณ„๋งˆ๋‹ค ์•ž ๋‹จ๊ณ„๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ๋‚œ์ˆ˜ ํ† ํฐ ๊ฒ€์ฆ

ํ’€์ด ํ๋ฆ„์€ ๋‹จ์ˆœํ•˜๋‹ค. /step1 ์— ์˜ฌ๋ฐ”๋ฅธ GET ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ฃผ๋ฉด /step2 ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋˜๊ณ , /step2 ๋Š” HTML ์•ˆ์— hidden ํ† ํฐ์„ ๋ฐ•์•„ ๋‚ด๋ ค์ค€๋‹ค. ๊ทธ ํ† ํฐ์„ ๊ทธ๋Œ€๋กœ /flag ์— POST๋กœ ๋‹ค์‹œ ์˜ฌ๋ฆฌ๋ฉด ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋–จ์–ด์ง„๋‹ค. ํ† ํฐ์ด ํ•œ ํ๋ฆ„ ์•ˆ์—์„œ๋งŒ ์œ ํšจํ•˜๋‹ค๋Š” ์ ๋งŒ ์ฃผ์˜ํ•˜๋ฉด ๋œ๋‹ค.



๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” GETยทPOST ์™€ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ, ๊ทธ๋ฆฌ๊ณ  ์„ธ์…˜

์ด ๋ฌธ์ œ๋ฅผ ํ’€๋ ค๋ฉด HTTP ์š”์ฒญ์˜ ๊ธฐ๋ณธ ์„ธ ๊ฐ€์ง€๋ฅผ ๊ตฌ๋ถ„ํ•  ์ค„ ์•Œ์•„์•ผ ํ•œ๋‹ค.

GET ๊ณผ POST. ๋‘˜ ๋‹ค ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋ฉ”์„œ๋“œ๋‹ค. GET์€ ๋ณดํ†ต ๋ฐ์ดํ„ฐ๋ฅผ URL ์ฟผ๋ฆฌ์ŠคํŠธ๋ง(?param=๊ฐ’)์— ๋‹ด์•„ "์กฐํšŒ"ํ•  ๋•Œ, POST๋Š” ๋ณธ๋ฌธ(body)์— ๋‹ด์•„ "์ œ์ถœ"ํ•  ๋•Œ ์“ด๋‹ค. Flask์—์„œ๋Š” request.args.get() ์œผ๋กœ GET ์ฟผ๋ฆฌ๋ฅผ, request.form.get() ์œผ๋กœ POST ํผ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๋Š”๋‹ค. ์ด ๋ฌธ์ œ๋Š” ๋‹จ๊ณ„๋งˆ๋‹ค ๋‘˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ •ํ™•ํžˆ ๋งž์ถฐ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค.

302 ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ. ์„œ๋ฒ„๊ฐ€ 302 ์‘๋‹ต์— Location ํ—ค๋”๋ฅผ ์‹ค์–ด ๋ณด๋‚ด๋ฉด, ๋ธŒ๋ผ์šฐ์ €๋‚˜ HTTP ํด๋ผ์ด์–ธํŠธ๋Š” ๊ทธ ์ฃผ์†Œ๋กœ ๋‹ค์‹œ ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค. Flask์˜ redirect(url_for(...)) ๊ฐ€ ์ด ์‘๋‹ต์„ ๋งŒ๋“ ๋‹ค. ์ด ๋ฌธ์ œ์—์„œ /step1 ํ†ต๊ณผ ์งํ›„ /step2 ๋กœ ๋„˜์–ด๊ฐ€๋Š” ๊ฒŒ ๋ฐ”๋กœ ์ด ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋‹ค.

์„ธ์…˜(์ฟ ํ‚ค ์—†์ด๋„). ๋ณดํ†ต "์„ธ์…˜"์ด๋ผ๊ณ  ํ•˜๋ฉด ์ฟ ํ‚ค๋กœ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฑธ ๋– ์˜ฌ๋ฆฌ์ง€๋งŒ, ์ด ๋ฌธ์ œ ์„œ๋ฒ„๋Š” ์ฟ ํ‚ค๋ฅผ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. ๋Œ€์‹  ๋‹จ๊ณ„ ํ† ํฐ์„ ์„œ๋ฒ„ ์ „์—ญ ๋ณ€์ˆ˜์— ์ €์žฅํ•ด ๋‘๊ณ , ๋‹ค์Œ ์š”์ฒญ์ด ๊ทธ ๊ฐ’๊ณผ ๋งž๋Š”์ง€ ๋น„๊ตํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ํ† ํฐ์„ ์‘๋‹ต์—์„œ ์ฝ์–ด ๋‹ค์Œ ์š”์ฒญ์— ์ง์ ‘ ์‹ค์–ด ๋ณด๋‚ด๋Š” ์‹์œผ๋กœ ํ๋ฆ„์„ ์ด์–ด์•ผ ํ•œ๋‹ค.


๐Ÿ”ฌ ์ฝ”๋“œ ์ •์ฐฐ โ€” app.py

์ œ๊ณต๋œ app.py ๋Š” ๋ผ์šฐํŠธ ๋„ค ๊ฐœ๊ฐ€ ์ „๋ถ€๋‹ค. ํ•˜๋‚˜์”ฉ ๋ณด๋ฉด ํ†ต๊ณผ ์กฐ๊ฑด์ด ๊ทธ๋Œ€๋กœ ๋“œ๋Ÿฌ๋‚œ๋‹ค.

@app.route("/step1", methods=["GET", "POST"])
def step1():
    global step1_num
    step1_num = int.from_bytes(os.urandom(16), sys.byteorder)   # ํ˜ธ์ถœ๋งˆ๋‹ค ์ƒˆ ๋‚œ์ˆ˜
 
    if request.method == "GET":
        prm1 = request.args.get("param", "")
        prm2 = request.args.get("param2", "")
        step1_text = "param : " + prm1 + "\nparam2 : " + prm2 + "\n"
        if prm1 == "getget" and prm2 == "rerequest":
            return redirect(url_for("step2", prev_step_num=step1_num))   # โ† ํ†ต๊ณผ ์‹œ step2๋กœ
        return render_template("step1.html", text=step1_text)
    else:
        return render_template("step1.html", text="Not POST")

/step1 ์€ GET์œผ๋กœ param=getget, param2=rerequest ๋ฅผ ๋ฐ›์œผ๋ฉด /step2?prev_step_num=<step1_num> ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ step1_num ์€ ํ•จ์ˆ˜์— ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑ๋˜๋Š” 16๋ฐ”์ดํŠธ ๋‚œ์ˆ˜๋‹ค. ์ด๊ฒŒ ๋’ค์—์„œ ํ•จ์ •์ด ๋œ๋‹ค.

์กฐ๊ฑด์ด ์•ˆ ๋งž์œผ๋ฉด ๊ทธ๋ƒฅ ์ž…๋ ฅํ•œ ๋‘ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ํ™”๋ฉด์— ์ถœ๋ ฅํ•œ๋‹ค. ์ง์ ‘ ์•„๋ฌด ๊ฐ’์ด๋‚˜ ๋„ฃ์–ด ๋ณด๋ฉด ๊ทธ๋Œ€๋กœ ๋ฐ˜์‚ฌ๋˜๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

/step1 ์— ์ž„์˜ param ๋‘ ๊ฐœ โ€” ์ž…๋ ฅ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ๋ฐ˜์‚ฌ๋œ๋‹ค
/step1 ์— ์ž„์˜ param ๋‘ ๊ฐœ โ€” ์ž…๋ ฅ๊ฐ’์ด ๊ทธ๋Œ€๋กœ ๋ฐ˜์‚ฌ๋œ๋‹ค
@app.route("/step2", methods=["GET", "POST"])
def step2():
    if request.method == "GET":
        if request.args.get("prev_step_num"):
            try:
                prev_step_num = request.args.get("prev_step_num")
                if prev_step_num == str(step1_num):           # โ† step1์ด ๋ฐœ๊ธ‰ํ•œ ๊ฐ’๊ณผ ์ผ์น˜?
                    global step2_num
                    step2_num = int.from_bytes(os.urandom(16), sys.byteorder)
                    return render_template("step2.html",
                                           prev_step_num=step1_num, hidden_num=step2_num)
            except:
                return render_template("step2.html", text="Not yet")
        return render_template("step2.html", text="Not yet")
    ...

/step2 ๋Š” GET ์ฟผ๋ฆฌ prev_step_num ์ด ํ˜„์žฌ step1_num ๊ณผ ๊ฐ™์„ ๋•Œ๋งŒ ๋‹ค์Œ์œผ๋กœ ๋„˜๊ฒจ์ค€๋‹ค. ํ†ต๊ณผํ•˜๋ฉด ์ƒˆ ๋‚œ์ˆ˜ step2_num ์„ ๋งŒ๋“ค์–ด step2.html ์— ๋„˜๊ธฐ๋Š”๋ฐ, ํ…œํ”Œ๋ฆฟ์„ ๋ณด๋ฉด ์ด ๊ฐ’์ด hidden input ์œผ๋กœ ๋ฐ•ํžŒ๋‹ค.

<!-- step2.html -->
<form action="/flag" method="post">
  <p>param <input type="text" name="param"/></p>
  <p>param2 <input type="text" name="param2"/></p>
  <input type="hidden" name="check" value="{{ hidden_num }}">   <!-- โ† step2_num์ด ์—ฌ๊ธฐ -->
  <input type="submit"/>
</form>
@app.route("/flag", methods=["GET", "POST"])
def flag():
    if request.method == "GET":
        return render_template("flag.html", flag_txt="Not yet")
    else:
        prev_step_num = request.form.get("check", "")
        try:
            if prev_step_num == str(step2_num):               # โ† step2๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ๊ฐ’๊ณผ ์ผ์น˜?
                prm1 = request.form.get("param", "")
                prm2 = request.form.get("param2", "")
                if prm1 == "pooost" and prm2 == "requeeest":
                    return render_template("flag.html", flag_txt=FLAG)   # โ† ํ”Œ๋ž˜๊ทธ!
                ...

/flag ๋Š” POST๋กœ check ๊ฐ€ ํ˜„์žฌ step2_num ๊ณผ ๊ฐ™๊ณ , ๋™์‹œ์— param=pooost, param2=requeeest ์ผ ๋•Œ FLAG ๋ฅผ ๋Œ๋ ค์ค€๋‹ค. ์ •๋ฆฌํ•˜๋ฉด ํ†ต๊ณผ ์กฐ๊ฑด์€ ์ด๋ ‡๋‹ค.

๋‹จ๊ณ„๋ฉ”์„œ๋“œ์กฐ๊ฑด
/step1GETparam=getget & param2=rerequest โ†’ step1_num ๋ฐœ๊ธ‰, /step2 ๋กœ redirect
/step2GETprev_step_num == step1_num โ†’ step2_num ๋ฐœ๊ธ‰, hidden input ์œผ๋กœ ๋…ธ์ถœ
/flagPOSTcheck == step2_num & param=pooost & param2=requeeest โ†’ FLAG

๐Ÿงฑ ์•ˆํ‹ฐ์น˜ํŠธ โ€” ํ† ํฐ์€ ํ•œ ํ๋ฆ„ ์•ˆ์—์„œ๋งŒ ์‚ฐ๋‹ค

์ฝ”๋“œ ์ฃผ์„์— "ํ’€์ด์™€ ๊ด€๊ณ„์—†๋Š” ์น˜ํŒ… ๋ฐฉ์ง€ ์ฝ”๋“œ"๋ผ๊ณ  ์ ํ˜€ ์žˆ๋Š” ๋ถ€๋ถ„์ด ๋ฐ”๋กœ step1_num / step2_num ๊ฒ€์ฆ์ด๋‹ค. ์ด๊ฒŒ ์—†์œผ๋ฉด ๊ทธ๋ƒฅ /flag ์— ์ •๋‹ต ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ POSTํ•˜๋ฉด ๋์ด๋ผ, ํ† ํฐ์œผ๋กœ "์•ž ๋‹จ๊ณ„๋ฅผ ์‹ค์ œ๋กœ ๊ฑฐ์ณค๋Š”์ง€"๋ฅผ ํ™•์ธํ•œ๋‹ค.

ํ•จ์ •์€ step1_num ์ด /step1 ์— ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑ๋œ๋‹ค๋Š” ์ ์ด๋‹ค. ๊ทธ๋ž˜์„œ /step1 ์„ ๋‹ค์‹œ ๋ถ€๋ฅด๋Š” ์ˆœ๊ฐ„ ์ง์ „์— ๋ฐ›์€ prev_step_num ์€ ๋ฌดํšจ๊ฐ€ ๋œ๋‹ค. /step2 ์˜ ํ† ํฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ทธ ๋‹ค์Œ /step2 ํ˜ธ์ถœ์—์„œ ๊ฐˆ๋ฆฐ๋‹ค. ๊ฒฐ๊ตญ STEP1 โ†’ STEP2 โ†’ FLAG ๋ฅผ ์ค‘๊ฐ„์— ๋Š์ง€ ์•Š๊ณ  ํ•œ ํ๋ฆ„์œผ๋กœ ์ด์–ด์•ผ ํ•œ๋‹ค.

์š”์ฒญ ์ฒด์ธ โ€” ๊ฐ ๋‹จ๊ณ„๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ํ† ํฐ์„ ๋‹ค์Œ ์š”์ฒญ์— ์‹ค์–ด ์ž‡๋Š”๋‹ค
์š”์ฒญ ์ฒด์ธ โ€” ๊ฐ ๋‹จ๊ณ„๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ํ† ํฐ์„ ๋‹ค์Œ ์š”์ฒญ์— ์‹ค์–ด ์ž‡๋Š”๋‹ค

๐Ÿ› ์‚ฝ์งˆ โ€” ๋งํฌ๋ฅผ ๋”ฐ๋กœ๋”ฐ๋กœ ๋ˆŒ๋Ÿฌ ๋ณด๊ธฐ

์ฒ˜์Œ์—” ๋ฉ”์ธ์˜ ๋งํฌ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋ˆŒ๋Ÿฌ ๋ดค๋‹ค. /step2 ์™€ /flag ๋ฅผ ๊ทธ๋ƒฅ ์ง์ ‘ ์—ด๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋‚˜ ํ™•์ธํ•˜๋Š” ์ฐจ์›์ด์—ˆ๋Š”๋ฐ, ๋‹น์—ฐํžˆ ๋‘˜ ๋‹ค ๋ง‰ํžŒ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ โ€” /step2ยท/flag ์ง์ ‘ ์ ‘๊ทผ์€ ์ „๋ถ€ Not yet

/step2 ๋ฅผ prev_step_num ์—†์ด ๊ทธ๋ƒฅ ์—ด๋ฉด else ๋ถ„๊ธฐ๋กœ ๋น ์ ธ "Not yet" ๋งŒ ๋‚˜์˜จ๋‹ค. step1_num ๊ฒ€์ฆ์„ ํ†ต๊ณผํ•œ ์ ์ด ์—†์œผ๋‹ˆ hidden ํ† ํฐ๋„ ์•ˆ ๋ฐ•ํžŒ๋‹ค.

/step2 ์ง์ ‘ ์ ‘๊ทผ โ€” prev_step_num ์ด ์—†์–ด Not yet
/step2 ์ง์ ‘ ์ ‘๊ทผ โ€” prev_step_num ์ด ์—†์–ด Not yet

/flag ๋ฅผ GET์œผ๋กœ ์—ด์–ด๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค. /flag ์˜ GET ํ•ธ๋“ค๋Ÿฌ๋Š” ๋ฌด์กฐ๊ฑด "Not yet" ์„ ๋Œ๋ ค์ฃผ๊ณ , ํ”Œ๋ž˜๊ทธ๋Š” POST + check ํ† ํฐ์ด ๋งž์„ ๋•Œ๋งŒ ๋‚˜์˜จ๋‹ค. ๋งํฌ ํด๋ฆญ(=GET)๋งŒ์œผ๋กœ๋Š” ์ ˆ๋Œ€ ๋„๋‹ฌํ•  ์ˆ˜ ์—†๋Š” ๊ตฌ์กฐ๋‹ค.

/flag GET ์ ‘๊ทผ โ€” POST๊ฐ€ ์•„๋‹ˆ๋ผ Not yet
/flag GET ์ ‘๊ทผ โ€” POST๊ฐ€ ์•„๋‹ˆ๋ผ Not yet

์—ฌ๊ธฐ์„œ "์•„, ๋ฉ”์„œ๋“œ์™€ ํ† ํฐ์„ ์ง์ ‘ ๋งž์ถฐ ๋ณด๋‚ด์•ผ ํ•˜๋Š”๊ตฌ๋‚˜"๊ฐ€ ๋ถ„๋ช…ํ•ด์ง„๋‹ค.

/step1 ๋งŒ ๋”ฐ๋กœ ์—ด๋ฉด ๋นˆ ์ž…๋ ฅ ํผ์ด ๋ณด์ธ๋‹ค. ํผ์€ method="get" ์ด๋ผ, ์—ฌ๊ธฐ์— ๊ฐ’์„ ๋„ฃ๊ณ  ์ œ์ถœํ•ด๋„ ๊ฒฐ๊ตญ GET ์ฟผ๋ฆฌ๋กœ ๋‚˜๊ฐ„๋‹ค.

/step1 โ€” param / param2 ์ž…๋ ฅ ํผ (GET)
/step1 โ€” param / param2 ์ž…๋ ฅ ํผ (GET)


๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉด ํ† ํฐ์ด ์ €์ ˆ๋กœ ์ด์–ด์ง„๋‹ค

์—ฌ๊ธฐ์„œ ํ•ต์‹ฌ ๊ด€์ฐฐ. /step1?param=getget&param2=rerequest ๋กœ ๋“ค์–ด๊ฐ€๋ฉด ์„œ๋ฒ„๊ฐ€ step1_num ์„ ๋งŒ๋“  ์งํ›„ /step2?prev_step_num=<๊ทธ ๊ฐ’> ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธํ•œ๋‹ค. ์ฆ‰ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ URL์ด ๋ฐฉ๊ธˆ ๋ฐœ๊ธ‰๋œ ํ† ํฐ์„ ๊ทธ๋Œ€๋กœ ๋‹ด๊ณ  ์žˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋“  HTTP ํด๋ผ์ด์–ธํŠธ๋“  ๊ทธ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉด, /step2 ๊ฒ€์ฆ์„ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ•˜๋ฉด์„œ step2_num ์ด ๋ฐ•ํžŒ ํผ์„ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

์‹ค์ œ๋กœ ๋ธŒ๋ผ์šฐ์ € ์ฃผ์†Œ์ฐฝ์— /step1?param=getget&param2=rerequest ๋ฅผ ์น˜๋ฉด, ์ž๋™์œผ๋กœ /step2?prev_step_num=... ๋กœ ๋„˜์–ด๊ฐ€ ์ž…๋ ฅ ํผ์ด ๋œฌ๋‹ค. ์ฃผ์†Œ์ฐฝ์˜ ๊ธด ์ˆซ์ž๊ฐ€ step1_num, ๊ทธ๋ฆฌ๊ณ  ์ด ํŽ˜์ด์ง€์˜ hidden input ์— step2_num ์ด ๋“ค์–ด ์žˆ๋‹ค.

/step1 ์ •๋‹ต ํŒŒ๋ผ๋ฏธํ„ฐ โ†’ /step2 ํผ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ (์ฃผ์†Œ์ฐฝ์— prev_step_num)
/step1 ์ •๋‹ต ํŒŒ๋ผ๋ฏธํ„ฐ โ†’ /step2 ํผ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ (์ฃผ์†Œ์ฐฝ์— prev_step_num)

์ด ํผ์€ action="/flag" method="post" ์ด๊ณ  hidden check ์— step2_num ์ด ์ด๋ฏธ ์ฑ„์›Œ์ ธ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์ด ํ™”๋ฉด์—์„œ param, param2 ์นธ์— ์ •๋‹ต์„ ๋„ฃ๊ณ  ์ œ์ถœํ•˜๋ฉด, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์•Œ์•„์„œ check + param + param2 ๋ฅผ POST๋กœ /flag ์— ์˜ฌ๋ฆฐ๋‹ค. ์ค‘๊ฐ„์— /step1 ์ด๋‚˜ /step2 ๋ฅผ ๋‹ค์‹œ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์•˜์œผ๋‹ˆ ํ† ํฐ๋„ ๊ทธ๋Œ€๋กœ ์œ ํšจํ•˜๋‹ค.


๐ŸŽฏ ์ต์Šคํ”Œ๋กœ์ž‡ โ€” ๋ธŒ๋ผ์šฐ์ €๋กœ ํ•œ ๋ฒˆ

๊ฐ€์žฅ ์†์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ์œ„ ํผ์„ ๊ทธ๋Œ€๋กœ ์“ฐ๋Š” ๊ฒƒ์ด๋‹ค. ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋กœ ๋„์ฐฉํ•œ /step2 ํผ์—์„œ param ์— pooost, param2 ์— requeeest ๋ฅผ ๋„ฃ๊ณ  ์ œ์ถœํ•˜๋ฉด ๋œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ hidden check ๊นŒ์ง€ ํ•จ๊ป˜ POSTํ•˜๋ฏ€๋กœ ์ถ”๊ฐ€๋กœ ์†๋Œˆ ๊ฒŒ ์—†๋‹ค.

ํผ ์ œ์ถœ ๊ฒฐ๊ณผ โ€” /flag ๊ฐ€ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ˜ํ™˜
ํผ ์ œ์ถœ ๊ฒฐ๊ณผ โ€” /flag ๊ฐ€ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ˜ํ™˜

/flag ํ™”๋ฉด์— ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ทธ๋Œ€๋กœ ์ฐํ˜”๋‹ค.

DH{c46b414ddba26adfa4606c59655bda0a18fbe260606b042b52d865e0160eea0e}

๐Ÿš€ Full Exploit โ€” requests.Session ์œผ๋กœ ์ž๋™ํ™”

๋งค๋ฒˆ ์†์œผ๋กœ ํผ์„ ์ฑ„์šฐ๋Š” ๋Œ€์‹ , requests.Session ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ์ž๋™ ์ถ”์ ํ•˜๊ณ  hidden ํ† ํฐ์„ ์ •๊ทœ์‹์œผ๋กœ ๋ฝ‘์•„ ์ด์–ด ๋ถ™์ด๋ฉด ํ•œ ๋ฒˆ์— ๋๋‚œ๋‹ค. allow_redirects=True (๊ธฐ๋ณธ๊ฐ’) ๋•๋ถ„์— STEP1์˜ 302๋ฅผ ๋”ฐ๋ผ๊ฐ€ STEP2 ํผ๊นŒ์ง€ ํ•œ ํ˜ธ์ถœ๋กœ ๋„๋‹ฌํ•˜๊ณ , ๊ฑฐ๊ธฐ์„œ check ๊ฐ’์„ ์ฝ์–ด ๊ทธ๋Œ€๋กœ POSTํ•˜๋ฉด ๋œ๋‹ค.

#!/usr/bin/env python3
# simple-web-request โ€” GET/POST ์š”์ฒญ ์ฒด์ด๋‹
import re, sys, requests
 
BASE = sys.argv[1] if len(sys.argv) > 1 else "http://host3.dreamhack.games:22341"
BASE = BASE.rstrip("/")
s = requests.Session()
 
# STEP1: ์ •๋‹ต param ๋‘ ๊ฐœ โ†’ 302 /step2?prev_step_num=step1_num ๊นŒ์ง€ ์ž๋™ ์ถ”์ 
r = s.get(f"{BASE}/step1", params={"param": "getget", "param2": "rerequest"},
          allow_redirects=True)
print(f"[STEP1โ†’STEP2] {r.url}")
print(f"             status={r.status_code}")
 
# STEP2 ์‘๋‹ต HTML ์—์„œ hidden check ๊ฐ’(step2_num) ์ถ”์ถœ
m = re.search(r'name="check"\s+value="(\d+)"', r.text)
step2_num = m.group(1)
print(f"[STEP2] hidden check (step2_num) = {step2_num}")
 
# FLAG: POST ๋กœ check + ์ •๋‹ต param ๋‘ ๊ฐœ
r = s.post(f"{BASE}/flag",
           data={"check": step2_num, "param": "pooost", "param2": "requeeest"})
print(f"[FLAG] status={r.status_code}")
flag = re.search(r"DH\{.*?\}", r.text)
print(f"[+] FLAG = {flag.group(0)}")

requests.Session ์„ ์“ด ๊ฑด ์ฟ ํ‚ค ๋•Œ๋ฌธ์ด ์•„๋‹ˆ๋ผ(์ด ์„œ๋ฒ„๋Š” ์ฟ ํ‚ค๋ฅผ ์•ˆ ์“ด๋‹ค) ๊ฐ™์€ ์ปค๋„ฅ์…˜์œผ๋กœ ํ๋ฆ„์„ ๊น”๋”ํ•˜๊ฒŒ ์ž‡๊ธฐ ์œ„ํ•ด์„œ๋‹ค. ํ•ต์‹ฌ์€ allow_redirects=True ๋กœ STEP1์˜ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋ฅผ ๋”ฐ๋ผ๊ฐ€ STEP2 ํผ์„ ๋ฐ›๊ณ , ๊ฑฐ๊ธฐ hidden check ๊ฐ’์„ ๊ทธ๋Œ€๋กœ STEP2๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ํ† ํฐ์œผ๋กœ ์žฌ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ. ์‹คํ–‰ํ•˜๋ฉด ๋‹จ๊ณ„๋ณ„ ํ† ํฐ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๋–จ์–ด์ง„๋‹ค.

solve.py ์‹คํ–‰ โ€” ๋‹จ๊ณ„๋ณ„ ํ† ํฐ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ž˜๊ทธ ํš๋“
solve.py ์‹คํ–‰ โ€” ๋‹จ๊ณ„๋ณ„ ํ† ํฐ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ž˜๊ทธ ํš๋“


๐Ÿ“ ๊ฒฐ๋ก 

์š”์ฒญ์„ ์ง์ ‘ ๋‹ค๋ค„ ๋ณด๋Š” ์ž…๋ฌธ ๋ฌธ์ œ.

๋งํฌ๋ฅผ ํด๋ฆญํ•˜๋Š” ๊ฒƒ๊ณผ, ๋ฉ”์„œ๋“œยทํŒŒ๋ผ๋ฏธํ„ฐยทํ—ค๋”๋ฅผ ์†์œผ๋กœ ๋งž์ถฐ ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์˜ ์ฐจ์ด๋ฅผ ์ตํžˆ๋Š” ๊ฒŒ ์ด ๋ฌธ์ œ์˜ ์ „๋ถ€๋‹ค. GET์€ ์ฟผ๋ฆฌ์ŠคํŠธ๋ง, POST๋Š” ํผ ๋ณธ๋ฌธ, 302๋Š” ๋”ฐ๋ผ๊ฐ€๋Š” ์‘๋‹ต โ€” ์ด ์…‹๋งŒ ๊ตฌ๋ถ„ํ•˜๋ฉด ํ๋ฆ„์ด ๊ทธ๋Œ€๋กœ ๋ณด์ธ๋‹ค.

์„œ๋ฒ„ ์ƒํƒœ๋ฅผ ํ† ํฐ์œผ๋กœ ์ž‡๋Š” ํŒจํ„ด.

๋‹จ๊ณ„๋งˆ๋‹ค ๋‚œ์ˆ˜๋ฅผ ๋ฐœ๊ธ‰ํ•˜๊ณ  ๋‹ค์Œ ์š”์ฒญ์—์„œ ๊ทธ ๊ฐ’์„ ํ™•์ธํ•˜๋Š” ๊ตฌ์กฐ๋Š” ์‹ค์ œ ์„œ๋น„์Šค์˜ CSRF ํ† ํฐยท๊ฒฐ์ œ ํ๋ฆ„ ํ† ํฐยทOAuth state ์™€ ๊ฐ™์€ ๋ฐœ์ƒ์ด๋‹ค. ๋‹ค๋งŒ ์—ฌ๊ธฐ์„  ํ† ํฐ์„ ์ „์—ญ ๋ณ€์ˆ˜ ํ•˜๋‚˜์— ๋‹ด์•„ ๋‘ฌ์„œ, ๋™์‹œ ์ ‘์†์ด๋‚˜ ์žฌ์š”์ฒญ์— ์ทจ์•ฝํ•˜๋‹ค. step1_num ์ด ํ˜ธ์ถœ๋งˆ๋‹ค ๋ฎ์–ด์จ์ง€๋Š” ๊ฒƒ๋„ ๊ทธ๋ž˜์„œ ํ•œ ํ๋ฆ„ ์•ˆ์—์„œ๋งŒ ์œ ํšจํ•œ ๊ฒƒ์ด๊ณ .

๋ฐฉ์–ด ๊ด€์ ์—์„œ ๋ณด๋ฉด.

์ด๋Ÿฐ ๋‹ค๋‹จ๊ณ„ ๊ฒ€์ฆ์„ ์ œ๋Œ€๋กœ ํ•˜๋ ค๋ฉด ํ† ํฐ์„ ์ „์—ญ ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž๋ณ„ ์„ธ์…˜(์„œ๋ช…๋œ ์ฟ ํ‚ค๋‚˜ ์„œ๋ฒ„ ์„ธ์…˜ ์ €์žฅ์†Œ)์— ๋ฌถ์–ด์•ผ ํ•œ๋‹ค. ์ „์—ญ์— ๋‘๋ฉด ํ•œ ์‚ฌ์šฉ์ž์˜ ํ† ํฐ์ด ๋‹ค๋ฅธ ์š”์ฒญ์— ๋ฎ์–ด์จ์ง€๊ฑฐ๋‚˜ ์ƒˆ์–ด ๋‚˜๊ฐˆ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  hidden input ์— ํ† ํฐ์„ ๋‚ด๋ ค๋ณด๋‚ด๋Š” ๊ฒƒ ์ž์ฒด๋Š” ํ”ํ•œ ๋ฐฉ์‹์ด์ง€๋งŒ, ๊ทธ ํ† ํฐ์ด "์–ด๋–ค ๋‹จ๊ณ„๊นŒ์ง€ ํ†ต๊ณผํ–ˆ๋Š”์ง€"๋งŒ์ด ์•„๋‹ˆ๋ผ "๋ˆ„๊ฐ€" ํ†ต๊ณผํ–ˆ๋Š”์ง€๊นŒ์ง€ ๋ฌถ์—ฌ ์žˆ์–ด์•ผ ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‰ Bronze 3] ๋šซ์„ ํ•„์š”๊ฐ€ ์—†์—ˆ๋‹ค, ์ œ๋Œ€๋กœ ๋ณด๊ธฐ๋งŒ ํ•˜๋ฉด ๋๋‹ค โ€” DreamHack [wargame.kr] already got ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 3] ๋šซ์„ ํ•„์š”๊ฐ€ ์—†์—ˆ๋‹ค, ์ œ๋Œ€๋กœ ๋ณด๊ธฐ๋งŒ ํ•˜๋ฉด ๋๋‹ค โ€” DreamHack [wargame.kr] already got ํ’€์ด

์˜› wargame.kr์—์„œ ๋ฆฌ๋งˆ์Šคํ„ฐํ•ด ๊ฐ€์ ธ์˜จ ๋ฌธ์ œ. ํŽ˜์ด์ง€๋ฅผ ์—ด์–ด๋ณด๋ฉด "you've already got key! :p"๋ผ๋Š” ํ•œ ์ค„์งœ๋ฆฌ ๋ฌธ๊ตฌ๋ฟ์ด๊ณ , HTML ์†Œ์Šค๋ฅผ ๋ด๋„ ๋˜‘๊ฐ™๋‹ค. ๋ฌธ์ œ ์„ค๋ช…์ด "HTTP ์‘๋‹ต ํ—ค๋”๊ฐ€ ๋ณด์ด๋‚˜์š”?"๋ผ๊ณ  ๋Œ€๋†“๊ณ  ํžŒํŠธ๋ฅผ ์ฃผ๋Š”๋ฐ, ์ด ์งง์€ ๋ฌธ์ œ ํ•˜๋‚˜๊ฐ€ ์‚ฌ์‹ค "๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด์— ๋ณด์ด๋Š” ๊ฒƒ"๊ณผ "์„œ๋ฒ„๊ฐ€ ์‹ค์ œ๋กœ ๋ณด๋‚ธ ๊ฒƒ"์ด ๋‹ค๋ฅด๋‹ค๋Š” ์›น ๋ฆฌ๋ฒ„์‹ฑ์˜ ๊ฐ€์žฅ ๊ธฐ์ดˆ์ ์ธ ๊ตํ›ˆ์„ ์ •ํ™•ํžˆ ์งš๊ณ  ์žˆ์—ˆ๋‹ค. ์ทจ์•ฝ์ ์„ ์ฐพ์•„ ๋šซ๋Š” ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ, HTTP ํ”„๋กœํ† ์ฝœ์„ ์žˆ๋Š” ๊ทธ๋Œ€๋กœ ๊ด€์ฐฐํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๋Š” ๋ฌธ์ œ์˜€๋‹ค.
#dreamhack#ctf#web+3
2026-07-21#dreamhack +4
[๐ŸŒฑ Sprout] ๊ธฐ๋ณธ ์ž๊ฒฉ์ฆ๋ช…์œผ๋กœ ์„ค์ •๊ฐ’ ์ฝ๊ธฐ โ€” DreamHack web-misconf-1 ํ’€์ด
blog

[๐ŸŒฑ Sprout] ๊ธฐ๋ณธ ์ž๊ฒฉ์ฆ๋ช…์œผ๋กœ ์„ค์ •๊ฐ’ ์ฝ๊ธฐ โ€” DreamHack web-misconf-1 ํ’€์ด

Grafana 6.6.2์˜ admin ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ดˆ๊ธฐ๊ฐ’ admin ๊ทธ๋Œ€๋กœ๋‹ค. ๊ธฐ๋ณธ ์ž๊ฒฉ์ฆ๋ช…์œผ๋กœ ๋กœ๊ทธ์ธํ•œ ๋’ค /api/admin/settings ๊ฐ€ ์‹คํ–‰ ์ค‘ ์„ค์ •์„ ํ†ต์งธ๋กœ ๋Œ๋ ค์ฃผ๋Š”๋ฐ, ๋ฏผ๊ฐ ํ‚ค๋งŒ ๊ฐ€๋ ค์ง€๊ณ  ํ‰๋ฒ”ํ•œ ํ‚ค๋Š” ํ‰๋ฌธ์ด๋ผ auth.anonymous.org_name ์— ์ˆจ๊ฒจ๋‘” ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ๋œ๋‹ค.
#dreamhack#ctf#web+4
2026-06-25#dreamhack +3
[๐Ÿฅˆ Silver 4] ๋ฌธ์ž์—ด์„ ์ด์–ด ๋ถ™์—ฌ JSON ์„ ๋งŒ๋“ค๋ฉด โ€” DreamHack role-admin ํ’€์ด
blog

[๐Ÿฅˆ Silver 4] ๋ฌธ์ž์—ด์„ ์ด์–ด ๋ถ™์—ฌ JSON ์„ ๋งŒ๋“ค๋ฉด โ€” DreamHack role-admin ํ’€์ด

ํšŒ์›๊ฐ€์ž…์ด username ์„ ์ด์Šค์ผ€์ดํ”„ ์—†์ด f-string ์œผ๋กœ JSON ๋ฌธ์ž์—ด์— ๋ผ์›Œ ๋„ฃ๋Š”๋‹ค. ๋”ฐ์˜ดํ‘œ๋ฅผ ๋‹ซ๊ณ  role ํ‚ค๋ฅผ ํ•˜๋‚˜ ๋” ๋„ฃ์œผ๋ฉด ๊ฐ™์€ ๊ฐ์ฒด์— role ์ด ๋‘ ๋ฒˆ ๋“ค์–ด๊ฐ€๊ณ , ํŒŒ์ด์ฌ json ์€ ๋‚˜์ค‘ ๊ฐ’์„ ๋‚จ๊ธด๋‹ค. ๊ฐ€์ž… ํ•œ ๋ฒˆ์œผ๋กœ ๊ด€๋ฆฌ์ž๊ฐ€ ๋˜๊ณ  /flag ๊ฐ€ ์—ด๋ฆฐ๋‹ค.
#dreamhack#ctf#web+5
2026-08-07#dreamhack +3