Hacking (CTF)/Web

[Dreamhack/드림핵] DOM XSS

gapsoo 2023. 11. 5. 13:13

 

 


 


(1) 문제 살펴보기

 

 

첫 화면이다.

 

 

 

 

 

vuln(xss) page 를 누르면

 

이와 같은 페이지가 나온다.

 

 

 

 

 

memo를 누르면

 

 

"hello"라는 문구가 적힌 페이지가 나온다.

 

 

 

 

flag를 눌러보면

 

 

아마 답을 입력할 수 있는 양식이 나오는 것 같다.

 

 

 

 


(2) 문제 풀이 (시도)

 

 

 

먼저, vuln(xss) page를 살펴보겠다.

 

 

이미지 태그가 사용되었다는 것을 추측할 수 있었다.

 

ctrl + u 를 눌러 소스코드를 확인해보았다.

 

 

 

url 주소를 보니 이미지 태그가 사용된 것을 볼 수 있었다.

참고로 "%20"은 스페이스 한 칸을 url 인코딩한 결괏값이다.

 

 

더 이상 알아낼 수 있는 것이 없어서 다른 페이지에 대해서도 알아보기로 했다.

 

 

 

 

 

 

 

 

memo 페이지를 나갔다가 다시 들어오면 방문한 횟수에 따라 "hello"라는 문구가 늘어나있는 것을 확인할 수 있었다.

 

소스 코드를 살펴보았다.

 

 

 

 

 

 

이것저것 살펴보다가, url 주소가 /memo?memo=hello 로 되어있길래,

/memo?memo=flag 라고 입력하고 이동해보았더니 메모에 "flag"도 추가된 것을 볼 수 있었다.

 

 

 

 

 

마지막으로 flag 페이지를 다시 살펴보겠다.

 

 

이 형식은 

 

 

아까 vuln(xss) page 를 눌렀을 때 이동하게 되는 페이지의 url 주소와 형식이 동일하다.

 

 

 

 

 

그래서 그대로 넣어봤더니, "good"이라는 메세지가 적힌 팝업창이 뜨고 다른 변화는 없었다.

 

 

 

 

문제 파일을 받아서 여러가지를 살펴보았다.

 

여러가지 파일이 들어있는 폴더를 받을 수 있었다.

 

 

 

 

그 중에 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)
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)

 

 

이와 같은 내용이었다.

 

그런데 해석을 할 줄 모르겠어서 여기서부터 구글링을 통해 문제를 풀었다.

 

 

 


 


(3) 개념 정리

 

우선 문제 이름이 DOM XSS이니, DOM XSS가 무엇인지 알아보도록 하자.

 

 

 


DOM(Document object Model)

 

웹에서 가장 많이 사용하는 언어 중 하나인 HTML(Hyper Text Markup Language) 등으로 작성된 문서를 계층으로 표현하여 문서 내의 헤드(Head), 폼(Form)과 같은 객체를 변형, 제어할 수 있도록 하는 인터페이스이다. 인터넷 사이트에서 회원 가입 시 메일 주소를 잘못 입력하거나, 전화번호에 숫자가 아닌 문자를 입력할 경우 발생하는 경고창이 대표적인 사례이다. DOM에 의해 HTML로 작성된 문서 내의 객체에 접근할 수 있기 때문에 이러한 형태의 검증이나 제어가 가능하게 된다. 컴파일 없이 실행할 수 있는 인터프리터(Interpreter) 방식의 대표적인 프로그래밍 언어 중 하나인 자바스크립트(JavaScript)에서 HTML로 작성된 문서에 접근할 때 주로 쓰인다.

[네이버 지식백과] DOM [Document Object Model] (두산백과 두피디아, 두산백과)

 

 

DOM의 구조

 

출처: 위키백과


웹페이지의 객체들을 주로 트리(Tree) 형식의 계층화된 구조로 구성하여 순차적으로 접근하는데, 이러한 트리구조의 각 요소를 노드(Node)라고 한다. 브라우저에서 HTML로 작성된 웹 페이지를 로딩(Loading)할 때 트리 구조의 각 노드를 인식해서 하나의 거대한 DOM을 형성하게 되고, 자바스크립트 등을 이용해서 해당 DOM의 노드에 접근할 수 있게 된다. 계층적 구조를 가지기 때문에 트리 구조의 각 노드를 순차적으로 접근해서 속성을 변경하거나 제어할 수 있는 것이다.
[네이버 지식백과] DOM [Document Object Model] (두산백과 두피디아, 두산백과)

 

 

DOM의 사례


인터넷 웹페이지에 기본적으로 포함 되는 대표적인 기능인 클릭(Click)의 경우 아래와 같이 텍스트나 이미지에 "onclick" 이벤트를 적용하고, 실제 버튼을 클릭하였을 경우에는 자바스크립트 등으로 클릭에 대한 함수를 호출하는 방식이다.

 

button id="click_button" onclick="Method_Invocation()"



해당 버튼을 클릭하였을 때 Method_Invocation()이라는 함수를 호출하고, 호출된 함수에서는 DOM에 있는 특정 노드의 속성에 접근해서 원하는 동작을 인터페이스 형태로 제공하게 된다.
[네이버 지식백과] DOM [Document Object Model] (두산백과 두피디아, 두산백과)

 

 

 

 


XSS(Cross Site Scripting) 공격

 

사이트 간 스크립팅(또는 크로스 사이트 스크립팅, 영문 명칭 cross-site scripting, 영문 약어 XSS)은 웹 어플리케이션에서 많이 나타나는 취약점의 하나로 웹사이트 관리자가 아닌 이가 웹 페이지에 악성 스크립트를 삽입할 수 있는 취약점이다.

주로 여러 사용자가 보게 되는 전자 게시판에 악성 스크립트가 담긴 글을 올리는 형태로 이루어진다. 이 취약점은 웹 어플리케이션이 사용자로부터 입력 받은 값을 제대로 검사하지 않고 사용할 경우 나타난다. 이 취약점으로 해커가 사용자의 정보(쿠키, 세션 등)를 탈취하거나, 자동으로 비정상적인 기능을 수행하게 할 수 있다. 주로 다른 웹사이트와 정보를 교환하는 식으로 작동하므로 사이트 간 스크립팅이라고 한다.

 

 

 

 

XSS 공격의 종류

 

  • Stored XSS 
  • Reflected XSS
  • DOM based-XSS  ➡️ 이 문제에서 다룰 XSS 공격
  • Universal XSS

 

 

 


DOM-BASED XSS

 

  • 클라이언트의 브라우저에서 DOM 환경을 수정한 결과 공격 페이로드가 실행되는 방식
  • 페이지 자체(HTTP 응답)는 변경되지 않지만, 페이지에 포함된 클라이언트 측 코드는 DOM 환경에서 발생한 악의적인 변조로 인해 공격 구문이 실행됨
  • Stored와 Reflected가 서버 측 어플리케이션 취약점으로 인한 공격인 반면, DOM-based XSS는 서버와 관계 없이 브라우저에서 발생

 

 

 

 

[공격 흐름]

 

 

웹 브라우저의 응답 페이지에 포함된 자바스크립트가 동작

→ DOM 객체 실행

→ URL에 포함된 악성 스크립트 동작

 

 

 

 

 

 


[4] 문제 풀이

 

app.py

 

CSP가 적용되어 있고 'strict-dynamic' 가 보인다.

이는 '신뢰할 수 있는 스크립트에 의해 동적으로 추가된 스크립트를 허용하도록 지시하는 것'이라고 한다.

 

 

스크립트 태그의 id를 name으로 한 뒤 #뒤로 악성 코드를 주입하여 쿠키를 탈취하면 되는 문제라고 한다.

(왜 쿠키에 flag 값이 있는 것인지는 이해하지 못했다.)

 

 

 

<script id="name"></script>

 

- param으로 id="name"인 <script> 태그를 삽입한다.

 

 

#location.href='/memo?memo='+document.cookie;//

 

- name으로 다음과 같이 건네주면 된다

- 신뢰 가능한(nonce 값 인증된) 스크립트에 의해 동적으로 추가되므로(using innerHTML) 전혀 문제되지 않는다

 

 

 

 

 

flag 값을 찾을 수 있었다! 그런데 문제를 이해하려고 노력해도 어렵게 느껴졌다...

웹에 대한 공부를 하다보면 나중에 이 문제를 완벽하게 이해하는 날이 오지 않을까 싶다...

 

 

 


[5] 참고

 

https://keyme2003.tistory.com/entry/dreamhack-DOM-XSS

 

[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

 

https://velog.io/@jckim22/%EB%B9%A1%EA%B3%B5%ED%8C%9F-7%EC%A3%BC%EC%B0%A8-%EA%B3%BC%EC%A0%9C-19-%EB%93%9C%EB%A6%BC%ED%95%B5-%EC%9B%8C%EA%B2%8C%EC%9E%84-DOM-XSS

 

[WARGAME] 드림핵 워게임 - DOM-XSS

먼저 아래 서버 코드를 보자.DOM XSS라 그런지 기분 탓인지 모르게 코드가 별게 없어 보인다.vuln.htmlDOM-XSS는 브라우저 단에서 일어나기 때문에 아무래도 웹 페이지에 스크립트 코드가 핵심일 것이

velog.io

 

https://minseosavestheworld.tistory.com/148

 

[Dreamhack Wargame] DOM XSS

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

minseosavestheworld.tistory.com

 

 

댓글수0