๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ•ธ๏ธ ์›น ํ•ดํ‚น

[wargame] ๋“œ๋ฆผํ•ต xss-1 ๋ฌธ์ œํ’€์ด

by ๋ถˆํƒ€๋Š” ์ฐธ์ƒˆ 2025. 3. 6.

๋“œ๋ฆผํ•ต ์›Œ๊ฒŒ์ž„ level1 xss-1 ๋ฌธ์ œ์— ๋Œ€ํ•œ ํ’€์ด์ด๋‹ค.

 

๋ฌธ์ œ ํŒŒ์ผ์„ ๋‹ค์šด๋ฐ›๊ณ  app.py ํŒŒ์ผ์„ ์—ด์–ด์ฃผ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŽ˜์ด์ง€๋“ค์ด ๋ผ์šฐํŒ… ๋˜๊ณ  ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. 

 

๊ฐ ํŽ˜์ด์ง€์˜ ๊ธฐ๋Šฅ์„ ์ •๋ฆฌํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

๐Ÿค” ์ฝ”๋“œ ๋ถ„์„

๊ฐ ํŽ˜์ด์ง€์˜ ๊ธฐ๋Šฅ์„ ์ •๋ฆฌํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.  (๋“œ๋ฆผํ•ต ์›น ํ•ดํ‚น ๊ฐ•์ขŒ์—์„œ ์ด๋ฏธ ์ •๋ฆฌ๋œ ๋‚ด์šฉ์ด๋‹ค.)

ํŽ˜์ด์ง€ ๊ธฐ๋Šฅ
/ ์ธ๋ฑ์Šค ํŽ˜์ด์ง€
/vuln ์ด์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’ ์ถœ๋ ฅ
/memo ๋ฉ”๋ชจ ์ž‘์„ฑ ๋ฐ ์ž‘์„ฑํ•œ ๋ฉ”๋ชจ ์ถœ๋ ฅ
/flag ์ž‘์„ฑํ•œ URL ์ „๋‹ฌ
 

 

์ด์ œ ์ฝ”๋“œ์—์„œ ํ•ต์‹ฌ ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด์ž.
@app.route("/vuln")
def vuln():
    param = request.args.get("param", "")
    return param
  • request.args.get("param", "")๋Š” HTTP ์š”์ฒญ์—์„œ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ
  • ์˜ˆ๋ฅผ ๋“ค์–ด, URL์ด http://example.com/?param=test๋ผ๋ฉด, request.args.get("param", "")๋Š” "test" ๊ฐ’์„ ๋ฐ˜ํ™˜

 

def check_xss(param, cookie={"name": "name", "value": "value"}):
    url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
    return read_url(url, cookie)

 

  • param: ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌํ•  ๊ฐ’์„ ์˜๋ฏธ
  • cookie: ์ฟ ํ‚ค ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” ๊ธฐ๋ณธ๊ฐ’์ด {"name": "name", "value": "value"}์ธ ๋”•์…”๋„ˆ๋ฆฌ
  • ์ด ์ฝ”๋“œ์—์„œ url ๋ณ€์ˆ˜๋Š” param ๊ฐ’์„ URL ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ํฌํ•จํ•˜๋Š” URL์„ ์ƒ์„ฑํ•จ
  • urllib.parse.quote(param)๋Š” param ๊ฐ’์„ URL ์ธ์ฝ”๋”ฉํ•˜์—ฌ ์ฟผ๋ฆฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ. ์ด๋Š” ํŠน์ˆ˜ ๋ฌธ์ž๋‚˜ ๊ณต๋ฐฑ ๋“ฑ์„ URL์— ์ ํ•ฉํ•œ ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ• .
    • ์˜ˆ๋ฅผ ๋“ค์–ด, param ๊ฐ’์ด "hello world"๋ผ๋ฉด, urllib.parse.quote(param)์€ "hello%20world"๋กœ ๋ณ€ํ™˜.
  • ์ตœ์ข…์ ์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” URL์€ http://127.0.0.1:8000/vuln?param=<encoded_param> ํ˜•ํƒœ

 

@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")
        if not check_xss(param, {"name": "flag", "value": FLAG.strip()}):
            return '<script>alert("wrong??");history.go(-1);</script>'

        return '<script>alert("good");history.go(-1);</script>'
  • GET ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด, flag.html ํ…œํ”Œ๋ฆฟ์„ ๋ Œ๋”๋งํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ๋ธŒ๋ผ์šฐ์ €๋กœ ๋ฐ˜ํ™˜. ์ฆ‰, ์‚ฌ์šฉ์ž์—๊ฒŒ ์›น ํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๋ถ€๋ถ„
  • ์‚ฌ์šฉ์ž๊ฐ€ ํผ์„ ์ œ์ถœํ•˜๋ฉด, POST ์š”์ฒญ์ด ์ฒ˜๋ฆฌ๋จ. ํผ์—์„œ ์ œ์ถœ๋œ param ๊ฐ’์„ request.form.get("param")์„ ์‚ฌ์šฉํ•ด ๊ฐ€์ ธ์˜ด.
  • if not check_xss(...):
    • check_xss๊ฐ€ False๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋Š” ๊ฒƒ์€, param์— XSS ๊ณต๊ฒฉ์ด ํฌํ•จ๋˜์–ด ์žˆ์Œ์„ ์˜๋ฏธ.
    • ์ด ๊ฒฝ์šฐ, ์„œ๋ฒ„๋Š” wrong??๋ผ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ alert ์ฐฝ์œผ๋กœ ํ‘œ์‹œํ•˜๊ณ , ์‚ฌ์šฉ์ž๋ฅผ ์ด์ „ ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ€๊ฒŒ (history.go(-1)) ๋งŒ๋“ฆ
๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•˜๋ฉด memo์™€ vuln ํŽ˜์ด์ง€ ๋ชจ๋‘ ์ „๋‹ฌ ๋ฐ›์€ param์„ ํ™”๋ฉด์— ์ถœ๋ ฅํ•˜๋Š”๋ฐ, param์€ flag์—์„œ cookie ๊ฐ’๊ณผ ํ•จ๊ป˜ xss๋ฅผ ํ™•์ธํ•˜๋Š” ํ•จ์ˆ˜๋กœ ์šฐ์„  ๋ณด๋‚ด์ง€๊ณ , ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š์œผ๋ฉด read_url ํ•จ์ˆ˜๋กœ ๋ณด๋‚ด์ ธ ๊ฐ ํŽ˜์ด์ง€์— param์„ ์ „๋‹ฌํ•˜๊ณ  ์žˆ๋‹ค.

 

๐Ÿ’ญ ๋ฌธ์ œ ํ’€์ด๋ฅผ ์œ„ํ•œ ์ง€์‹

 

๋ฌธ์ œ๋ฅผ ํ’€๋ ค๋ฉด ์šฐ์„  ์•„๋ž˜ ๋‘ ๊ฐ€์ง€ ์†์„ฑ๊ฐ’์„ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค. 

 

location.href: ์ „์ฒด URL์„ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜, URL์„ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋Š” ์†์„ฑ๊ฐ’

document.cookie: ํ•ด๋‹น ํŽ˜์ด์ง€์—์„œ ์‚ฌ์šฉํ•˜๋Š” ์ฟ ํ‚ค๋ฅผ ์ฝ๊ณ , ์“ฐ๋Š” ์†์„ฑ๊ฐ’

 

memo์™€ vuln ๋ชจ๋‘ flag์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’์„ ์ถœ๋ ฅ ํ•˜์ง€๋งŒ ์ฟ ํ‚ค๋ฅผ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๋Š” vuln ํŽ˜์ด์ง€์ผ ๋ฟ์ด๋‹ค. ๊ทธ ์ด์œ ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์ฐจ์ด์ ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

  • memo: render_template ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด memo.html์„ ์ถœ๋ ฅ, render_template ํ•จ์ˆ˜๋Š” ์ „๋‹ฌ๋œ ํ…œํ”Œ๋ฆฟ ๋ณ€์ˆ˜๋ฅผ ๊ธฐ๋กํ•  ๋•Œ HTML ์—”ํ‹ฐํ‹ฐ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•ด ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— XSS๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ (์ถœ์ฒ˜: ๋“œ๋ฆผํ•ต ์›น ํ•ดํ‚น ๊ฐ•์˜)
  • vuln: ์ด์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’์„ ํŽ˜์ด์ง€์— ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ, XSS ๋ฐœ์ƒ ๊ฐ€๋Šฅ

 

๐Ÿ“Œ ๋ฌธ์ œ ํ’€์ด

 

vuln ํŽ˜์ด์ง€์— ์ฟ ํ‚ค๋ฅผ ํƒˆ์ทจํ•˜๊ธฐ ์œ„ํ•œ param์„ ์ „๋‹ฌํ•œ ๋’ค ํƒˆ์ทจํ•œ ์ฟ ํ‚ค๋ฅผ memo ํŽ˜์ด์ง€์—์„œ ๋ณด์ด๊ฒŒ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. 

flag ํŽ˜์ด์ง€ param์„ ์ž‘์„ฑํ•˜๋Š” ๋ถ€๋ถ„์— ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. 

<script>location.href = "/memo?memo=" + document.cookie;</script>

 

document.cookie ์ฆ‰, ํƒˆ์ทจํ•œ ์ฟ ํ‚ค๊ฐ€ ๋‹ด๊ฒจ์žˆ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ memo ์—”๋“œํฌ์ธํŠธ์— ์ „๋‹ฌํ•˜๋ฉด

memo ํŽ˜์ด์ง€๋Š” ์ „๋‹ฌ๋ฐ›์€ param์„ ๋ณด์—ฌ์ค€๋‹ค.

 

memo ํŽ˜์ด์ง€์— ๋ณด์ด๋Š” FLAG