CSRF中文名:跨站請求僞造,英文譯爲:Cross-site request forgery,CSRF攻擊就是attacker(攻擊者)利用victim(受害者)還沒有失效的身份認證信息(cookie、session等),以某種方式誘騙victim點擊attacker精心製做的惡意連接或者訪問包含惡意攻擊代碼的頁面,當victim觸發成功以後,惡意代碼會被執行,瀏覽器默默的向目標service發出請求加載着victim還沒有失效的身份認證信息,致使victim替attacker完成了非法操做好比:在某些論壇上發佈大量的惡意言論、網站用戶密碼被惡意篡改、帳戶金額被盜取、刪除網站我的信息~~~~~php
經過DVWA平臺,對CSRF漏洞進行測試,讓你們走進CSRF的世界html
環境:dvwa服務IP :192.168.43.146mysql
attacker服務IP :192.168.43.150sql
查看源碼:數據庫
<?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就能夠直接進行攻擊服務器
用戶正常修改密碼:cookie
用戶非正常修改密碼:session
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不上了
查看源碼:
<?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的網站密碼被惡意篡改
查看源碼:
<?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的利用能夠查看筆者的這篇文章(https://www.cnblogs.com/qftm/p/10317166.html)
利用user_token進行CSRF攻擊
構造惡意代碼:
victim瀏覽存在惡意攻擊的網頁
攻擊成功
查看源碼:
<?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,所形成的一種攻擊手段
二者看似類似,當又有一些不同的地方