본문 바로가기
드림핵

[드림핵] file-csp-1 풀이

by jwcs 2024. 2. 5.
728x90

https://dreamhack.io/wargame/challenges/36

 

file-csp-1

문제에서 요구하는 조건에 맞게 CSP를 작성하면 플래그를 획득할 수 있습니다. Reference Introduction of Webhacking

dreamhack.io

csp에 대해 공부해볼 수 있는 문제다

 

/

초기 페이지다. Test 페이지와 Verify 페이지로 나뉘어져 있는 것이 보인다.

 

/test
/verify

 

 

#!/usr/bin/env python3
import os
import shutil
from time import sleep
from urllib.parse import quote

from flask import Flask, request, render_template, redirect, make_response
from selenium import webdriver

from flag import FLAG

APP = Flask(__name__)


@APP.route('/')
def index():
    return render_template('index.html')


@APP.route('/test', methods=['GET', 'POST'])
def test_csp():
    return render_template('test.html')


@APP.route('/verify', methods=['GET', 'POST'])
def verify_csp():
    global CSP
    if request.method == 'POST':
        csp = request.form.get('csp')
        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(f'http://localhost:8000/live?csp={quote(csp)}')
            try:
                a = driver.execute_script('return a()');
            except:
                a = 'error'
            try:
                b = driver.execute_script('return b()');
            except:
                b = 'error'
            try:
                c = driver.execute_script('return c()');
            except Exception as e:
                c = 'error'
                c = e
            try:
                d = driver.execute_script('return $(document)');
            except:
                d = 'error'

            if a == 'error' and b == 'error' and c == 'c' and d != 'error':
                return FLAG

            return f'Try again!, {a}, {b}, {c}, {d}'
        except Exception as e:
            return f'An error occured!, {e}'

    return render_template('verify.html')


@APP.route('/live', methods=['GET'])
def live_csp():
    csp = request.args.get('csp', '')
    resp = make_response(render_template('csp.html'))
    resp.headers.set('Content-Security-Policy', csp)
    return resp


if __name__ == '__main__':
    APP.run(host='0.0.0.0', port=8000, threaded=True)

app.py와 코드다.

 

test 페이지와 verify 페이지 둘 다 csp.html에 요청을 보내게 된다.

<!doctype html>
<html>
	<head>
	<!-- block me -->
	<script>
		function a() { return 'a'; }
		document.write('a: block me!<br>');
	</script>
	<!-- block me -->
	 <script nonce="i_am_super_random">
		function b() { return 'b'; }
		document.write('b: block me!<br>');
	</script>
	<!-- allow me -->
	<script
  src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
  integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8="
  crossorigin="anonymous"></script>
	<!-- allow me -->
	 <script nonce="i_am_super_random">
		function c() { return 'c'; }
		document.write('c: allow me!<br>');
		try { $(document); document.write('jquery: allow me!<br>'); } catch (e) {  }
	</script>
	</head>
</html>

csp.html의 내용은 위와 같다.

해석해보면 스크립트가 줄지어 있는데, a,b는 실행시키지 못하도록 막고 c,d는 실행시켜야 한다.

@APP.route('/live', methods=['GET'])
def live_csp():
    csp = request.args.get('csp', '')
    resp = make_response(render_template('csp.html'))
    resp.headers.set('Content-Security-Policy', csp)
    return resp

csp에 대한 헤더를 `/live`에 의해 설정되고 있다. 그럼 이 헤더에 들어갈 값을 넣어주면 될 것이다.

 

a 함수의 경우, 인라인으로 스크립트가 삽입되어 있다. csp는 `script-src [허용해줄 것]` 의 형식으로 사용된다. 따라서 우리는 unsafe-inline 의 사용을 하지 않고 c와 d를 허용해주어야한다.

b 함수의 경우, nonce값으로 `i_am_super_random` 값을 가지고 있다. c와 d가 포함되어있는 스크립트도 nonce값으로 이 값을 가지고 있다. 따라서 nonce 값으로 c와 d를 허용시키는 방법은 막힌 상태이다.

 

그럼 우리는 unsafe-inline과 nonce값을 사용하지 않고 c와 d만을 허용시킬 방법을 찾아야한다.

여기에서 우리는 hash 값을 사용할 수 있다. 스크립트 태그 안에 내용들을 해싱하고, 그 값을 넣어주는 것이다. 스크립트 태그 안에 내용들을 해싱할 때 들여쓰기까지 신경써서 복사해주자.

복사 값

위와 같이 나왔다.

 

https://report-uri.com/home/hash

 

CSP Hash Generator

Our CSP Hash Generator creates hash values of assets for allowing in a Content Security Policy.

report-uri.com

이 사이트에서 해싱했다.

 

직접 해싱하지 않고도 `script-src 'none'`을 입력하고 개발자 도구의 콘솔을 살펴보면 해싱 값을 볼 수도 있다.

 

개발자 도구 콘솔창

 

 

script-src 'sha256-l1OSKODPRVBa1/91J7WfPisrJ6WCxCRnKFzXaOkpsY4='

이런 값을 입력창에 입력해보았다.

 

입력 결과

d가 제대로 출력되지 않는다.

 

<script
  src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
  integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8="
  crossorigin="anonymous"></script>
	<!-- allow me -->
	 <script nonce="i_am_super_random">
		function c() { return 'c'; }
		document.write('c: allow me!<br>');
		try { $(document); document.write('jquery: allow me!<br>'); } catch (e) {  }
	</script>

try/catch 구문을 살펴보면 `$(document)`가 보인다. 이것은 jquery의 문법임을 알 수 있다. jquery를 가져와야 하는데 위의 스크립트에서 가져오고 있다. 위의 스크립트에서 hash값을 가지고 있는데, 이 값을 입력해줘도 잘 작동한다.

 

script-src 'sha256-l1OSKODPRVBa1/91J7WfPisrJ6WCxCRnKFzXaOkpsY4=' 'sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8='

실행 결과

이 방법 외에도 참조하는 저 url을 직접 넣어줘도 된다.

script-src 'sha256-l1OSKODPRVBa1/91J7WfPisrJ6WCxCRnKFzXaOkpsY4=' https://code.jquery.com/jquery-3.4.1.slim.min.js

 

test 페이지와 verify 페이지 둘 다 같은 csp.html을 사용하기 때문에 그대로 verify 페이지의 입력창에 써주면 flag를 얻을 수 있다.

 

짜잔

 

728x90
반응형

'드림핵' 카테고리의 다른 글

[드림핵] crawling 풀이  (0) 2024.02.11
[드림핵] weblog-1 풀이  (0) 2024.02.10
[드림핵] Dream Gallery 풀이  (0) 2024.01.22
[드림핵] login-1 풀이  (1) 2024.01.13
[드림핵] baby-sqlite 풀이  (2) 2024.01.10