홈페이지 취약점 분석 이야기 파일 지도 사진 깨알






>> 목록보이기
#DVWA Stored XSS #DVWA #Damn Vulnerable Web Application #웹해킹 실습 #실습설명서 #inspect element #HTML 삽입 #HTML Injection #크로스사이트스크립트 #Cross-site Script #자바스크립트 삽입 #저장형 XSS #iframe 삽입 #홈페이지 위변조 #website defacement #해킹 시나리오 #A3-Cross-Site Scripting (XSS)

DVWA: Stored XSS (low, medium, high level) 공략

DVWA(Damn Vulnerable Web Application) 1.9 훈련장 라이브 ISO는 다음에서 다운로드 받을 수 있다.

DVWA 1.9 훈련장 라이브 ISO를 구동하는 방법은 다음 문서에서 볼 수 있다.

여기서는 구동 후 DVWA 훈련장의 주소가 http://192.168.189.246/이었다. DVWA 누리집의 로그인 정보는 admin/password이다. 실습을 진행하기 전에 다음 두 가지 작업을 사전에 수행하야야 한다.

  1. "Setup / Reset DB" 항목에서 "Create / Reset Database"를 실행한다.
  2. "DVWA Security" 항목에서 "Security Level"을 시험하고자 하는 보안수준에 따라 Low, Medium, High 로 변경한다.

XSS 취약점은 사용자의 입력을 HTML에 출력하는 부분에서 발생한다. HTML을 제대로 검증하지 않으면 공격자는 자신이 원하는 자바스크립트를 삽입할 수 있다. 기본적으로 자바스크립트 삽입이 가능한 지 점검하는 방법은 HTML 삽입 취약점 탐지방법 문서에서 살펴볼 수 있다.
DVWA 반사형 XSS 실습문제도 동일한 취약점을 다루고 있다. 다만 저장형 XSS는 - 반사형 XSS 공격과는 달리 - 공격의 결과가 서버에 남기 때문에 훨씬 위험한 공격이며, 비밀글을 지원하는 게시판의 경우에는 관리자를 특정하여 공격을 수행할 수도 있다.

Vulnerability: Stored Cross Site Scripting (XSS) 실습문제는 방명록(Guestbook)이다. 사용자가 이름(Name)과 전갈(Message)을 남길 때 HTML 검증이 이루어지지 않으면 발생하는 저장형 크로스사이트스크립트 취약점을 다룬다. 댓글 기능이나 질의응답(Q&A)과 같은 일반회원을 대상으로 하는 게시판에서도 동일한 문제가 발생할 수 있다.

Vulnerability: 저장형 XSS (low level) 실습 설명

Vulnerability: Stored Cross Site Scripting (XSS) low level에서는 사용자의 입력인 txtNamemtxMessage 변수를 그대로 출력한다. PHP 소스코드를 살펴보자.

    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = stripslashes( $message );
    $message = mysql_real_escape_string( $message );

    // Sanitize name input
    $name = mysql_real_escape_string( $name );

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";

위에서 사용한 stripslashes() 함수mysql_real_escape_string() 함수는 작은따옴표, 큰따옴표, 줄바꾸기 등의 특수문자를 데이터로 처리하기 위한 기능이다. HTML에 대한 처리 기능이 거의 없다.

DVWA XSS (Stored) - low level
[ DVWA 저장형 XSS (low level): 입력란에서 inspect element 선택 ]

DVWA 저정향 XSS의 방명록 입력에는 제한이 있다. Name 변수의 최대 문자열 길이는 10이다. <input> 엘리먼트의 maxlength 속성을 이용하여 입력 길이을 제한한다.

	Name *  <input name="txtName" type="text" size="30" maxlength="10">

이렇게 HTML을 이용하여 입력을 제한하는 방법은 HTML을 수정하여 풀 수 있다. 가장 간단한 방법은 Firefox 웹브라우저의 inspect element 기능을 이용하는 것이다. 위의 그림처럼 해당 HTML에서 오른 쪽 마우스 단추를 누르면 편리하게 inspect element를 접근할 수 있다 (Firebug도 inspect element with Firebug에서 HTML을 편집할 수 있는 기능을 제공한다).

DVWA XSS (Stored) - low level
[ DVWA 저장형 XSS (low level): inspect element 실행 화면, maxlength = "100" ]

위와 같이 <input> 엘리먼트에서 maxlength = "100"으로 변경하여 입력제한을 풀 수 있다.

DVWA XSS (Stored) - low level
[ DVWA 저장형 XSS (low level): 길이 제한을 해제한 입력 ]

Name의 입력값은 <img src=x onerror=alert(document.cookie)> Hi~~이다. 자바스크립트가 실행되면 document.cookie 값을 경고창으로 띄운다. Message의 입력값은 <svg onload=alert('SVG_image')> Bye~~이다. ECMAscript가 실행되면 - JavaScript와 거의 동일 - SVG_image라는 문자열을 경고창으로 띄운다. 경고창이 뜨면 XSS 취약점이 존재한다는 뜻이다.

DVWA XSS (Stored) - low level
[ DVWA 저장형 XSS (low level): docment.cookie 출력 ]

document.cookie 값이 경고창에 나타난다. txtName 변수에 자바스크립트를 입력할 수 있는 XSS 취약점이 존재한다.

DVWA XSS (Stored) - low level
[ DVWA 저장형 XSS (low level): SVG의 alert() 창 ]

SVG_image라는 문자열이 경고창에 나타난다. mtxMessage 변수에 자바스크립트를 입력할 수 있는 XSS 취약점이 존재한다.

이제부터 Vulnerability: Stored Cross Site Scripting (XSS) 훈련장에 접속하면 항상 두 경고창을 만나게 된다. 서버(실제로는 MySQL 데이터베이스)에 사용자의 공격 스크립트가 저장된 것이다. 이렇게 공격 결과가 서버에 남게 되는 HTML 삽입 공격을 "저장형 XSS"라고 한다.

[ 공격 시나리오: 홈페이지 위변조 ]

저장형 XSS가 가능하면 다양한 공격이 가능하다. 접속하는 모든 사용자의 세션을 탈취할 수도 있다. Vulnerability: Stored Cross Site Scripting (XSS) 실습문제는 관리자만 접속할 수 있으므로 document.cookie를 유출할 수 있으면 관리자 권한을 탈취하게 된다. document.cookie를 다른 웹 브라우저에서 삽입해보면 알 수 있다.

여기서는 홈페이지 위변조 공격을 시도해 보자. 실제 침해사고 사례는 저장형 XSS를 이용한 홈페이지 위변조 사례 문서에서 볼 수 있다.

DVWA XSS (Stored) - low level
[ DVWA 저장형 XSS (low level): 홈페이지 위변조용 HTML 삽입 ]

파이어폭스의 inspect element 기능을 이용하여 mtxMessage 변수의 최대 입력 길이를 50에서 500으로 변경한다. mtxMessage 변수의 입력은 <body topmargin=0 leftmargin=0 onload="document.body.innerHTML='<iframe width=100% height=800 src=http://www.daum.net/></iframe>';">이다. 공격자는 전갈에 이러한 HTML을 삽입한다. 피해자가 웹 브라우저로 방명록 페이지에 접근한다. 이때 <body>onload되면 자바스크립트는 <body> 엘리먼트의 내용(document.body.innerHTML)을 <iframe>으로 바꿔치기한다.

DVWA XSS (Stored) - low level
[ DVWA 저장형 XSS (low level): 홈페이지 위변조 결과 ]

그 결과는 위의 그림과 같다. URL은 Vulnerability: Stored Cross Site Scripting (XSS) 실습문제(http://192.168.189.246/vulnerabilities/xss_s/)이지만 피해자가 보는 내용은 다음 홈페이지(http://www.daum.net)이다. 홈페이지 위변조된 것이다. 원래 누리집 기능에 접근할 수 없으므로 서비스거부(Denial of Service) 상태에 빠지게 된다.

Vulnerability: 저장형 XSS (medium level) 실습 설명

Medium level의 PHP 소스 코드를 살펴보자.

    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = mysql_real_escape_string( $message );
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = str_replace( '<script>', '', $name );
    $name = mysql_real_escape_string( $name );

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";

mtxMessage 입력에 대해서는 strip_tags() 함수(HTML 제거)와 htmlspecialchars() 함수(특수문자 변환)를 적용하고 있다. 따라서 mtxMessage 변수에는 HTML을 삽입할 수 있는 방법이 제거된다.

txtName 입력에서는 str_replace() 함수를 이용하여 "<script>" 문자열을 제거한다. 이 때 대소문자를 구분한다. 이 방어법은 "<SCRIPT>"와 같은 문자열은 걸러내지 못한다. HTML 문법은 대소문자를 구분하지 않으므로 <SCRIPT><script>와 동일하다. 즉 이 방법은 우회가 가능하다.

DVWA XSS (Stored) - medium level
[ DVWA 저장형 XSS (medium level): HTML 삽입 시도 ]

Low level에서와 동일한 문자열을 입력해보자. 이 때 Firefox의 inspect element 기능을 사용하면 최대 입력 길이를 늘일 수 있다. 입력값은 다음과 같다.

  • txtName 입력: <img src=x onerror=alert(document.cookie)> Hi~~
  • mtxMessage 입력: <svg onload=alert('SVG_image')> Bye~~

Name 입력에 취약점이 있으면 쿠키를 확인하는 경고창이, Message 입력에 취약점이 있으면 'SVG_image' 경고창이 뜰 것이다.

DVWA XSS (Stored) - medium level
[ DVWA 저장형 XSS (medium level): HTML 삽입 결과 ]

방명록의 결과를 보면 위와 같이 쿠키 값이 alert() 창으로 뜨고 'SVG_image' 경고창은 나타나지 않는다. 즉 txtName 변수에는 XSS 취약점이 있고, mtxMessage 변수에는 취약점이 없다. 해당 출력부의 HTML 소스를 살펴보면 다음과 같다.

<div id="guestbook_comments">Name: <img src="x" onerror="alert(document.cookie)"> Hi~~<br>Message:  Bye~~<br></div>

Name 입력에서는 HTML이 그대로 출력되고 Message 입력에서는 HTML이 완전히 제거된 것을 볼 수 있다.

Vulnerability: 저장형 XSS (high level) 실습 설명

High level에서의 HTML 삽입 방어기작을 살펴보자. 관련 PHP 소스는 다음과 같다.

    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = mysql_real_escape_string( $message );
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
    $name = mysql_real_escape_string( $name );

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";

방어기작은 medium level과 거의 동일하다. 다만 Name 입력에 대해서는 preg_relace() 함수를 이용하여 대소문자를 구분하지 않고 "<*s*c*r*i*p*t"를 제거하고 있다. "</script", "<sc\ript", "<SCRIPT", "<Script", "<sCRIPT" 등의 다양한 우회방법을 방어할 수 있다. 하지만 자바스크립트를 삽입할 수 있는 엘리먼트는 매우 다양하므로 쉽게 우회가 가능한 방어기작이다.

Medium level과 동일하게 시험해보면 결과도 동일하다. 증빙화면은 생략하였다.

<div id="guestbook_comments">Name: <img src="x" onerror="alert(document.cookie)"> Hi~~<br>Message:  Bye~~<br></div>

서버가 응답하는 HTML 소스도 medium level과 동일하다.

Vulnerability: 저장형 XSS (impossible level) 실습 설명

이제 HTML삽입이 불가능한 impossible level의 PHP 소스코드를 살펴보자.

    // Check Anti-CSRF token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // Get input
    $message = trim( $_POST[ 'mtxMessage' ] );
    $name    = trim( $_POST[ 'txtName' ] );

    // Sanitize message input
    $message = stripslashes( $message );
    $message = mysql_real_escape_string( $message );
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = stripslashes( $name );
    $name = mysql_real_escape_string( $name );
    $name = htmlspecialchars( $name );

    // Update database
    $data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );

Name 입력과 Message 입력 모두에 대해서 htmlspecialchars() 함수를 적용하고 있다. 이 함수는 입력에서 특수문자를 변환('<' -> '&lt;', '>' -> '&gt;', '&' -> '&amp;', '"' -> '&quot;')한다. 사용자 입력에서 HTML 삽입을 거의 원천적으로 차단하게 된다.

DVWA XSS (Stored) - impossible level
[ DVWA 저장형 XSS (impossible level): HTML 삽입 결과 ]

Medium level에서와 동일한 시험을 하면 결과는 위와 같다. 부등호, 앰퍼샌드 등이 모두 변환되면서 HTML 삽입이 차단되는 것을 볼 수 있다. 서버가 응답하는 HTML 소스는 다음과 같다.

<div id="guestbook_comments">Name: &amp;lt;img src=x onerror=alert(document.cookie)&amp;gt; Hi~~<br>Message: &amp;lt;svg onload=alert(\'SVG_image\')&amp;gt; Bye~~<br>lt;/div>

서버의 DB에 저장될 때 htmlspecialchars()가 한번 적용되었다. 그리고 다시 HTML에 출력할 때 다시 한번 htmlspecialchars()가 적용되어 위와 같은 HTML 코드가 만들어졌다. 결과적으로 공격자가 의도한 HTML 삽입은 모두 차단되었다.

마무리

거의 모든 웹취약점이 그렇듯이 "HTML 삽입을 통한 자바스크립트 공격"(Cross-site Scripting)은 서버가 입력값을 제대로 검증하지 않을 때 발생한다. 특히 저장형 XSS의 경우에는 불특정 다수를 대상으로 하는 지속적인 공격뿐만 아니라 특정 소수 대상의 표적 공격이 가능하고, 반사형 XSS에 비해서 다양한 방법의 공격을 동원할 수 있으므로 매우 위험하다.

PHP의 경우에는 서버가 HTML에서 출력하는 모든 입력에 대해서 htmlspecialchars() 함수htmlentities() 함수를 적용하면 거의 모든 HTML 삽입 공격을 차단할 수 있다.

[처음 작성한 날: 2017.01.01]    [마지막으로 고친 날: 2017.01.01] 


< 이전 글 : DVWA Reflected Cross Site Scripting (XSS) 실습 설명서 (2016.12.27)

> 다음 글 : SVG 이미지의 ECMAscript를 이용한 악성코드 배포 (2016.12.01)


크리에이티브 커먼즈 라이선스 이 저작물은 크리에이티브 커먼즈 저작자표시 4.0 국제 라이선스에 따라 이용할 수 있습니다.
잘못된 내용, 오탈자 및 기타 문의사항은 j1n5uk{at}daum.net으로 연락주시기 바랍니다.
문서의 시작으로 컴퓨터 깨알지식 웹핵 누리집 대문