펜테스트짐


훈련을 통해 당신의 실력을 향상시켜보세요.



Reflected XSS (반사된 XSS)

모두 32명의 회원님이 완료했어요.


 실습 환경

  실습을 하시려면 로그인이 필요합니다.

이번 훈련에서는 반사된(Reflected) XSS에 대해 알아보고 실습 문제를 통해 반사된 XSS 취약점을 식별하고 테스트하는 방법에 대해 훈련합니다.  





반사된 XSS란?


사용자가 조작할 수 있는 입력값을 갖는 요청이 즉각적으로 사용자에게 응답되고 이 응답에 사용자의 입력값이 안전하지 않은 방식으로 포함될 때 발생하는 XSS를 반사된 XSS라고 합니다. HTTP 요청을 통해 전달된 사용자의 입력값이 응답에 그대로 포함되어 사용자에게 다시 되돌려 보내지므로 "반사된" 이라고 표현합니다. 

예를 들어보죠. 다음의 요청을 살펴봅시다.

http://vulnerable-web.com/examples/greeting.php?name=버그바운티클럽

greeting.php 웹 페이지는 name이라는 GET 파라미터를 통해 전달받은 "버그바운티클럽"이라는 입력값을 어떤 유효성 검사나 위생 처리도 없이 아래 그림처럼 그대로 응답 HTML 소스에 포함시킵니다. 


정상적인 입력값이 전달된 Greeting 화면


위 응답 페이지의 HTML 소스는 다음과 같습니다.

<html>
<body>
<h1>Greeting</h1>
Hi, 버그바운티클럽
</body>
</html>


만일 name 매개변수에 정상적인 텍스트값이 아닌 다음과 같은 스크립트 코드를 입력한다면 어떤 일이 일어날까요?

http://vulnerable-web.com/examples/greeting.php?name=<script>alert(77)</script>

아래 그림과 같이 스크립트가 실행되어 77이라는 팝업창이 뜨게 됩니다. 


name 매개변수를 통해 전달된 자바스크립트 코드가 실행된다.


HTML 소스를 살펴보면 name 매개변수를 통해 텍스트가 삽입될 부분에 스크립트 코드가 그대로 추가된 것을 볼 수 있습니다.

<html>
<body>
<h1>Greeting</h1>
Hi, <script>alert(77)</script>
</body>
</html>

이 예시에서는 단순히 77이라는 경고창을 팝업시키는 무해한 스크립트를 사용하였지만 공격자는 생각만큼 착하지 않습니다. 공격자는 본인의 목적을 달성하기 위해 치밀하게 작성된 악성 스크립트를 사용할 것입니다. 예를 들어, 공격자가 name 매개변수에 공격자의 서버로 쿠키에 저장된 세션 토큰을 전송하는 자바스크립트를 포함시키고 다른 사용자에게 해당 URL을 방문하도록 유도할 수 있다면 URL을 방문한 사용자의 브라우저에서 공격자의 악성 스크립트는 실행되고, 공격자는 해당 사용자의 세션 토큰을 훔쳐 사용자의 신분으로 웹 애플리케이션을 사용할 수 있게 되는 것입니다.  

반사된 XSS는 즉각적인 응답에 사용자 입력값이 반사되므로 공격자가 만든 악성 링크를 클릭한 경우에만 공격이 성공할 수 있습니다. 따라서 공격자는 보다 많은 사람들이 악성 링크를 클릭하도록 유포하는 추가적인 작업을 해야 합니다. 보통 제 3의 웹 애플리케이션의 게시판 본문이나 댓글, SNS, 이메일 등을 이용하여 악성 링크를 유포하며, 공격자에 의해 유포된 링크를 클릭한 피해자의 웹 브라우저는 악성 스크립트를 실행하여 피해를 보게 됩니다.


반사된 XSS 테스트 방법


대부분의 웹 취약점을 발견하기 위해 가장 먼저 해야 할 일은 사용자가 조작이 가능한 입력이 있는 모든 종단점(Endpoint)을 수집하는 일입니다. 이러한 작업은 스파이더링(Spidering)을 통해 할 수 있으며 다양한 자동화 도구와 기법들이 있습니다. 하지만 자동화 도구에만 의존하는 것보다는 BurpSuite 또는 OWASP ZAP 등과 같은 프록시 도구를 이용하여 수동으로 웹 애플리케이션의 모든 기능을 따라가면서 직접 사용해보는 것이 좋습니다. 프록시 도구는 여러분이 웹 애플리케이션에 보낸 요청이나 그에 따른 응답이 히스토리에 저장하고 트리구조의 웹 애플리케이션 사이트맵이 자동으로 생성되므로 웹 애플리케이션의 구조나 파라미터가 있는 요청을 식별해내는데 도움이 됩니다. 그럼 이제부터 사용자 입력이 가능한 종단점을 수집했다고 가정하고 그 이후의 단계부터 반사된 XSS를 식별하고 테스트하는 방법에 대해 살펴보도록 하겠습니다. 


Step 1. 테스트 문자열을 통한 입력값 반사 점검

사용자 입력이 가능한 모든 종단점(Endpoint)을 수집하고 식별하셨다면 각 종단점의 공격 벡터(입력이 가능한 매개변수, Body 데이터 또는 HTTP 헤더 등)에 웹 애플리케이션에서 사용되지 않을 법한 무해하고 고유한 테스트 문자열을 입력값으로 지정하여 다시 요청합니다. 예를 들면 아래와 같습니다. 

xsstestpayload

만일 응답 내에 테스트 문자열이 표시(반사)된다면 해당 종단점은 XSS에 잠재적으로 취약할 수 있습니다.

입력값 반사 테스트의 자동화를 지원하는 프록시 도구의 확장 기능, 또는 다른 헌터들이 개발한 Command-Line 기반의 자동화 도구들을 활용할 수 있습니다. 다만 자동화 도구를 사용할 경우에는 타겟 웹 애플리케이션이 짧은 시간에 다량의 요청을 감지했을 경우 접속을 차단할 수 있으니 요청 속도를 설정할 수 있는 옵션이 있는지 확인하고 이를 잘 활용해야 합니다. 


Step 2. 테스트 문자열이 표시되는 위치의 응답 소스코드 분석

입력값으로 전달한 테스트 문자열이 반사되는 종단점을 찾았다면 응답 내에서 테스트 문자열이 포함된 위치의 HTML이나 자바스크립트 등의 소스코드를 분석합니다. XSS 기초 훈련에서 보았듯이 HTML 태그와 태그 사이(원시 HTML 영역)에 포함되는지, HTML 태그 내부의 속성값으로 포함되는지, 자바스크립트 내부에 포함되는지에 따라 사용할 페이로드가 결정됩니다.

일반적으로는 XSS 테스트시 가장 기본이 되는 아래 페이로드를 흔히 사용합니다.

<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg onload=alert(1)>

하지만 입력값이 반사되는 위치의 문맥을 올바르게 탈출해야만 XSS가 트리거될 수도 있으므로 성공율을 높이기 위해서는 입력값이 반사되는 지점에 대한 소스코드를 분석하셔서 문맥에 맞는 페이로드를 선택하셔야 합니다.

  더 읽어보세요.


페이로드는 웹 애플리케이션이 구현된 방식이나 필터링 규칙에 따라 다양하게 변형됩니다. OWASP의 XSS Filter Evasion Cheatsheet에서 다양한 XSS 페이로드를 확인해 볼 수 있습니다. 참고로 어떤 구문에서도 실행되도록 만들어진 Polyglot 페이로드도 있습니다.


Step 3. 스크립트 주입을 통한 취약 여부 테스트

소스코드 분석 후 입력값으로 스크립트를 전달하여 웹 브라우저에 의해 스크립트 코드로 파싱되고 스크립트가 정상적으로 실행되는지 확인합니다. 스크립트가 실행된다면 XSS에 취약한 것입니다. 만일 요청이 차단되거나 꺽쇠괄호(< 또는 >)나 쌍따옴표("), 홑따옴표(') 등이 인코딩이나 이스케이프 처리되어 스크립트가 실행되지 않는다면 방화벽이나 자체적으로 구현된 필터링을 통해 입력값을 무해하게 만든 것입니다.  


Step 4. 필터링으로 인한 실패 시 우회 시도

보안에 신경 쓴 웹 애플리케이션은 이미 방화벽이나 필터링 기능을 구현해두었습니다. 따라서 일반적으로 사용되는 <script>alert(1)</script> 또는 onclick과 같은 HTML 이벤트를 활용한 잘 알려진 XSS 공격 스크립트는 대부분 실패할 것입니다. 

방화벽이나 필터링 기능은 일반적으로 아래와 같은 기능을 수행합니다. 

  • 입력값에 포함된 공격에 자주 사용되는 패턴을 식별하고 입력 자체를 차단합니다.
  • 위험한 문자를 삭제하거나 인코딩 처리하여 입력값을 무해하게 만듭니다.
  • 입력값 길이를 제한하여 공격에 필요한 전체 페이로드를 전달할 수 없게 합니다.

사실상 이 단계부터 삽질이 시작되는 단계라고 볼 수 있습니다. 수많은 연습과 경험에 기반한 통찰력과 창의력, 구글링 등의 정보 수집력 등이 요구되는 단계입니다.

웹 애플리케이션 내에 구현된 필터링 기능은 많은 우회 기법을 시도하면서 필터의 동작 방식을 이해하고 필터링에서 처리하지 못하는 페이로드를 고안해야 합니다. 

방화벽에 의해 요청이 차단된 경우에는 우선 사용 중인 방화벽의 종류와 버전을 식별하여 이미 알려진 우회 기법을 찾아 시도해보는 것이 좋습니다. 



실습 문제 풀이 


지금부터는 약간의 실습 문제를 통해 지금까지 학습했던 내용과 약간의 응용 사례를 실습해볼 것입니다. 혹시 위의 가상 실습 환경을 생성하여 실습을 해보셨나요? 아직 해보지 않으셨다면 아래 내용을 읽어보시기에 앞서 직접 실습해보시는 것을 권장합니다. 아래에 설명된 방법 외에 다른 다양한 방법으로 스크립트 실행이 가능할 수도 있습니다. 먼저 실습을 해보신 후 자신의 풀이 방법을 아래에 설명된 풀이 방법과 비교해보세요. 


Exercise 1

이 문제는 username 이라는 GET 파라미터를 통해 사용자로부터 입력받은 이름을  원시 HTML 영역에 그대로 표시합니다. 


Exercise 2

이 문제는 응답 내에 두 위치에서 사용자 입력값이 표시됩니다. 하나는 원시 HTML 영역이고 다른 하나는 input 태그의  value 속성값입니다. 원시 HTML 영역에 표시되는 입력값은 인코딩 처리가 됩니다. 따라서 첫번째 문제에서 사용한 기본 페이로드로는 스크립트를 실행시킬 수 없습니다. 하지만 input 태그의 value 속성값에 표시되는 입력값은 어떠한 필터링 처리도 되지 않는 것 같군요. 속성값에 사용자 입력값이 표시되는 경우에는 해당 속성을 탈출해야 합니다.  


Exercise 3

두 번째 문제와 유사하지만 HTML 태그에 사용되는 꺽쇠괄호(< 또는 >)가 인코딩됩니다.


Exercise 4

웹 애플리케이션 개발자는 숨겨진 필드를 사용하여 HTML 페이지 내에 데이터를 보관하기도 합니다. 이 문제는 사용자가 입력한 이름이 type="hidden"으로 지정된 input 태그의 value 속성값으로 포함됩니다.  이와 같은 경우 Firefox에서는 재미있는 일이 벌어집니다. Firefox에서 실습하세요.


Exercise 5

앞서 살펴본 문제와 달리 이 문제에서는 사용자로부터 입력받은 이름이 모두 인코딩 처리되었습니다. 하지만 개발자가 신경쓰지 못한 부분이 하나 남아있습니다. 웹 애플리케이션은 때때로 href나 src 속성에 사용자가 조작할 수 있는 파라미터의 값을 이용합니다. 이 문제에서는 예약 내용을 볼 수 있는 rxss5-1.php 페이지에 XSS 취약점이 있습니다.


Exercise 6

이 문제에서는 사용자로 부터 전달받은 입력값이 자바스크립트 코드 내부에 표시됩니다.



참고 문헌