Webhacking-Write-Up/Dreamhack

[Dreamhack] CSP Bypass

SlowTurtle_ 2023. 1. 10. 06:31
728x90

[Dreamhack] CSP Bypass

우선 코드를 보자.

#!/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'alert("wrong??");history.go(-1);' return f'alert("good");history.go(-1);' 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)

우선 /vuln을 보면 아무런 필터링조차 하지 않고 입력값을 페이지에 출력함으로써  XSS공격이 쉽게 가능해보인다. memo부분은 render_template함수를 이용해서 memo.html을 출력하는데 render_template함수가 변수를 HTML 엔티티코드로 변환하여 저장하기 때문에 XSS공격을 이용할 수 없다. 또한 none은 계속 바뀌어 생성되어 예측한다는 것은 불가능이다.

그렇다면 vuln을 이용해서 스크립트를 실행시켜보자.

<script src='/vuln?param=alert(1)'></script>

alert(1)이 정상적으로 실행되는 것을 알 수 있다. 이것을 document.location과 document.cookie를 활용해서 조금 수정해서 memo페이지에 받아보자.

<script src="/vuln?param=document.location='/memo?memo='+document.cookie"></script>

왜 안될까...싶어서 이것저것 계속 바꿔보고 시도하다가 힌트를 얻기위해 드림핵 함께실습을 보았는데...

스크립트 부분은 두 단계에 거쳐서 파라미터로 해석되기때문에 URL 디코딩되어 공백으로 해석되었기 때문이었다..

그렇다면 +를 인코딩하면 %2b가 나오기때문에 +대신 %2b를 넣어 다시 제출해봤다.

flag값을 획득할 수 있었다.

728x90