CSRF(跨站請求偽造)
CSRF(跨站請求偽造),全稱為Cross-site request forgery,簡單來說,是攻擊者利用受害者尚未失效的身份認證信息,誘騙受害者點擊惡意鏈接或含有攻擊代碼的頁面,在受害者不知情的情況下以受害者的身份像服務器發起請求,從而實現非法攻擊(改密)。
CSRF主題:
Low
源碼解析
<?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); } ?>
通過GET方式獲取兩次密碼,兩次密碼輸入一致的話,就可以直接帶入數據中修改密碼。
漏洞復現
(1)先來做個嘗試,將密碼改成123456,兩次都輸入123456,發現直接修改成功了
(2)測試一下密碼123456,先測試一下原來的密碼(原來的密碼就是你登錄DVWA的密碼)
原來的密碼很明顯已經不行了,接下來試一下123456
(3)經過上面的嘗試發現可以成功修改賬號密碼,而且我們發現了URL那里的變化,接下來我們從URL處入手再改一下,首先先重置一下數據庫,把密碼改回來。
在 C:\phpstudy_pro\WWW 文件夾中建立一個 1.html ,里面寫入
<img src="http://127.0.0.3/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#" border="0" style="display:none;"/>
<h1>404<h1>
<h2>file not found.<h2>
這樣的話,當我們打開 1.html 文件的時候,密碼就會被修改成123456,(誘騙受害者點擊這個1.html文件)
Medium
源碼解析
<?php if( isset( $_GET[ 'Change' ] ) ) { // Checks to see where the request came from //stripos(str1, str2)檢查str2在str1中出現的位置(不區分大小寫),如果有返//回True,反之False //判斷Host字段是否出現在referer字段中 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); } ?>
Medium級別的代碼檢查了保留變量 HTTP_REFERER(http包頭的Referer參數的值,表示來源地址)中是否包含SERVER_NAME(http包頭的Host參數,及要訪問的主機名,這里是127.0.0.3),希望通過這種機制抵御CSRF攻擊。
漏洞復現
其實意思是這個referer中只要出現Host就可以正常操作
惡意網站中是這樣的,顯然是不成立的
Host 127.0.0.3
Referer http://127.0.0.1/
如果是這樣的呢
Host 127.0.0.3
Referer http://127.0.0.1/127.0.0.3.html
這樣就可以繞過,從而實現改密的操作了。
High
源碼解析
<?php if( isset( $_GET[ 'Change' ] ) ) { // Check Anti-CSRF token //可以看到加入了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(); ?>
High級別的代碼增加了Anti-CSRF token機制,用戶每次訪問改密頁面時,服務器會返回一個隨機的token,向服務器發起請求時,需要提交token參數,而服務器在收到請求時,會優先檢查token,只有token正確,才會處理客戶端請求。
漏洞復現
思路:利用xss,xss可以執行代碼,獲取token(在這路利用XSS其實是獲取的cookie值,但是cookie值中是包含token值的,所以在這個地方我們使用XSS執行。參考文章:https://www.cnblogs.com/zhaof/p/6281482.html)
(1)利用XSS獲取cookie
<script>alert(document.cookie)</script>
復制: PHPSESSID=8kha1nlrqamui5c8n6k1au4md1; security=low
(2)抓包修改密碼
Impossible
源碼分析
<?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(); ?>
漏洞復現
要求我們先輸入密碼再修改,攻擊者不知道原始密碼的情況下是無法發起 CSRF 攻擊的。
常見防范措施
1、加入Anti-CSRF,每次向客戶端發送一個隨機數,當客戶端向服務器發送數據時對比隨機數從而確定身份
2、獲取當前用戶密碼,以此判斷是否為當前用戶操作
3、二次確認(提示、二次密碼、驗證碼)