[웹 해킹] Dreamhack xss-1(Level 1)
#259
1. 개요
워게임 명 : xss-1
난이도 : Level 1
관련 개념 : Javascript, XSS, Cookie
문제 : XSS 취약점을 이용하여 FLAG 값 획득
XSS 강의에 포함된 워게임입니다.
2. 소스 코드 확인
1) HTML
소스코드에는 html 문서가 포함되어 있었고 어떻게 구현되어 있는지 확인이 필요해 보였습니다.
1-1) base.html
1-2) index.html
페이지를 분기하는 소스로 vul(xss) page를 클릭하였을때 스크립트가 실행되어 alert(1) 창을 띄웁니다.
1-3) memo.html
1-4) flag.html
파라미터를 입력받는 페이지입니다.
2) python
2-1) 초기 선언 부분
#!/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**]"
2-2) read_url 함수
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
2-3) check_xss
def check_xss(param, cookie={"name": "name", "value": "value"}):
#check_xss는 read_url함수 호출하여 vuln 엔드포인트 접속
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
return read_url(url, cookie)
2-4) app.route("/")
#render_template : flask에서 제공하는 함수로 templates에 저장된 html을 불러올 때 사용하는 함수
@app.route("/")
def index():
return render_template("index.html")
2-5) app.route("/vuln")
#사용자가 입력한 param 값을 출력
#필터 없이 그대로 요청 받은 내용을 그대로 출력
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param
2-6) app.route("/flag", methods["GET", "POST"])
@app.route("/flag", methods=["GET", "POST"])
def flag():
#이용자에게 URL을 입력받는 페이지를 제공
if request.method == "GET":
return render_template("flag.html")
elif request.method == "POST":
param = request.form.get("param")
#파라미터 값과 쿠키에 FLAG를 포함해 check_xss 함수 호출
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>'
2-7) app.route("/memo")
memo_text = ""
#사용자가 요청한 내용을 메모로 작성하여 출력
#여기는 render_template를 통해 출력하기 때문에 취약하지 않음
@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)
2-8) 서비스 실행
app.run(host="0.0.0.0", port=8000)
3. 웹 화면 확인
앞에서 소스를 분석했을때처럼 /vuln, /memo, /flag 경로가 존재하는 웹 서비스였습니다.
1) /vuln
index.html 소스에서 /vuln 링크를 클릭했을 때 아래와 같이 alert 창이 뜨도록 스크립트를 작성했었습니다.
그 결과, 스크립트가 실행되었고 해당 경로는 취약하다고 볼 수 있습니다.
2) /memo
여기는 메모가 입력되어 저장되는 경로로 링크를 클릭할 때 마다 hello 문구가 추가됩니다.
3) /flag
이 곳이 저희가 실제 테스트하여 flag 값을 얻을 경로입니다.
4. 문제 풀이
1) 요구사항 파악
취약한 페이지의 경우 /vuln 페이지고 /flag 링크에서 이를 이용하여 flag값을 얻으려고 합니다.
flag 값은 서버 로컬 경로의 flag.txt 파일에 존재하고 /flag 경로에 파라미터를 입력하면 cookie 값으로 flag 값을 저장합니다.
이것을 /memo 경로에 출력하면 됩니다.
2) 메모페이지 출력 테스트
flag 값을 확인하기 위해서는 /memo 경로에 어떻게 출력되는지 알아야하기 때문에 확인해보도록 하겠습니다.
GET 방식으로 URL에 데이터를 입력되면 출력되는 방식입니다.
hello 대신 여러 데이터를 입력하면 출력되는 것을 확인하실 수 있습니다.
3) flag 값 출력하기
이제 어떻게 출력하는지 알게 되었으니 본격적으로 flag 값을 출력해보도록 하겠습니다.
flag 값은 쿠키에 저장되어 있으며, 쿠키를 /memo 경로에 출력하도록 요청을 보내면 됩니다.
/vuln 경로에서는 스크립트 구문이 실행되었기 때문에 아래와 같이 작성하여 요청을 보냅니다.
<script>location.href = "/memo?memo=" + document.cookie;</script>
보시면 해당 웹 서버의 /memo 경로에 쿠키 값(document.cookie)을 입력한다는 스크립트입니다.
그럼 위와 같이 good이란 알림창이 뜨면서 /memo경로로 가게되면 flag 값을 확인할 수 있습니다.
해당 게임의 경우 강의에서 내용을 다루고 있어 쉽게 풀 수 있었습니다.
이 공격을 응용한다면 XSS 취약점을 찾아내는데 큰 도움이 될 것으로 보입니다.