1. 程式人生 > >CSRF 漏洞測試

CSRF 漏洞測試

CSRF簡介:

CSRF中文名:跨站請求偽造,英文譯為:Cross-site request forgery,CSRF攻擊就是attacker(攻擊者)利用victim(受害者)尚未失效的身份認證資訊(cookie、session等),以某種方式誘騙victim點選attacker精心製作的惡意連結或者訪問包含惡意攻擊程式碼的頁面,當victim觸發成功之後,惡意程式碼會被執行,瀏覽器默默的向目標service發出請求載入著victim尚未失效的身份認證資訊,導致victim替attacker完成了非法操作比如:在某些論壇上釋出大量的惡意言論、網站使用者密碼被惡意篡改、賬戶金額被盜取、刪除網站個人資訊~~~~~

漏洞測試:

通過DVWA平臺,對CSRF漏洞進行測試,讓大家走進CSRF的世界

環境:dvwa服務IP      :192.168.43.146

           attacker服務IP :192.168.43.150

  • low level

檢視原始碼:

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

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

?> 

程式碼審計之後發現:網站沒有對CSRF做出防範,只對網站進行了SQL防禦(通過mysqli_real_escape_string()函式的過濾作用,將使用者傳入的資料中的特殊字元進行轉義,有效預防了attacker對網站的SQL注入攻擊)

mysqli_real_escape_string()函式詳細介紹:

定義和用法
mysql_real_escape_string() 函式轉義 SQL 語句中使用的字串中的特殊字元。

下列字元受影響:

\x00
\n
\r
\
'
"
\x1a
如果成功,則該函式返回被轉義的字串。如果失敗,則返回 false。

語法
mysql_real_escape_string(string,connection)

引數	     描述
string	     必需。規定要轉義的字串。
connection	 可選。規定 MySQL 連線。如果未規定,則使用上一個連線。

//有效預防了資料庫攻擊

既然網站沒有對CSRF保護那麼attacker就可以直接進行攻擊

使用者正常修改密碼:

使用者非正常修改密碼:

attacker製作含有惡意攻擊程式碼網頁

惡意程式碼:

	<iframe hidden src="http://192.168.43.146/dvwa/vulnerabilities/csrf/?password_new=attacker&password_conf=attacker&Change=Change#"></iframe>

attacker將程式碼嵌入自己的釣魚網頁中,當victim受害者被誘導訪問該網頁時,惡意程式碼就會自動被瀏覽器所執行並攜帶著victim未失效的身份認證資訊向目標伺服器發出請求,而victim卻毫無察覺,自己的使用者密碼已經被attacker惡意篡改

victim被attacker誘導瀏覽自己網站上的圖片(惡意程式碼就隱藏在該網頁中)

你會發現該網站一切好像很正常&很美好,但自己卻不知道已經被攻擊了,網站密碼已經被修改為attacker

此時victim再去登陸時會發現自己已經login不上了

  • medium level

檢視原始碼:

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

?> 

程式碼審計發現:該網站進行了referer欄位檢測,該欄位限制了不是同一個域的不能跨域訪問,attacker要想進行CSRF攻擊只要繞過if條件就可以,接下來分析該if條件

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

$_SERVER[ 'HTTP_REFERER' ] 獲取網頁請求的來源URL

$_SERVER[ 'SERVER_NAME' ] 獲取目標伺服器的域名

$_SERVER[ 'HTTP_REFERER' ]和$_SERVER[ 'SERVER_NAME' ]通過stripos()進行匹配,檢視$_SERVER[ 'SERVER_NAME' ]字串是否包含在$_SERVER[ 'HTTP_REFERER' ]中,從而判斷使用者的服務請求是否是跨域請求,若是跨域請求則會被目標伺服器所拒絕訪問

attacker可以有兩種方法進行繞過:

第一種:將網頁的名字改為$_SERVER[ 'SERVER_NAME' ].html
第二種:新增網頁父目錄包含$_SERVER[ 'SERVER_NAME' ]

在這裡使用第一種方法進行繞過:

victim瀏覽該網頁,惡意程式碼被成功執行,victim的網站密碼被惡意篡改

  • high level

檢視原始碼:

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

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

// Generate Anti-CSRF token
generateSessionToken();

?> 

程式碼審計發現:該網站進行了user_token的檢測

user_token:

當用戶每次訪問修改使用者密碼頁面時,伺服器會先返回一個隨機的token,接下來當用戶向伺服器發起修改密碼請求時,需要提交user_token,當請求到達伺服器時,伺服器會優先檢查token,判斷客戶端的token和服務端的token是否匹配,若不匹配,伺服器則會拒絕客戶端的請求

在這裡attacker是不能偽造token的,因為token是一個很長的隨機數,attacker要想CSRF攻擊成功,只有通過利用網站的XSS漏洞獲取使用者的token

XSS利用程式碼獲取user_token:

<iframe src="../csrf" onload=alert(document.getElementsByName('user_token'))></iframe>

有關XSS的利用可以檢視筆者的這篇文章XSS Stored

利用user_token進行CSRF攻擊

構造惡意程式碼:

victim瀏覽存在惡意攻擊的網頁

攻擊成功

  • impossible level

檢視原始碼:

<?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
        echo "<pre>Password Changed.</pre>";
    }
    else {
        // Issue with passwords matching
        echo "<pre>Passwords did not match or current password incorrect.</pre>";
    }
}

// Generate Anti-CSRF token
generateSessionToken();

?> 

程式碼審計發現:使用者修改網站密碼不僅需要user_token的驗證還需要使用者輸入當前密碼進行驗證,因此,attacker不能對使用者進行CSRF攻擊

總結:

學過CSRF和XSS之後,你可能會有疑惑,它們兩個一樣嗎,不用多說,相信大家看名字就知道不一樣

CSRF攻擊是直接利用使用者尚未失效的cookie,並偽造特殊的請求對使用者造成危害的一種攻擊手段

XSS攻擊是直接盜取使用者的cookie,所造成的一種攻擊手段

兩者看似相似,當又有一些不一樣的地方