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`)을 이용한다.
- 내부 봇의 쿠키 값을 메모장에 출력하기 위해서 필터링을 우회해 스크립트를 작성한다.
- 내부 봇이 메모장 페이지로 쿠키 값을 가지고 리다이렉션되고, 메모장엔 쿠키 값(`플래그`)가 출력된다.