app.py
#!/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)
read_url(url, cookie):
cookie -> {"domain": "127.0.0.1"} update됨
chrome 드라이버가 http://127.0.0.1:8000/에 GET 요청 보냄
cookie 더함
url에 GET 요청 보냄
check_xss(param, cookie):
cookie -> {"domain": "127.0.0.1"} update됨
chrome 드라이버가 http://127.0.0.1:8000/에 GET 요청 보냄
cookie 더함
param을 포함한 새 url에 GET 요청 보냄
/로 들어오면:
index.html 렌더링됨
/vuln으로 들어오면:
vuln.html 렌더링됨
/flag로 들어오면:
요청이 GET이면 -> flag.html 렌더링됨
요청이 POST이면 ->
check_xss(요청의 param, {"name": "flag", "value": FLAG.strip()})
위 함수 반환이 False이면 -> '<script>alert("wrong??");history.go(-1);</script>' 반환
'<script>alert("good");history.go(-1);</script>' 반환
/memo로 들어오면:
요청으로부터 memo를 추출하고, 전역 변수인 memo_text에 누적
memo.html을 렌더링하여 memo_text 출력
vuln.html
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<div id='vuln'></div>
<script>
var x = new URLSearchParams(location.search);
document.getElementById('vuln').innerHTML = x.get('param');
</script>
{% endblock %}
※ location.search: 현재 URL의 query string 부분 (URL의 ? 이후 부분)
현재 URL의 param 부분을 현재 document 내에 넣는다.
그럼 브라우저는 vuln.html 내 태그들을 다시 parse하고 (DOM update),
새로운 태그가 img, script 등일 경우 이들에 대한 GET 요청을 보낸다.
즉, URL의 query string 중 param에 따라 페이지의 내용이 변경될 수 있는 취약점이 있다.
flag.html
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<form method="POST">
http://127.0.0.1:8000/vuln?param=<input type="text" name="param"/><br/>
<input type="submit"/><br/>
</form>
{% endblock %}
화면의 form에 채운 값은 POST 요청의 param이 된다.
Flag가 어디에 있는가?
/flag에서 form(= URL의 param이 된다)을 @@@으로 채워 POST 요청을 보내면,
check_xss() 함수를 통해
cookie ({"name": "flag", "value": FLAG.strip()})를 add한 뒤
http://127.0.0.1:8000/vuln?param=@@@에 GET 요청을 보낸다.
따라서, /flag에 POST 요청을 보내면 flag값을 cookie에 담을 수 있는 것이다.
param에 뭘 쓸까?
param에 <script>alert(1)</script>와 같은 스크립트를 넣어 XSS를 발생시키고 싶지만, 여기서는 불가능하다.
∵
render_template 함수를 사용하면,
전달된 템플릿 변수가 기록될 때 HTML 엔티티코드로 변환돼 저장되기 때문에,
이용자가 입력한 값을 페이지에 그대로 출력하지 않아 XSS가 발생하지 않는다.
그럼, 만약 param에 <img src="..." onerror="...">가 들어간다면?
img의 src인 URL에 이미지가 존재하지 않는 경우, onerror로 설정한 event handler에 의해 javascript 코드가 실행된다.
그러니, src에 이상한 URL을 적고 onerror를 통해 /memo에 cookie를 write한다면, cookie값을 볼 수 있을 것이다.
/memo에 cookie를 write하기 위해서는,
i) /memo로 redirection하고
location.href='/memo를 통해 현재 document의 URL (location.href)을 /memo로 바꾸면 브라우저가 여기로 redirect한다.
ii) memo parameter를 document.cookie로 설정하면 된다.
?memo='+document.cookie
최종 exploit은 아래와 같다:
<img src="XSS-2" onerror="location.href='/memo?memo='+document.cookie">
'IT > Wargame' 카테고리의 다른 글
[Web hacking] csrf-2 (0) | 2024.07.06 |
---|---|
[Web Hacking] csrf-1 (2) | 2024.07.05 |
[System Hacking] fho ㅡ Hook overwrite (1) | 2024.05.17 |
[System Hacking] basic_rop_x64 (0) | 2024.03.19 |
[System Hacking] ssp_001 (0) | 2024.03.07 |