CSRF全称为跨站请求伪造(Cross-site request forgery),是一种网络攻击方式,也被称为 one-click attack 或者 session riding。CSRF攻击利用网站对于用户网页浏览器的信任,挟持用户当前已登陆的Web应用程序,去执行并非用户本意的操作。下图是CSRF的攻击原理:
CSRF与XSS的区别:
(1)首先,确定我们的用户名和密码分别为:admin和admin。
(2)按照提示,修改密码,抓取修改密码时浏览器所发的数据包;
(2)选择Engagement tools,点击CSRF Poc,最后会生成一段payload。
该payload就是一个特定的网站,一旦用户访问这个网站,该网站就会给原来改密码的服务端发送改密码请求。至于密码改成什么样,完全是由这个伪造的网站决定。
得到网站链接:http://burpsuite/show/3/tvpt0va0g5xddv4nytmm71z0p7uhhh2a
。
(3)现在可以关闭burp的拦截功能(只是不抓包,但是数据包还是先流向burp),然后在同一个1浏览器访问刚刚伪造的链接。发现密码已被修改!
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 = mysql_real_escape_string( $pass_new ); //转义在 SQL 语句中使用的字符串中的特殊字符(\x00、\n、\r、\、'、"、\x1a)
$pass_new = md5( $pass_new ); //使用MD5对密码进行加密
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysql_query( $insert ) or die( ''
. mysql_error() . '
' );//mysql_query()查询成功返回True;否则返回False。
// Feedback for the user
echo "Password Changed."; } else { // Issue with passwords matching echo "
Passwords did not match."; } mysql_close(); } ?>
分析:服务器收到修改密码的请求后,会检查参数password_new与password_conf是否相同,如果相同,就会修改密码,并没有任何的防CSRF机制。
(1)首先,确定我们的用户名和密码分别为:admin和admin。
(3)在同一个浏览器上访问生成的链接,抓包,修改renferer值为http://192.168.92.1:32769/vulnerabilities/csrf/
(4)重新登陆,发现密码已被修改。
注:这里绕过referer值是通过burp,实际情况下,被害者点击链接后,按照以上方法,攻击方是抓不到被害者的数据包的。还可以在伪造的服务端重新设置referer值来绕过referer检测。
if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
//eregi():在一个字符串搜索指定的模式的字符串。
//$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。
//$_SERVER['SERVER_NAME'] :服务器主机的名称。
//$_SERVER['HTTP_REFERER'] :链接到当前页面的前一页面的 URL 地址
if( eregi( $_SERVER[ 'SERVER_NAME' ], $_SERVER[ 'HTTP_REFERER' ] ) ) {
// 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 = mysql_real_escape_string( $pass_new );
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysql_query( $insert ) or die( ''
. mysql_error() . '
' );
// Feedback for the user
echo "Password Changed."; } else { // Issue with passwords matching echo "
Passwords did not match."; } } else { // Didn't come from a trusted source echo "
That request didn't look correct."; } mysql_close(); } ?>
分析:代码检查了保留变量HTTP_REFERER (http包头部的Referer字段的值,表示来源地址)是否包含SERVER_NAME(http包头部的 Host 字段表示要访问的主机名)。针对这一过滤规则,我们只要想办法绕过,那么我们后面的代码和low级别的基本都一样了,很容易实现CSRF攻击。
使用CSRF Token Tracker
插件,来捕获每次服务端返回的token。
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 = mysql_real_escape_string( $pass_new );
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysql_query( $insert ) or die( ''
. mysql_error() . '
' );
// Feedback for the user
echo "Password Changed."; } else { // Issue with passwords matching echo "
Passwords did not match."; } mysql_close(); } // Generate Anti-CSRF token generateSessionToken(); ?>
分析:High级别的代码增加了Anti-CSRF token机制,用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端请求。
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 = mysql_real_escape_string( $pass_curr ); //转义一下字符`\x00、\n、\r、\、'、"、\x1a`
$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 = mysql_real_escape_string( $pass_new );
$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 "Password Changed.
";
}
else {
// Issue with passwords matching
echo "Passwords did not match or current password incorrect.
";
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
分析: