Wargame/Dreamhack

[Dreamhack] XSS Filtering Bypass

sniffy-fanta 2025. 7. 7. 22:54

문제 설명

Exercise: XSS Filtering Bypass에서 실습하는 문제입니다.

 


 

코드 분석

@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>'
  • `/flag`페이지에서 파라미터 값을 `check_xss` 함수에 파라미터 값과, 플래그 값을 전달한다.
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("/vuln")
def vuln():
    param = request.args.get("param", "")
    param = xss_filter(param)
    return param
    
def xss_filter(text):
    _filter = ["script", "on", "javascript:"]
    for f in _filter:
        if f in text.lower():
            text = text.replace(f, "")
    return text
  • `/flag`에서 받은 파라미터가 `check_xss`로 전달되고, 이 함수가 내부적으로 `/vuln`을 호출해서 XSS 필터를 적용한다.
  • XSS 필터는 블랙리스트 기반이라 완벽하지 않아서, 우회가 충분히 가능하다.
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)
  • `/memo` 페이지에는 파라미터 값이 그대로 출력된다.

풀이 과정

  • XSS 필터링을 우회하기 위해서 빈 값으로 치환되는 것(`scrscriptipt`)을 이용한다.
  • 내부 봇의 쿠키 값을 메모장에 출력하기 위해서 필터링을 우회해 스크립트를 작성한다.

  • 내부 봇이 메모장 페이지로 쿠키 값을 가지고 리다이렉션되고, 메모장엔 쿠키 값(`플래그`)가 출력된다.