歡迎光臨
我們一直在努力

DVWA(Damn Vulnerable Web Application)- Cross Site Request Forgery

DVWA(Damn Vulnerable Web Application)

DVWA(Damn Vulnerable Web Application)是一個用來進行弱點安全測試的網站系統,旨在為安全專業人員測試自己的專業技能和工具提供合法的環境,幫助web開發者更好的理解web應用安全防範的過程。

DVWA共有十個模組,分別是Brute Force(暴力破解)、Command Injection(命令注入)、CSRF(跨站請求偽造)、File Inclusion(檔案包含)、File Upload(檔案上傳)、Insecure CAPTCHA(不安全的驗證碼)、SQL Injection(SQL注入)、SQL Injection(Blind)(SQL盲注)、XSS(Reflected)(反射型跨站腳本)、XSS(Stored)(儲存型跨站腳本)。

需要注意的是,DVWA 的程式碼分為四種安全級別:Low,Medium,High,Impossible。初學者可以通過比較四種級別的程式碼,接觸到一些PHP代碼審計的內容。

Cross Site Request Forgery (low)

CSRF(跨站請求偽造),全稱Cross-site request forgery(跨站請求偽造),是指利用受害者尚未失效的身份認證資訊(cookie、會話等),誘騙其點選惡意連結或者訪問包含攻擊程式碼的頁面,在受害人不知情的情況下以受害者的身份向(身份認證資訊所對應的)伺服器傳送請求,從而完成非法操作(如轉帳、改密碼等)。CSRF與XSS最大的區別就在於,CSRF並沒有盜取cookie而是直接利用。

在這一題,題目要我們輸入New password與Confirm new password,先嘗試隨意輸入串密碼。
例:123456

送出後,顯示密碼已更改成功。仔細觀察可發現網址變成
http://127.0.0.1/DVWA/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#

也就是說,只要將這個網址貼給其他人,那麼當有人點擊後,他的密碼即會被改成123456。當然,前提是該使用者必須要先登入這個網站,才能成功觸發。

什麼意思呢? 我們直接看Source Code,第5、6行是輸入password_new與password_conf,第9行進行判斷 $pass_new == $ pass_conf 是否相同,接下來會進行將$pass_new進入到資料庫更新,過程中沒有進行任何防禦。

也就是說,假設今天管理員已經登入上這DVWA網站,而你將該網址傳送給管理員,並讓管理員點擊,即可成功更改管理員的密碼。

因為這只是練習的靶機網站,所以修改密碼的網址才會這麼明顯看出來,但在現實生活中,駭客會透過各種方法隱藏起來,甚至隱藏在偽造的網頁中,並添加短網址等,誘騙使用者點擊。

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // 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
        $html .= "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        $html .= "<pre>Passwords did not match.</pre>";
    }

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

?>

Cross Site Request Forgery (medium)

直接看Source code

<?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
            $html .= "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            $html .= "<pre>Passwords did not match.</pre>";
        }
    }
    else {
        // Didn't come from a trusted source
        $html .= "<pre>That request didn't look correct.</pre>";
    }

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

?>

主要關鍵點是

if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )

意思就是判斷來源地址與伺服器地址是否一樣,而stripos函數是指查詢字串在另一字串中第一次出現的位置。

我們利用Burp Suite來查看下效果是如何,可以觀察Host與Referer,這是在此級別中會去判斷的

如果受害者點擊了
http://192.168.43.162/DVWA/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#
可以發現缺少了Referer,所以驗證無法通過,導致無法實現CSRF。

那麼要如何繞過呢?
我們可以偽造一個Referer,例如 evil.com/192.168.43.162.html
在請求中添加Referer:192.168.43.162

<img src="http://192.168.43.162/DVWA/vulnerabilities/csrf/?password_new=1234&password_conf=1234&Change=Change#"border="0" style="display:none;"/>
<h1>CSRF demo<h1>


當使用者訪問192.168.43.162.html後,即可達到CSRF的攻擊。

Cross Site Request Forgery (High)

我們直接從Burp Suite查看結果,可觀察到多了一個user_token

接下來直接看Source Code,第5行增加了token機制,當使用者每次訪問時,都會得到一組token參數值並驗證。所以通常可利用XSS的方式來取得使用者的cookie、token值搭配CSRF修改密碼。

<?php

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

    // 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
        $html .= "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        $html .= "<pre>Passwords did not match.</pre>";
    }

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

// Generate Anti-CSRF token
generateSessionToken();

?>

Cross Site Request Forgery (Impossible)

最後的級別,直接看Source Code,除了增加Anti-CSRF token外,也添加了PDO來防禦SQL Injection,更重要的是修改密碼功能也添加了需要輸入原始密碼才能修改。

所以就算我們用XSS的方式取得管理員的cookie,我們也是無法知道原始密碼,無法修改密碼。

<?php

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

    // Get input
    $pass_curr = $_GET[ 'password_current' ];
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

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

    // Check that the current password is correct
    $data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
    $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
    $data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
    $data->execute();

    // Do both new passwords match and does the current password match the user?
    if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
        // It does!
        $pass_new = stripslashes( $pass_new );
        $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 database with new password
        $data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
        $data->bindParam( ':password', $pass_new, PDO::PARAM_STR );
        $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
        $data->execute();

        // Feedback for the user
        $html .= "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        $html .= "<pre>Passwords did not match or current password incorrect.</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?>
贊(0) 打賞
轉載請附上作者連結:波波的寂寞世界 » DVWA(Damn Vulnerable Web Application)- Cross Site Request Forgery

波波的寂寞世界

Facebook聯繫我們

覺得文章有用,請作者喝杯咖啡

掃一掃打賞作者狗糧