Hacking (CTF)/웹 해킹

[Dreamhack/드림핵] xss-1

gapsoo 2023. 11. 5. 19:08


 

 

[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>를 입력하더라도 출력은 &lt;script&gt;로 되기 때문에 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

 

https://hobbylists.tistory.com/entry/XSSCross-Site-Scripting%EA%B3%B5%EA%B2%A9-%EC%8B%A4%EC%8A%B5-Dreamhack-%EC%8B%A4%EC%8A%B5%EC%98%88%EC%A0%9C

 

[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