(해당 취약점은 공격 방법이 없습니다.)

기준

 

취약점 설명

ID를 통한 PW 찾기를 할 때 임시로 생성된 PW가 취약할 경우 발생하는 취약점입니다.

 

ID값을 알고 있을 경우 복구를 통해 타 사용자의 ID를 탈취할 수 있습니다.

 

해당 취약점은 사용자 및 관리자의 PW를 복구하여 사용자의 ID를 탈취하는 방법으로 서비스를 장학할 수 있는 위험한 취약점입니다.

 

------------------------------------------------------------------------------------------------------------------------------

 

간략하게나마 찾은 취약점에 대해 말하자면 패드워드 복구가 약한 경우도 있지만 인증 코드 발송 시에 Response 패킷에 인증 코드가 노출되는 방법으로 발견하는 경우도 있습니다.

 

물론 관련된 취약점은 아닐 수도 있지만 말한 경우처럼 각각의 공격 방법을 조합하여 취약점을 발견하는 경우도 많아 간략하게 써봤습니다.

기준

취약점 설명

ID/PW 찾기와 같은 사용자 인증이 꼭 필요한 부분이 존재합니다.

이러한 경우 2차 인증을 구현하여 사용자 인증을 통해 권한이 없는 사용자가 침해할 수 없도록 조치하는 것이 일반적입니다.

 

하지만 비밀번호가 설정되어 있는 글이나 타 사용자 ID에 인증 값 변조를 통해 접근이 가능한 경우 발생하는 취약점입니다.

 

해당 취약점은 인증이 필요한 부분이 구현된 인증이 미흡하여 열람/변조가 가능하여 서비스 가용성을 떨어트리거나 관리자 ID가 탈취당한 경우 공격자에 의해 웹 서비스가 장악될 수 있는 위험한 취약점입니다.

 

다만 여기에서 중요한 것은 후에 작성할 불충분한 인가와의 차이점입니다.

아래에 제가 생각하는 차이점을 올려보도록 하겠습니다.

 

불충분한 인증

위의 사진을 보면 인증정보를 변조하여 관리자 ID를 접근하는 공격자를 그림으로 나타내 보았습니다.

예를 들어 URL이 http://www.test.com/board/edit.jsp?id=test인 페이지가 있다고 가정하겠습니다.

이런 경우 위의 URL의 마지막 부분의 id를 admin으로 바꿔주었을 때 admin으로 접근 가능할 때 admin에 대한 인증이 없는 경우로 불충분한 인증으로 포함되는 것입니다.

 

즉, 로그인과 같이 인증이 필요한 부분에 인증이 없거나 부족한 경우 불충분한 인증이라고 판단합니다.

 

불충분한 인가

위의 사진은 권한이 없는 페이지에 접근하는 공격자를 표현한 그림입니다.

예를 들어 타 사용자 혹은 관리자가 작성한 글이나 공지사항이 존재한다고 가정하겠습니다.

작성된 글이 타 사용자를 위해 공개된 글이 아닌 경우 공격자가 인증 정보를 바꾸지 않은 상태로 접근 가능하면 불충분한 인가에 포함되는 것입니다.

 

즉, 공개되지 않거나 권한이 없을 때 별 다른 인증 정보 변경 없이 접근이 가능한 경우 불충분한 인가라고 판단합니다. 

 

공격 방법

(실습할 수 있는 페이지를 찾지 못해서 설명으로 대체하겠습니다.ㅠㅠ)

위와 같은 로그인 페이지가 있다고 가정하겠습니다.

username값이 rokefoke였으나 admin으로 바꿔주고 접근했을 때 정상적으로 접근이 된다면 취약한 것입니다.

 

이외에 URL에 포함된 ID값을 변조하는 방식으로도 시도 가능합니다.

위는 쿠키 값입니다.

쿠키 값은 세션에 더 가깝지만 쿠키에 ID가 포함된 경우 ID 값만을 변조하여 시도하는 경우도 존재합니다.

 

--------------------------------------------------------------------------------------------------------------------

실제 페이지를 보여드릴 순 없어 위처럼 설명으로 대체하여 포스팅하였습니다.

 

만약 실습할 수 있는 페이지를 방명록에 남겨주신다면 작성하여 포스팅하도록 하겠습니다.

(실습할 공간을 알고 계신 분은 꼭 알려주시길 부탁드려요ㅠㅠㅠ)

기준

 

취약점 설명

관리자 혹은 사용자의 ID/PW가 유추할 수 있는 문자열로 구성되는 경우 발생하는 취약점입니다.

또한 로그인 시도 횟수 제한이 없는 경우 무차별 대입 공격(bruteforce 혹은 dictionary attack)을 통해 침해당할 가능성도 있습니다.

 

이외에도 로그인 시에 ID가 맞는 경우와 맞지 않는 경우 발생되는 페이지 혹은 문구가 다를 경우 서버에 존재하는 ID를 유추할 수 있습니다.

 

해당 취약점은 관리자 계정 탈취를 통해 해당 서비스를 장악할 수도 있고 사용자의 정보가 탈취당할 가능성이 있는 매우 위험한 취약점입니다.

 

공격 방법

해당 취약점은 실습보다는 글로 대체하도록 하겠습니다.

 

1. 취약한 ID/PW 설정

이 경우는 직접 로그인 시도를 통한 공격 방법도 있지만 화이트박스 테스트를 통해 현재 존재하는 ID 중 취약한 ID가 존재하는지 확인하는 방법을 주로 사용합니다.

 

2. 로그인 횟수 제한 설정

해당 경우는 직접 로그인을 시도하여 3~5회 시도 후 계정이 잠기는가를 확인해보는 방식으로 확인합니다.

 

3. 로그인 시도 시 발생되는 페이지 혹은 문구

정상적인 ID를 삽입한 후 PW를 다르게 넣어 발생되는 문구와 존재하지 않는 ID를 삽입한 후 발생되는 문구의 차이가 있는지 확인하여 진단합니다.

 

----------------------------------------------------------------------------------------------------------------------------------

물론 각각의 문제점을 종합하여 공격하는 방식도 있습니다.

 

예를 들어 취약한 ID가 존재하는가를 발생되는 문구를 통해 확인한 후 무차별 대입 공격을 통해 PW를 알아보는 방법이 존재합니다.

 

무차별 대입 공격은 burpsuite, fiddler(Burplikeinspctor), john the ripper 등등을 사용하여 가능합니다.

기준

취약점 설명

사용자의 브라우저(크롬, 익스플로러, 엣지, 웨일 등등)에서 스크립트가 실행되도록 공격자가 악의적인 스크립트를 사용자로 하여금 실행되도록 하는 공격 방법입니다.

 

XSS(Cross-Site Script)를 사용한 공격은 세션을 가로채거나 웹 페이지 변조, 악성 콘텐츠 삽입, 피싱 공격 등에 주로 사용됩니다.

 

주로 XSS(Cross-Site Scirpt) 공격은 3가지로 분류가 됩니다.

Reflected-XSS : 공격자가 피해자로 하여금 서버에 스크립트를 실행하도록 유도하여 정보를 요청하도록 만드는 공격입니다. 사용자가 요청한 정보를 공격자에게 전송하도록 설정하여 의도한 정보를 획득하는 방식입니다. URL 접근으로 스크립트를 피해자의 PC에서 전송하도록 만드는 공격으로 1회성 공격이라고 할 수 있습니다.

 

Stored-XSS : 공격자가 서버에 저장하여 공격하는 방식으로 덫을 설치한다고 생각하면 쉽습니다. 공격자가 취약한 게시판이나 댓글 등의 공간에 악의적인 스크립트를 저장하여 해당 게시물에 방문한 피해자가 스크립트를 실행하여 공격자에게 전송하는 방식입니다. Stored-XSS는 서버의 데이터베이스에 저장되어 방문하는 사용자는 상대로 실행되기 때문에 지속적인 피해를 발생시킬 수 있습니다.

 

DOM(Document object Model) Based XSS : 문서 객체 모델(DOM)을 사용하여 피해자의 브라우저에서 서버와 상호작용하지 않고 실행되는 스크립트입니다. 서버를 상대로 하지 않은 공격이고 대상은 피해자로 정해진 공격입니다. 스크립트 삽입 시에 서버로 전송되는 패킷에 스크립트가 포함되지 않고 피해자의 브라우저만을 상대로 하기 때문에 웹 사이트의 코드를 확인하지 않고는 발견할 수 없는 취약점입니다.

 

세 가지의 XSS 종류 중 Reflected와 DOM based는 비슷한 부분이 많지만 가장 큰 차이점은 서버에 스크립트를 전송하여 정보를 획득하는 방식과 피해자의 브라우저만을 상대로 하느냐가 차이입니다.

 

Reflected와 Stored는 응답 페이지의 HTML에서 삽입된 악성 스크립트가 보이기 때문에 발견 가능성이 높지만, DOM based는 패킷에서 보이는 공격이 아니기 때문에 웹사이트의 코드나 URL을 자세하게 보지 않는 이상 발견하기 어려운 취약점입니다.

 

공격 방법

(본 취약점은 DVWA를 활용하여 실습해보도록 하겠습니다.)

 

1.Reflected-XSS

1) Low Level

 Low Level의 보안을 위한 필터를 없는 것으로 확인됩니다.

기본 스크립트 삽입을 시도해 보겠습니다.

<script>alert("XSS")</script>

소스 내에 따로 구현된 필터링이 없기 때문에 script를 사용하여 시도하도록 하겠습니다.

삽입된 스크립트가 정상적으로 실행되는 것을 확인할 수 있습니다.

 

2) Medium Level

페이지의 형태는 Low Level에서 본 것과 같은 것을 확인할 수 있습니다.

소스코드를 확인해보니 str_replace를 이용해 <script>를 빈칸으로 필터링하는 것을 확인할 수 있습니다.

 

위와 같은 경우에는 script를 사용하지 않고 태그를 이용하여 시도해보도록 하겠습니다.

<SCRIPT>alert("XSS")</SCRIPT>

소스코드를 확인했을 경우 <script>를 필터링하는 것을 확인할 수 있었습니다. 하지만 저 문자열 즉 소문자로 구성된 <script>만을 필터링하고 있으므로 대문자로 바꾸어주고 시도해보도록 하겠습니다.

스크립트가 정상적으로 실행되는 것을 확인할 수 있습니다.

필터링이 된다고 하더라도 필터링 방식에 따라 대소문자 구별만으로도 성공할 수 있는 것을 확인할 수 있었습니다.

 

3) High Level

페이지의 형태는 전과 동일합니다.

소스코드를 확인해보면 전과 달리 대/소분자 전부 필터링하는 것을 확인할 수 있습니다.

이런 경우에는 script를 사용하지 않고 태그를 이용하여 시도할 수 있을 것이라 생각하고 진행해보도록 하겠습니다.

위에서 img와 on 태그를 사용해서 스크립트를 작성해보았습니다.

img는 src에 redirect를 걸어줄 필요는 없기 때문에 blank로 두고 onerror에 alert를 삽입하여 진행해보도록 하겠습니다.

삽입된 스크립트가 정상적으로 실행되는 것을 확인할 수 있습니다.

위와 같은 경우 script를 사용하지 않고 on 태그를 사용하여 XSS를 성공했습니다.

사용 가능한 스크립트 태그를 생각나는 대로 나열해보면 script, iframe, object, onerror, onmouseover, onload, img, a 등등이 있습니다.

 

위의 스크립트에서 응용해서 사용해보자면

i) <img width="0" height="0" src="x" onerror="document.localtion='http://192.168.75.128/dvwa/vulnerabilities/xss_r/?name='+document.cookie">

ii) <script>location.href="http://192.168.75.128/dvwa/vulnerabilities/xss_r/?name="+document.cookie</script>

iii) <img src="/dvwa/vulnerabilities/xss_r/?name="+document.cookie>

위와 같이 작성하여 적용하는 방식도 생각해볼 수 있습니다.

2. Stored-XSS

1) Low Level

<?php

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

    // Sanitize message input
    $message = stripslashes( $message );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Sanitize name input
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?>​

페이지의 형태는 흔한 게시판의 형태인 것을 확인할 수 있습니다.

소스코드를 확인해보면 따로 구현되어 있는 필터링은 없는 것을 확인할 수 있습니다.

name 필드는 maxlength가 10이고 Message필드는 50인 것을 확인할 수 있습니다.

소스 상에서 name필드에 스크립트를 넣을 수 있는 길이는 안되지만 따로 방법은 있습니다.

우선 Low Level에서는 Message 필드에 기본 스크립트를 삽입하는 방식으로 시도해보도록 하겠습니다.

<script>alert("XSS")</script>

위와 같이 message필드에 기본 스크립트를 삽입하여 시도해보도록 하겠습니다.

스크립트가 정상적으로 실행되는 것을 확인할 수 있습니다.

 

2) Medium Level

<?php

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

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = str_replace( '<script>', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?>

이번에도 마찬가지로 페이지의 형태는 Low Level과 동일합니다.

하지만 여기에서 볼 수 있는 소스코드의 차이점은 message와 name필드에 다르게 적용된 보안 조치입니다.

message필드는 htmlspecialchars( $message )로 조치된 것을 확인할 수 있는데 우회하는 방법으로 나오는 것 들은 소스코드 내에 취약한 태그가 있어서 삽입된 스크립트가 실행되는 경우 말고는 알고 있는 바가 없습니다.

(혹시 아는 분은 알려주세요ㅋㅋ)

 

하지만 name필드는 <script>만을 필터링하고 있으며 reflected medium level때와 같이 해당 문자열 만을 필터링하고 있다는 것을 알 수 있습니다.

 

그럼 전과 마찬가지로 시도하고 싶지만 name 필드의  maxlength가 10으로 제한되어 삽입할 수 없다고 생각할 수 있습니다. 이런 경우 우회 방법은 여러 가지가 있지만 두 가지만 말하자면 첫 번째는 개발자 도구에서 maxlength값을 바꿔주는 것이고 두 번째는 파라미터를 수정하는 방법이 있습니다.

 

먼저 maxlength값을 바꿔보도록 하겠습니다.

변경하려는 필드를 더블클릭하면 위 사진처럼 변경이 가능합니다.

물론 사용자 브라우저에서 HTML 코드 수정이므로 서버와는 관련이 없습니다.

바꾼 후에 작성해보면 위와 같이 작성되는 것을 확인할 수 있습니다. 그 상태에서 Sigh Guestbook을 눌러주시면 스크립트가 삽입되는 것을 확인할 수 있습니다.

HTML 코드만 수정한 것일 뿐 서버를 수정한 게 아니기 때문에 새로고침하면 원상태로 복귀됩니다. 

 

두 번째는 파라미터 값을 수정하는 방법입니다.

위 사진과 같이 각 필드에 아무 값이나 채워준 후에 Sign Guestbook을 눌러줍니다. 그 파라미터 값을 아래와 같이 수정해주는 방법으로 삽입해줄 수 있습니다.

먼저 언급한 방법으로 삽입하였을 경우 위의 사진과 같이 스크립트가 실행되는 것을 확인할 수 있습니다.

 

3) High Level

<?php

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

    // Sanitize message input
    $message = strip_tags( addslashes( $message ) );
    $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    $message = htmlspecialchars( $message );

    // Sanitize name input
    $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
    $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

    // Update database
    $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

    //mysql_close();
}

?>

페이지의 형태는 전과 동일합니다.

소스코드를 확인해보면 message 필드는 전과 동일한 것을 알 수 있지만 name 필드는 reflected-XSS의 High Level과 동일하게 script를 대/소문자 구분 없이 필터링하는 것을 알 수 있습니다.

name 필드에 <img src="" onerror=alert("XSS")>를 삽입하여 시도해보겠습니다.

이처럼 스크립트가 정상적으로 실행되는 것을 확인할 수 있습니다.

위의 내용에서 알 수 있는 것은 Reflected와 Stored는 저장의 차이일 뿐 공격 방법에서는 큰 차이를 보이지 않는 것을 알 수 있었습니다.

 

하지만 Reflected는 1회성 공격이고 Stored는 저장되어 유지되므로 지속성이 있는 공격 방법이라는 차이가 있었습니다.

 

3. DOM(Document Object Model) Based XSS

1) Low Level

페이지의 형태는 언어를 선택하는 구성으로 되어있습니다.

소스코드를 확인해보면 아무런 보안 조치가 되어있지 않은 것을 알 수 있습니다.

English를 선택한 후 Select를 눌러주면 위와 같이 URL에 default=English가 생성되는데 이 필드에 스크립트를 삽입해보려고 합니다.

위와 같이 English 대신 스크립트를 넣어주면 실행되는 것을 확인할 수 있습니다.

하지만 중요한 것은 스크립트가 서버에 전송되는가가 가장 중요한 부분입니다.

위의 사진처럼 패킷에 정보가 포함되어 전송됐고 웹 페이지 소스에도 남는 것을 확인할 수 있습니다.

일단 계속 진행해보도록 하겠습니다.

 

2) Medium Level

페이지의 형태는 동일하지만 Low Level과 다르게 <script가 Deny 되는 것을 확인할 수 있습니다.

stripos()를 통해 필터링을 하고 있는데 확인하고자 하는 문자열(=<script)을 앞부터 검색해서 찾고자 하는 문자열이 몇 번째 위치에 존재하는지 리턴해주는 함수입니다.

 

이런 경우 대문자로 script를 넣어준다고 하더라도 필터링될 수 있습니다.

 

그러므로 Reflected High Level에서 사용한 스크립트를 사용하여 시도해보도록 하겠습니다.

위의 사진처럼 스크립트를 삽입해주면 실행되지 않는 것을 확인할 수 있습니다.

실행이 되지 않는 이유를 알기 위해서 소스를 확인해보도록 하겠습니다.

스크립트가 삽입된 부분을 확인해보면 select가 실행 중인 것을 알 수 있습니다.

select 내에서는 img 태그가 들어갈 수 없기 때문에 스크립트가 실행되지 않았던 것입니다.

그럼 작성된 스크립트 앞에 </select>를 삽입하여 다시 시도해보겠습니다.

</select><img src="" onerror=alert("XSS")><select>

위처럼 스크립트를 삽입해보면 실행되는 것을 확인할 수 있습니다.

뒤에 <select>를 넣어준 이유는 그냥 지저분한 게 싫어서입니다.

전과 마찬가지로 패킷과 웹 페이지 소스를 확인해보도록 하겠습니다.

이번에도 마찬가지로 패킷과 페이지 소스에서 스크립트가 포함되어 있는 것을 확인할 수 있습니다.

분명 서버로는 전송되지 않는다 알고 있지만 왜 이러는지 궁금한 분이 있을 거라 생각합니다.

이 방법은 마지막 High Level을 보면 알 수 있습니다.

 

3) High Level

페이지의 형태는 역시 동일합니다.

소스코드를 확인해보면 switch함수를 통해 case를 정해두고 값을 받아가는 것을 확인할 수 있습니다.

white list형식으로 다른 값이 들어가면 공격하기 까다로운 상황인 것입니다.

이런 경우 전에 말씀드린 바와 같이 서버에서는 정상적인 패킷으로 인식하되 클라이언트의 브라우저에만 공격하도록 스크립트를 작성해야 합니다.

http://192.168.75.128/dvwa/vulnerabilities/xss_d/?default=English#<script>alert("XSS")</script>

위에서 처럼 #을 이용해 뒤의 스크립트는 서버로 전달되지 않도록 주석처리해주었습니다.

사진처럼 삽입한 스크립트가 정상적으로 실행되는 것을 확인할 수 있습니다.

여기에서 패킷과 웹 페이지 소스를 확인해보도록 하겠습니다.

위의 사진에서 확인할 수 있듯이 서버로 전송되는 패킷에서는 공격 스크립트가 확인되지 않습니다.

하지만 브라우저의 웹 페이지 소스에서는 스크립트가 남는 것을 확인할 수 있습니다.

 

이렇게 웹 페이지 소스를 직접 확인해야 알 수 있기 때문에 사용자가 URL이나 웹 소스를 보지 않는 이상 발견하기 어렵습니다.

+ Recent posts