rsp-∞
[Dreamhack] xss-1 본문
서버를 생성하여 홈페이지를 먼저 본다.
첫 번째 vuln(xss)page 항목에 들어가 보았더니 아래 사진과 같은 알림창이 내려왔다.
1이 나왔는데, 이게 xss 취약점을 가지고 있다는 것을 알려 준다. 위 url을 보면 지금 <script>aler(1)</script> 명령어가 url에 포함되어 해당 스크립트가 실행되고 있다는 것을 알 수 있기 때문이다. 그렇다면 이 페이지는 <script> 태그에 내성이 없는 상태. 그러므로 우리가 <script> 태그를 사용하여 xss 취약점을 공략해 볼 수 있을 것이다.
그 다음은 memo 페이지에 들어가 본다.
hello가 출력된다. url에 memo?memo=hello가 써 있는 것을 볼 수 있다. hello를 지우고 다르게 수정하면 어떻게 출력되나 보자.
hi라고 입력했더니 hi라고 메모장에 쓰인 걸 볼 수 있다. 그렇다면 저 url의 값을 조작함으로써 이 화면에 플래그를 출력시킬 수 있지 않을까?(사실 냅다 flag를 넣어서 url을 수정해 보기도 했다. ㅎㅎ)
문제 파일을 다운로드 받고 코드를 살펴보자.
#!/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**]"
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
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)
활용할 수 있는 명령어로 location.href와 document.cookie가 있다. 문제가 자바 스크립트에 기반하고 있기 때문에 명령어를 html에서 실행시키기 위해 <script></script> 태그를 사용하면서, 위 두 개의 자바 스크립트 속성을 사용하면 문제를 풀 수 있다.
먼저 location.href는 해당 페이지의 url 주소를 나타낸다. 그러므로 이 값을 바꾸면 페이지 간의 이동이 가능하게 되고, 즉 뒤에 따라붙는 명령어가 실행되는 페이지를 다르게 할 수 있다. document.cookie는 브라우저에 저장된 쿠키 정보를 다룰 수 있는 속성이다. 문자열 형태로 쿠키를 읽고, =로 값을 지정해 설정할 수 있다.
코드를 살펴보면 /vuln 부분에서 사용자가 입력한 파라미터 값인 param을 그대로 return하고 있는 것을 볼 수 있다. 여기가 xss의 주요 진입점이라고 할 수 있다. 학회에서 OWASP 취약점 중 improper output handling을 다루며 xss의 두 가지 유형을 발표했는데, 여기서는 공격자가 직접 악성 스크립트를 url에 삽입하여 클라이언트를 대상으로 공격을 수행하는 reflected xss에 해당한다고 볼 수 있다.
read_url()과 check_xss()는 자동 방문 기능을 수행한다. 서버는 사용자가 입력한 param 값을 check_xss() 함수로 넘긴다. 사용자의 XSS 스크립트가 실행되면 봇이 쿠키를 포함한 정보에 노출될 수 있는데, 코드 34번째 줄의 driver.add_cookie(cookie)를 보면 flag인 쿠키 값을 들고 포함하고 있고 공격자가 xss 페이로드를 통해 이 쿠키 값을 탈취하면 flag를 획득할 수 있다. 이 내용을 포함한 파라미터를 전달하면 서버는 해당 파라미터를 포함한 url을 클라이언트가 방문하도록 하며 document.cookie에 접근이 가능하고, 해당 쿠키에 포함된 flag 값을 도출해 낼 수 있다.
코드의 끝에 보면 /memo라고 하여 memo 페이지가 어떻게 구성되는지 알 수 있는데, 필터링 없이 입력을 받아 html에 내보고 memo에서 출력하는 내용 뒤에 붙어 출력된다. 아래와 같이 페이로드를 작성하였다.
<script>location.href="/memo?memo=hello"+document.cookie;</script>
띄어쓰기, 따옴표, 세미콜론 등 조건이 꽤나 까다롭지만 전부 다 알맞게 되어야만... 플래그가 제대로 뜬다. 닫히는 따옴표가 앞에 붙어 한참 동안 플래그가 뜨지 않아 고생했던.
사진과 같이 memo에 플래그가 hello 뒤에 붙어 출력된 것을 알 수 있다. 내가 보낸 페이로드에서 url 자체를 memo=hello라고 설정해 놓았지만, 아마 hello를 지우면 flag=DH{....} 형식으로 출력될 것이다.
'Write-ups > web' 카테고리의 다른 글
[Dreamhack] csrf-1 (0) | 2025.04.04 |
---|---|
[Dreamhack] xss-2 (0) | 2025.04.04 |
[DreamHack] session-basic (0) | 2025.03.27 |
[DreamHack] cookie (0) | 2025.03.27 |
[DreamHack] devtools-sources (0) | 2025.03.20 |