우선 문제 설명을 보면, 문제 이름에서도 볼 수 있고, 설명에서도 볼 수 있듯이 DOM XSS에서 실습하는 문제라고 되어있다. 그럼 본격적으로 문제 풀이를 시작하기 전에 DOM XSS가 무엇인지 알아보자.
DOM (Document Object Model)
HTML의 문법은 태그의 집합으로 구성되어 있고 이러한 태그들은 아래 사진과 같이 트리 구조로 객체가 형성되는데 이러한 트리 구조 집합을 DOM 구조라고 한다.
DOM 구조에 접근하기 위해서는 JavaScript와 같은 스크립트 언어를 사용해야 DOM 구조에 접근할 수 있다.
Ex. Document.write, Document.cookie 등
DOM Based XSS
DOM 구조를 이용하여 요소들을 수정하거나 추가하는 등 동적 행위를 할 때 접근하는 JavaScript에 악성 스크립트를 삽입하여 클라이언트 측 브라우저에서 악성 스크립트가 실행되도록 하는 공격
▶ Reflected XSS와 같이 동적 페이지를 구성하는 과정 상에서 발생되는 XSS 공격이지만, Reflected XSS는 서버 측에서 동적 페이지를 구성하는 환경에서 발생되는 XSS이다.
▶ 반면에 DOM Based XSS는 클라이언트 측에서 사용자 입력 값을 통해 동적 페이지를 구성하는 환경에서 발생되는 XSS 취약점이다. 즉, 요청이 서버로 전송되지 않고 클라이언트 브라우저에서 공격이 이루어지는 특징을 가지고 있다.
출처 : https://maker5587.tistory.com/57
그럼 이제 DOM Based XSS에 대해 알아본 내용을 바탕으로 문제를 풀어보자.
문제 페이지에 접속하면 아래와 같은 화면을 볼 수 있다.
우선 나와있는 3개의 카테고리를 클릭하여 어떤 내용이 있는지 확인해보았다.
첫번째로 vuln(xss) page를 클릭하니 'dreamhack is my name !'이라는 문구가 있는 페이지를 볼 수 있었고
두번째 memo 카테고리에서는 다음과 같이 hello 문구가 출력되고 있었는데, 처음에 들어 갔을 때는 hello가 한개였다. 따라서 해당 페이지에 접속할 때마다 hello가 한줄 씩 추가되는 것을 알 수 있었다.
마지막으로 flag 페이지에는 아래와 같이 파라미터 값을 입력할 수 있는 공간이 있다. 여기에 값을 입력하여 플래그 값을 얻을 수 있는 것으로 보인다.
각 페이지에서 문제 해결을 위한 별도의 정보를 얻을 수 있는 부분이 없으므로 문제 코드를 살펴보자.
우선, app.after_request 부분의 코드를 보면 response.headers 부분을 통해 CSP가 적용되어 있는 것을 볼 수 있다.
CSP란? (Content Security Policy)
웹 브라우저에서 각종 XSS공격을 막기위해 만들어진 정책으로, 아무나 인라인 자바스크립트나 CSS를 입력할 수 없도록한다.
출처) https://velog.io/@dung002/CSP%EB%9E%80
strict-dynamic이란?
이미 허용된 스크립트의 내부에서 동적으로 생성된 스크립트를 허용하는 옵션
출처) https://hs-archive.tistory.com/110
따라서, 해당 코드를 통해 동적으로 추가된 스크립트를 허용하고 있음을 알 수 있다.
@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
또한, 아래 코드를 통해 알 수 있는 것은 다음과 같다.
@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")
name = request.form.get("name")
if not check_xss(param, name, {"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>'
- 플래그가 담긴 쿠키와 함께 /vuln?param=[param input]#[name input] 에 접속하도록 하고 있음
- location.href 와 document.cookie를 이용해 외부 request bin이나 /memo 엔드포인트에 get 요청을 보내야 함
문제 사이트 카테고리 중 'vuln page'에서 개발자 도구를 통해 소스코드를 확인해보면 아래와 같은 코드를 볼 수 있는데 해당 코드는 HTML Injection이 가능한 param에 대해 아무런 필터링이 이루어지지 않고 있다.
</div> <!-- /container -->
<!-- Bootstrap core JavaScript -->
<script src="/static/js/jquery.min.js" nonce=eaf8d55293312fd5e31861b26eeda70f></script>
<script src="/static/js/bootstrap.min.js" nonce=eaf8d55293312fd5e31861b26eeda70f></script>
vuln.html의 소스코드를 통해 알 수 있는 것은 다음과 같다.
<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>
- param을 통해 HTML injection이 가능하며, 해시값은 name_elem의 innerHTML 값으로 들어간다
- URL 해시 값에 대해 URL decoding 과정을 안거치므로 <, > 사용 불가. HTML element 삽입 불가능
- <pre id="name"> element보다 위에 injection 가능하다
▶ 따라서, id="name"인 element를 삽입한다면 document.getElementById("name") 결과 내가 삽입한 객체가 리턴될 것이다.
최종적으로, param 값으로는 id="name"인 <script> 태그를 삽입한다
→ empty tag이므로 CSP에 걸리지 않는다
<script id="name"></script>
다음으로 name 부분에는 신뢰 가능한(nonce 값 인증된) 스크립트에 의해 동적으로 추가되므로(using innerHTML) 전혀 문제되지 않는 코드를 다음과 같이 삽입해준다.
#location.href='/memo?memo='+document.cookie;//
이렇게 찾은 값들을 flag 카테고리에 다음과 같이 입력하면
아래 화면처럼 'good'이라는 alert 창이 뜬다.
이렇게 창이 뜨면 memo 부분에서 플래그 값을 확인할 수 있다.
나는 플래그 값을 memo 부분에서 확인할 수 있다는 것을 잊고 왜 플래그 값이 안나오지? 이생각 하면서 여러번 시도하다보니 플래그 값이 왕창 나와버렸다 ㅋㅋㅋㅋㅋㅋ
이렇게 해서 얻은 플래그 값을 드림핵 사이트에 입력하면 문제를 풀 수 있다.
'CTF' 카테고리의 다른 글
[Dreamhack] xss-2 write-up (0) | 2023.11.04 |
---|---|
[Dreamhack] xss-1 write-up (0) | 2023.11.04 |
[XSS-GAME] level5(reflected xss) write-up (0) | 2023.11.04 |
[LOS] orc write-up (0) | 2023.11.01 |
[LOS] goblin write-up (0) | 2023.11.01 |