rsp-∞
[Dreamhack] xss-2 본문
서버를 생성하고 사이트에 접속하면 전체적인 인터페이스는 똑같지만 여기서 차이가 있다.
vuln(xss)page에서 url에 똑같이 <script> 태그를 사용하여 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**]"
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():
return render_template("vuln.html")
@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)
이 코드의 /vuln 부분에서 사용자가 입력한 param 값을 직접 출력하지 않고, 정적 html인 param.html로 렌더링한다. 그러므로 xss-1에서 보았던 reflected xss 취약점이 제거된 것을 알 수 있다. 그러나 stored xss 취약점은 여전히 존재한다. /memo 엔드포인트가 여전히 필터링 없이 데이터를 저장하고 출력하고 있기 때문이다.
그렇다면 <script> 태그는 사용이 불가능하고, 다른 사용 가능한 자바 스크립트 태그를 찾아보자. 구글링하면 쉽게 찾아볼 수 있는데, 대표적으로 iframe, img, object 등이 있다. 각 태그에 따라 html에 출력시키는 형식이 다른데, iframe은 html 문서 내에 또 다른 html 문서를 불러올 수 있다고 한다. XSS가 발생한 페이지와 데이터를 가져올 페이지가 다를 경우에는 <iframe>을 사용하면 된다.
[출처] XSS IFRAME 활용 / XSS 대응방안 11-2|작성자 leem8419
코드가 어떻게 동작하는지는 위 차이점을 제외하면 xss-1과 유사하므로 따로 설명하지는 않겠다. <iframe> 태그를 사용하여 페이로드를 작성할 때 주의할 점은 <script> 태그와 작성 양식이 조금 다르다는 것뿐이다. 그 외의 내용은 xss-1에 보낸 페이로드와 크게 다르지 않다. 페이로드는 아래와 같이 작성하였다.
<iframe src="javascript:location.href='/memo?memo='+document.cookie"></iframe>
페이로드를 자세히 보면 <iframe> 태그를 사용하고, 그 외의 location.href와 document.cookie 속성을 xss-1과 동일하게 사용한 것을 볼 수 있다. iframe 뒤에 src가 붙는 이유와 javascript:의 기능이 궁금하였는데, 먼저 javascript:는 <iframe> 태그가 기본적으로 자바가 아닌 html에서 사용하는 태그라서, 해당 태그를 자바를 거쳐 자바 스크립트로 기능할 수 있도록 붙여 주는 일종의 사족이다. 반대로 javascript:를 사용하지 않는 <script>는 html에서 자바 스크립트를 사용할 때 쓰는 태그이다. 그러므로 굳이 javascript:를 붙여 주지 않아도 자연스럽게 자바를 거쳐 사용되는 태그인 것이다.
iframe 뒤에 src가 붙는 이유는 간단하다. iframe이 기본적으로 한 html 안에 다른 html을 불러 오는 기능을 하기 때문에, 어떤 웹 페이지를 불러 올지 지정해야 한다. 그 기능을 하는 것이 src 속성이다. 그러므로 위 페이로드를 해석하면 iframe 태그를 사용하여 다른 웹 페이지를 불러올 것이고, 그 과정에서 자바를 거쳐 location.href='memo?memo=' 페이지로 이동한다. 그리고 해당 페이지에서 document.cookie에 포함된 쿠키가 가지고 있는 flag를 출력시킨다.
다음과 같이 플래그를 획득할 수 있었다.
'Write-ups > web' 카테고리의 다른 글
[Dreamhack] csrf-2 (0) | 2025.04.04 |
---|---|
[Dreamhack] csrf-1 (0) | 2025.04.04 |
[Dreamhack] xss-1 (0) | 2025.04.04 |
[DreamHack] session-basic (0) | 2025.03.27 |
[DreamHack] cookie (0) | 2025.03.27 |