XSS(Cross Site Scripting) 취약점 - 우회 방법 및 대응 방안

by Toff

   개요
이번 포스팅에서는 XSS(Cross-Site Scripting) 취약점우회 방법과 대응 방안에 대해서 알아보자. XSS 취약점의 기본적인 개념 설명은 다음 포스팅에서 살펴보도록 한다.
http://dntsecurity.blogspot.kr/2017/01/xsscross-site-scripting.html

해당 포스팅에서는 XSS의 간단한 대응 방안을 세우고, 해당 대응 방안에 대한 우회 방법을 실습하는 순으로 진행한다. ASP로 이루어져 있는 쇼핑몰을 대상으로 게시판에서 실습을 진행한다.

우회 방법과 대응 방안에 대해 지속적으로 연구중이며, 해당 포스팅에서의 우회 방법과 대응 방안은 완벽한 것이 아님을 미리 알린다.



   대응 방안(기본)
대부분 XSS 취약점을 진단할 때 단순히 <script>alert("1");</script> 의 구문만을 삽입하는 경우가 많다. 해당 공격 코드만을 생각했을 때의 간단한 대응 방안을 표 1-1 과 같이 작성했다. 게시판에 문자열이 들어왔을 때 <script> 와 </script> 구문이 있을 때 각각 A와 B로 치환해 값을 저장하는 방식이다.
ls_Content = Replace(ls_Content, "<script>", "A")
ls_Content = Replace(ls_Content, "</script>", "B")
표 1-1 간단한 대응 방안

대응 방안을 적용한 후에 그림 1-1 과 같이 문자열을 삽입한다. 
그림 1-1 문자열 치환 확인(1)

그림 1-2 와 같이 <script> 와 </script> 값이 각각 A 와 B 로 치환된 것을 알 수 있다.
그림 1-2 문자열 치환 확인(2)


   우회 방법(대소문자 이용)
위의 대응 방안의 우회 방법은 굉장히 많다. 첫번째는 위의 대응 방안에서는 소문자에 해당하는 값들만 치환한 것을 알 수 있다. 이를 이용해 그림 1-3 과 같이 문자에 대문자를 섞어 문자열을 삽입한다.
그림 1-3 대문자로 우회 (1)

그림 1-4 와 같이 스크립트 문이 실행되면서 우회에 성공한다.
그림 1-4 대문자로 우회 (2)



   우회 방법(HTML 태그 속성 이용)
두번째로 HTML 태그 속성을 이용한 우회가 있다. 위에서 설명했듯이 특정 문자열만 치환하는 방식을 사용했기에 해당 우회 방법이 가능하다. 그림 1-5 와 같이 img 태그를 사용해서 onmouseover 옵션을 사용한다.
그림 1-5 HTML 태그 속성 이용 우회 (1)

그림 1-6 과 같이 특정 이미지 파일에 마우스를 올렸을 경우 스크립트가 실행된다. onmouseover 외에도 다양한 옵션으로 사용이 가능하다.
그림 1-6 HTML 태그 속성 이용 우회 (2)


   우회 방법(문자열 분리)
마지막으로 문자열 분리를 이용한 우회 방법이다. 위의 대응 방안에서는 특정 문자열 전체를 치환했기때문에 자바스크립트의 '+' 와 'eval' 함수를 사용해서 우회한다. 코드를 확인하면 문자열이 모두 잘려 있어 정상적으로 치환되지 않고 '+'와 eval 함수를 사용해 정상적으로 동작하게끔 삽입한다.
그림 1-7 문자열 분리 이용 우회 (1)

그림 1-8 과 같이 특정 이미지 파일을 클릭 할 경우 스크립트가 실행된다. 

그림 1-8 문자열 분리 이용 우회 (2)



   대응 방안(대소문자 이용)
대소문자를 이용한 우회 방법에 대한 대응 방안은 문자열 값이 들어왔을 때 먼저 모두 소문자 혹은 대문자로 치환하면 된다. 표 1-2 와 같이 LCase 명령어를 사용해 모든 문자열 값을 소문자로 치환한 후에 <script> 와 </script> 값을 각각 A 와 B 로 치환한다.
 ls_Content = LCase(ls_Content)
 ls_Content = Replace(ls_Content, "<script>", "A")
 ls_Content = Replace(ls_Content, "</script>", "B")
표 1-2 대소문자 치환

대응 방안을 적용한 후에 그림 1-7 과 같이 문자열을 삽입한다.
그림 1-9 대소문자 대응 방안

그림 1-8 과 같이 <script> 와 </script> 값이 각각 A 와 B 로 치환된 것을 알 수 있다.
그림 1-10 문자열 치환 확인


   대응 방안(최종)
위는 대소문자 우회 방안의 대응 방안이기 때문에 HTML 태그와 문자열 분리를 이용하면 결국 다시 우회된다. 대소문자, HTML 태그 이용, 문자열 분리 등 현재까지 연구한 우회 방안에 대한 최종 대응 방안은 다음과 같다.

- 태그의 사용이 불필요한 경우

먼저 태그의 사용이 필요없는 경우이다. 이럴 경우에는 모든 태그 사용이 불가능하도록 치환하는 방법을 사용한다. 모든 태그는 '<', '>' 문자를 통해 사용된다. 표 1-3 과 같이 해당 문자열을 출력만 정상적으로 되게끔 치환한다.
ls_Content = Replace(ls_Content, "<", "&lt;")
ls_Content = Replace(ls_Content, ">", "&gt;")
표 1-3 '<', '>' 치환

대응 방안을 적용한 후에 그림 1-11 과 같이 우회 방법에서 사용했던 모든 문자열을 삽입한다.
그림 1-11 우회 문자열 삽입

그림 1-12 와 같이 '<', '>' 태그를 각각 &lt; &gt; 로 치환해서 스크립트를 문자열로 인식해 출력한 것을 알 수 있다. 글을 수정하면 그림 1-13 과 같이 '<', '>' 문자열이 치환되어 있다.
그림 1-12 치환 확인(1)

그림 1-13 치환 확인(2)


- 태그의 사용이 필요한 경우

게시판 같은 곳은 불가피하게 태그를 사용해야 하는 경우가 있다. 이럴 경우에는 블랙리스트/화이트리스트 방식을 사용해 사용해야 하는 특정 태그만 사용해야 한다. 블랙리스트의 경우 javascript, eval, documnet, onload, iframe, div, alert, onclick, onkeydown 등 악의적으로 행위할 수 있는 모든 태그를 막아줘야하기 때문에 비효율적일 수 있다. 여기선 화이트리스트 방식을 사용해 <p> 태그만 사용한다는 가정하에 대응 방안을 세운다.

표 1-4 와 같이 위와 동일하게 "<", ">" 태그를 모두 치환하며 <P> 태그만 다시 치환함으로써 사용가능 하게끔 한다.
ls_Content = Replace(ls_Content, "<", "&lt;")
 ls_Content = Replace(ls_Content, ">", "&gt;")
 ls_Content = Replace(ls_Content, "&lt;P&gt;", "<p>")
표 1-4 <p> 태그를 제외한 태그 모두 치환

대응 방안을 적용한 후에 그림 1-14 와 같이 우회 방법에서 사용했던 모든 문자열과 <P> 태그를 삽입한다. 
그림 1-14 치환 확인(1)

위와 동일하게 모든 태그는 실행이 안되며 <P> 태그만 실행됨을 알 수 있다. 
그림 1-15 치환 확인(2)


- 공통적으로 적용

위의 대응 방안과 더불어 추가로 입력 값 검증을 해줘야 한다. 기본적으로 XSS 취약점은 <script>alert("1");</script> 와 같이 사용하려면 아무리 짧아도 10자는 넘어야 한다. 내용/이메일/제목 등의 필드는 길이를 제한하기에 애매한 부분이 있지만 이름과 같은 필드는 불필요하게 길 필요가 없다. 표 1-5 와 같이 길이를 6자로 제한한다.
<tr>
<td width="80" style='padding:5 5 5 30;'><FONT COLOR="#009BD4">이름</FONT></td>
<td height="20" style='padding:5 5 5 5;'> <input class="box_s" type="text" name="name" size="12" maxlength="6"></td>
</tr>
표 1-5 이름 필드 길이 제한

대응 방안을 적용한 후에 글을 작성하면 그림 1-16 과 같이 이름 필드에서 6자까지 밖에 사용 할 수 없다. 이와 마찬가지로 이메일/제목 등 도 길이에 맞게 입력 제한을 해야한다.

그림 1-16 길이 제한 확인

위와 같이 적용하면 클라이언트 측에서 maxlength 를 삭제해 우회할 수 있으므로 반드시 표 1-6 과 같이 데이터베이스에서도 길이 제한을 적용해야 한다.
Dim ls_Name: If HasValue(GMFORM.Form("name")) Then ls_Name = Mid(GMFORM.Form("name"),1,6) Else ls_Name = ""
표 1-6 데이터베이스 길이 제한