문제 설명
XSS 기법을 사용하여 쿠키에 저장된 플래그 값을 탈취하자.
페이지 설명
- / : 인덱스 페이지입니다.
- /vuln : 이용자가 입력한 값을 출력합니다.
- /memo : 이용자가 메모를 남길 수 있으며, 작성한 메모를 출력합니다.
- /flag : 전달된 URL에 임의 이용자가 접속하게끔 합니다. 해당 이용자의 쿠키에는 FLAG가 존재합니다.
/vuln 페이지에 접속하면 param 자리에 script 를 작성한 것이 제대로 작동하는 것을 확인할 수 있다.
여기서 vuln의 코드를 보게 되면 사용자가 입력한 파라미터를 바로 return 하기 때문에 이것이 바로 XSS 취약점이 될 수 있는 것이다.
@app.route("/vuln")
def vuln():
param = request.args.get("param", "") # 이용자가 입력한 vuln 인자를 가져옴
return param # 이용자의 입력값을 화면 상에 표시
/memo 페이지에 접속하면 사용자가 보낸 memo의 value를 화면에 띄워주기 때문에
여기서 플래그 값을 확인할 수 있다고 생각하면 된다. 여기서 코드를 확인하자면 메모 페이지는 사용자가 입력한 value를 render_template를 통하여 "memo.html" 안에 출력하기 때문에 XSS가 발생하지는 않는다.
render_template() 은 첫 번째 매개변수로 랜더링 할 페이지를 보내고, 두 번째 매개변수로 해당 페이지에 보낼 변수를 넘겨준다.
@app.route("/memo") # memo 페이지 라우팅
def memo(): # memo 함수 선언
global memo_text # 메모를 전역변수로 참조
text = request.args.get("memo", "") # 이용자가 전송한 memo 입력값을 가져옴
memo_text += text + "\n" # 이용자가 전송한 memo 입력값을 memo_text에 추가
return render_template("memo.html", memo=memo_text) # 사이트에 기록된 memo_text를 화면에 출력
/flag 페이지에서는 사용자에게 param 변수에 넣을 입력창을 제공합니다.
코드를 보자면 GET 과 POST 요청에 따라 구현한 기능이 다른데요,
GET 일 경우에는 "flag.html" 페이지를 랜더링하는 것을 볼 수 있습니다.
POST 요청을 받을 경우에는 param 변수에 사용자에게 입력받은 값을 저장한 후에 check_xss() 함수로 넘겨줍니다.
@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>'
그러면 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)
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
check_xss()를 보면 파라미터 하나와 쿠키에 해당하는 딕셔너리 하나를 넘겨받고 있는데요,
이 파라미터는 URL 화 되어 쿠키와 같이 다시 read_url 이라는 함수로 넘겨집니다.
read_url()은 add_cookie를 통해 cookie를 추가시키고, get을 통해 사용자가 만들어낸 url 요청을 수행합니다.
그러면 결국에 사용자가 param에 입력한 스크립트가 작동되어 memo 페이지에 플래그가 표시되게 됩니다.
풀이 방법
1. flag 페이지에서 param 으로 스크립트를 작성한다
<script>location.href = "/memo?memo=" + document.cookie;</script>
2. 해당 param 값은 flag 딕셔너리 데이터와 check_xss 로 이동한다
3. 이동한 곳에서 param은 vuln 엔드포인트 뒤에 추가된 채로 url화 되어 다시 쿠키와 read_url 로 이동한다
4. 이동한 곳에서 넘겨받은 플래그 딕셔너리를 쿠키에 추가하고, 사용자의 커스텀 url을 실행한다
5. 그러면 document.cookie에 플래그 값이 추가되어 있기 때문에, 스크립트가 실행되면서 메모 페이지에 플래그가 출력된다
https://dreamhack.io/wargame/challenges/28/
xss-1
여러 기능과 입력받은 URL을 확인하는 봇이 구현된 서비스입니다. XSS 취약점을 이용해 플래그를 획득하세요. 플래그는 flag.txt, FLAG 변수에 있습니다. 플래그 형식은 DH{...} 입니다. 문제 수정 내역
dreamhack.io