본문 바로가기
빡공팟(P4C)

웹해킹트랙 6주차 과제 - DVWA Medium레벨 실습

by qoth_0 2022. 10. 30.
728x90
반응형

Vulnerability: Brute Force

 

<?php

if( isset( $_GET[ 'Login' ] ) ) {
    // Sanitise username input
    $user = $_GET[ 'username' ];
    $user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

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

    // Check the database
    $query  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
    $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>' );

    if( $result && mysqli_num_rows( $result ) == 1 ) {
        // Get users details
        $row    = mysqli_fetch_assoc( $result );
        $avatar = $row["avatar"];

        // Login successful
        echo "<p>Welcome to the password protected area {$user}</p>";
        echo "<img src=\"{$avatar}\" />";
    }
    else {
        // Login failed
        sleep( 2 );
        echo "<pre><br />Username and/or password incorrect.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

medium 레벨에서는 mysql_real_escape_string()을 사용해서 입력 값을 검증하고 있는데, 이는 SQL 명령문에 사용되는 문자열에서 특수 문자를 회피하기 때문에 SQL Injection이 불가능하다. 하지만 Brute Force 공격 시에는 의미 없는 검증 방법이다. 

유의미한 변화는 sleep()으로 로그인이 실패했을 때  서버 응답 시간을 2초 지연시키기 때문에 low 레벨보다 공격 시 소요되는 시간이 길다는 것인데, 그럼에도 low 레벨과 같이 하나씩 대입해보면 결국 공격에 성공하게 된다.  

 

Vulnerability: Command Injection

 

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
    // Get input
    $target = $_REQUEST[ 'ip' ];

    // Set blacklist
    $substitutions = array(
        '&&' => '',
        ';'  => '',
    );

    // Remove any of the charactars in the array (blacklist).
    $target = str_replace( array_keys( $substitutions ), $substitutions, $target );

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
}

?>

소스 코드를 보면 str_replace()를 사용하여 입력값을 필터링하고 있다. '&&'이나 ';'가 입력되면 이를 공백으로 바꾸게 되는데 그 외의 문자를 사용하여 명령어를 입력하면 우회할 수 있다. 

'&', '||', '|' 등을 사용해서 명령어를 실행해보자.

 

Vulnerability: Cross Site Request Forgery (CSRF)

 

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // Checks to see where the request came from
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

        // Do the passwords match?
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
            $pass_new = md5( $pass_new );

            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

            // Feedback for the user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    }
    else {
        // Didn't come from a trusted source
        echo "<pre>That request didn't look correct.</pre>";
    }

    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

stripos()는 문자열을 찾는 함수로, $_SERVER['HTTP_REFERER']에 $_SERVER['SERVER_NAME]가 있는지 없는지 확인하고 있다.

$_SERVER['SERVER_NAME']는 현재 DVWA가 실행되고 있는 도메인을 의미하며 $_SERVER['HTTP_REFERER']는 현재 페이지로 이동하기 이전 페이지를 의미한다.

이것으로 비밀번호 변경 요청이 현재 DVWA 사이트 내부에서 전송된 요청인지 확인할 수 있다.

만약 공격자가 악의적으로 생성한 CSRF 링크를 메일로 받거나 사이트 외부에서 클릭했다면 도메인이 일치하지 않기 때문에 CSRF 공격이라고 확인하는 것이다.

정상적인 비밀번호 변경 요청

이를  우회하기 위해서는 Referer 값에 localhost가 포함되기만 하면 된다. 따라서 html 파일명에 localhost를 포함하여 작성하고, 해당 링크로 접속하면 Referer 값에 localhost가 포함되기 때문에 비밀번호를 변경할 수 있게 된다.

 

Vulnerability: File Inclusion

 

<?php

// The page we wish to display
$file = $_GET[ 'page' ];

// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\\" ), "", $file );

?>

소스 코드를 보면 str_replace()로 "http://", "https://", "../", "..\"을 필터링하고 있다.

low레벨에서 http://localhost/DVWA-master/vulnerabilities/fi/?page=php://filter/convert.base64-encode/resource=../../hackable/flags/fi.php로 접속하면

소스코드를 확인할 수 있었는데, medium레벨에서는 불가능했다.

../../hackable/flags/fi.php가 str_replace()때문에 hackable/flags/fi.php로 바뀌었기 때문이다.

이를  우회하기위해 ../를 삭제해도 ../가 되도록 http://localhost/DVWA-master/vulnerabilities/fi/?page=php://filter/convert.base64-encode/resource=....//....//hackable/flags/fi.php로 변경하여 접속해보면

 

PD9waHAKCmlmKCAhZGVmaW5lZCggJ0RWV0FfV0VCX1BBR0VfVE9fUk9PVCcgKSApIHsKCWV4aXQgKCJOaWNlIHRyeSA7LSkuIFVzZSB0aGUgZmlsZSBpbmNsdWRlIG5leHQgdGltZSEiKTsKfQoKPz4KCjEuKSBCb25kLiBKYW1lcyBCb25kCgo8P3BocAoKZWNobyAiMi4pIE15IG5hbWUgaXMgU2hlcmxvY2sgSG9sbWVzLiBJdCBpcyBteSBidXNpbmVzcyB0byBrbm93IHdoYXQgb3RoZXIgcGVvcGxlIGRvbid0IGtub3cuXG5cbjxiciAvPjxiciAvPlxuIjsKCiRsaW5lMyA9ICIzLikgUm9tZW8sIFJvbWVvISBXaGVyZWZvcmUgYXJ0IHRob3UgUm9tZW8/IjsKJGxpbmUzID0gIi0tTElORSBISURERU4gOyktLSI7CmVjaG8gJGxpbmUzIC4gIlxuXG48YnIgLz48YnIgLz5cbiI7CgokbGluZTQgPSAiTkM0cEkiIC4gIkZSb1pTQndiMjlzIiAuICJJRzl1SUgiIC4gIlJvWlNCeWIyOW1JRzEiIC4gIjFjM1FnYUdGIiAuICIyWlNCaCIgLiAiSUd4bFkiIC4gIldzdSI7CmVjaG8gYmFzZTY0X2RlY29kZSggJGxpbmU0ICk7Cgo/PgoKPCEtLSA1LikgVGhlIHdvcmxkIGlzbid0IHJ1biBieSB3ZWFwb25zIGFueW1vcmUsIG9yIGVuZXJneSwgb3IgbW9uZXkuIEl0J3MgcnVuIGJ5IGxpdHRsZSBvbmVzIGFuZCB6ZXJvZXMsIGxpdHRsZSBiaXRzIG9mIGRhdGEuIEl0J3MgYWxsIGp1c3QgZWxlY3Ryb25zLiAtLT4K

 

소스코드를 확인할 수 있다.

 

Vulnerability: File Upload

 

<?php

if( isset( $_POST[ 'Upload' ] ) ) {
    // Where are we going to be writing to?
    $target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
    $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

    // File information
    $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
    $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
    $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];

    // Is it an image?
    if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
        ( $uploaded_size < 100000 ) ) {

        // Can we move the file to the upload folder?
        if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
            // No
            echo '<pre>Your image was not uploaded.</pre>';
        }
        else {
            // Yes!
            echo "<pre>{$target_path} succesfully uploaded!</pre>";
        }
    }
    else {
        // Invalid file
        echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
    }
}

?>

medium레벨에서는 uploaded_type을 통해 image/jpeg, imge/png형식의 파일만 업로드할 수 있게 하였다.

이는 Content-Type만 변경해도 우회할 수 있다.

텍스트파일을 올리고 패킷을 intercept해보니 Content-Type: text/plain인 것을 확인할 수 있다.

이를 image/jpeg로 바꿔서 전송하면 업로드된다.

역시나 악의적인 php 파일이나 자바스크립트 파일이 업로드되었다면 피해가 발생할 수 있을 것이다.

 

 

Vulnerability: Insecure CAPTCHA

 

소스코드를 보면 low레벨과 달리 passed_captcha가 hidden으로 있다.

비밀번호를 변경해보고 개발자 도구-Network에서 captch Form Data로 확인해보니 step: 2에 passed_captcha가 true로 되어있는 것을 확인할 수 있었다.

low레벨처럼 step의 value만 2로 바꾸면 오류메세지가 뜬다.

이번엔 step 밑에 passed_captcha를 true로 추가하여 CAPTCHA인증을 거치지않고 비밀번호를 변경해보자.

CAPTCHA인증없이 성공적으로 비밀번호가 변경되었다.

 

Vulnerability: SQL Injection

 

medium레벨에선 목록에서 사용자 ID를 선택하게끔 되어있다.

이 패킷을 잡아서 low레벨처럼  ' union select 1, column_name from information_schema.columns where table_schema='dvwa' and table_name='users'#로 추가하여 전송해보면 실행이 안된다.

 

 

low레벨
medium레벨

소스 코드를 보면 $id가  따옴표로 감싸져 있지 않기 때문이다.

'를 떼고 다음과 같이 수정하여 입력해보면 실행된다.

union select user_id, password from dvwa.users#;

 

Vulnerability: SQL Injection (Blind)

 

소스코드를 보면 위 실습과 동일하게 $id가 따옴표가 감싸져 있지 않다.

 low레벨에서 버전의 가장 앞글자가 1인지 확인하기 위해 입력했던 1'and substr(version(),1,1)='1'#에서 따옴표를 떼고 입력하면

맞는 것으로 확인할 수 있다.

이후 low레벨과 동일하게 Brute Force공격을 통해 알아낼 수 있다.

 

 

Vulnerability: Weak Session IDs

 

소스코드를 보면 time()을 사용해서 초를 정수형태로 바꿔 쿠키 값에 적용시키는 것을 알 수 있다.

<?php

$html = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {
    $cookie_value = time();
    setcookie("dvwaSession", $cookie_value);
}
?>

개발자도구-Network에서 Generate 버튼을 연속해서 누르고 weak_id의 쿠키를 확인하면 dvwaSession이 변화하지 않는다.

 

시간을 두어 눌러보면 그만큼 증가하는 것을 볼 수 있다.

 

 

Vulnerability: DOM Based Cross Site Scripting (XSS)

 

소스코드를 보면  stripos()로 <script 문자열이 있으면 default=English로 바뀌는 것을 확인할 수 있다.

<?php

// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
    $default = $_GET['default'];
    
    # Do not allow script tags
    if (stripos ($default, "<script") !== false) {
        header ("location: ?default=English");
        exit;
    }
}

?>

http://localhost/DVWA-master/vulnerabilities/xss_d/?default=<script>alert(document.cookie)</script>로 일반적인 스크립트를 작성하면 default=English로 변하여 스크립트 실행이 안된다.

low레벨에서와 같이 #을 이용해서 스크립트를 실행시키면 # 뒤에 오는 문장은 서버에 전달이 안되기 때문에 http://localhost/DVWA-master/vulnerabilities/xss_d/?default=#<script>alert(document.cookie)</script>는 실행가능하다.

 

Vulnerability: Reflected Cross Site Scripting (XSS)

 

이번 소스코드에서는 str_replace()로 '<script>' 를  필터링하고 있는 것을 확인할 수 있다.

<?php

header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
    // Get input
    $name = str_replace( '<script>', '', $_GET[ 'name' ] );

    // Feedback for end user
    echo "<pre>Hello ${name}</pre>";
}

?>

str_replace()는 대상 문자열만 한 번 바꾸기 때문에 <<script>script>alert(document.cookie)</script>로 써주면 스크립트 실행이 가능하다. 

입력값을 넣으면 http://localhost/DVWA-master/vulnerabilities/xss_r/?name= 로 이동하는데 뒤에  <<script>script>alert(document.cookie)</script>를 붙여 실행시켜보자.

스크립트가 실행되어 사용자의 쿠키를 확인할 수 있다.

 

Vulnerability: Stored Cross Site Scripting (XSS)

 

<?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();
}

?>

 

소스코드를 보면 $message에서 stirp_tags를 사용하여 문자열에서 모든 HTML 태그와 PHP 태그를 제거하고,

$name에서는 str_replace로 '<script>'를 필터링하는 것을 알 수 있다.

 

아래와 같이 Message란에 스크립트문을 작성하고 Sign Guestbook 버튼을 누르면, 저장된 Message는 script 태그는 삭제되는 것을 확인할 수 있다.

 

따라서 Name에 <<script>script>location.href="https://dreamhack.io"</script>을 입력하여 실행시켜보자.

해당 url로 이동하는 것을 확인할 수 있다.

 

Vulnerability: Content Security Policy (CSP) Bypass

 

<?php

$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";

header($headerCSP);

// Disable XSS protections so that inline alert boxes will work
header ("X-XSS-Protection: 0");

# <script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
    " . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
    <p>Whatever you enter here gets dropped directly into the page, see if you can get an alert box to pop up.</p>
    <input size="50" type="text" name="include" value="" id="include" />
    <input type="submit" value="Include" />
</form>
';

소스코드를 보면 Content-Security-Policy에 unsafe-inline옵션과 nonce옵션을 사용하여 특정 인라인 스크립트 블록만 허용하는 것을 확인할 수 있다.

이는 <script nonce=' '>를 사용하여 실행시킬 수 있다.

<script nonce='TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA='>alert(document.cookie);</script>를 입력하고 include 버튼을 누르면

스크립트가 정상적으로 실행된다.

 

Vulnerability: JavaScript Attacks

 

medium.js를 보면 token은 입력받은 문자열을 역순으로 재배치하고 XX를 붙인 것임을 알 수 있다.

개발자도구로 코드를 보면 token의 value가 "XXeMegnahCXX"로 되어있는 것을 확인할 수 있다.

 

이에 value를 'XXsseccusXX'로 수정하고 success를 입력 후 Submit 버튼을 눌러보자.

성공하였다.

728x90
반응형