DVWA-CSRF

一、CSRF简介
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF。它是利用受害者尚未失效的身份认证信息(cookie、会话等),诱骗其点击恶意链接或者访问包含攻击代码的页面,在受害者不知情的情况下以受害者的身份向(身份认证信息所对应的)服务器发送请求,从而完成非法操作(如转账、改密等)。
二、原理示意图

CSRF攻击成功的前提条件:

   1、 用户必须登录
   2、Hacker必须懂得一些发包的请求
   3、 服务器端不会有二次认证
   4、 被害方是不知情的


现在我们在kali,dvwa进行试验操作。
安全级别:Low

查看源码
`<?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);

}

?> `
源码分析

从源码中可以看到,密码中不能包含SQL中的特殊字符;
服务器收到修改密码的请求后,会检查参数pass_new
与pass_conf是否相同,如果相同,则会更新数据库,
修改密码。这个过程没有任何的防CSRF机制(用户已经登录服务器)。
比如我们修改对应的URL
让参数pass_new与pass_conf相同。
127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
我们可以把对应的URL发给用户,如果用户点击

我们可以看见这样我们就可以成功修改用户的密码,
但是要注意需要服务器验证Cookie,所以必须让该URL和DVWA系统打开的浏览器一致,这样才可以验证成功
这个很明显,用户不容易上当。
修改密码的链接过于明显,可以使用一些缩短链接的方法,这样用户更容易上当。
当然我们也可以写一个修改密码的脚本文件,尝试访问该页面就可以修改密码

404

file not found.

这个脚本文件很简单
就是用一个标签插入http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#
这样当用户点击访问这个页面时,会以为访问的页面丢失了,但是当他打开这个页面时,用户的密码已经被修改了!
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);

}

?>
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )`
这里通过STRIPOS函数对比HTTP_REFERER,SERVER_NAME是否一致
后台的服务器会去检查HTTP_REFERER函数是否包含SERVER_NAME(host参数、主机名等),用此方法来抵御CSRF攻击
方法:
通过验证,就必须保证在Http请求中Referer字段中必须包含Host,所以攻击者只需要将文件名改成受害者的Host以及name就可以完美通过验证!
使用burp suite来抓包,发送到Repeater模块

然后修改一致

点击GO
回到游览器刷新页面

我们可以看到密码修改成功。
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 = 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( '<pre>' . mysql_error() . '</pre>' );

    // Feedback for the user

    echo "<pre>Password Changed.</pre>";

}

else {

    // Issue with passwords matching

    echo "<pre>Passwords did not match.</pre>";

}

mysql_close();

}

// Generate Anti-CSRF token

generateSessionToken();

?>
`
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
通过源代码分析可以看到该模块中加入了Anti-CSRF token来防范CSRF攻击,同时每次随机生成了一个Token,当
用户提交的时候,在服务器端比对一下Token值是否正确,不正确就丢弃掉,正确就验证通过。
将数据包发送到Repeater模块:

我们可以把Cookie里面的security的值修改为low。

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 = mysql_real_escape_string( $pass_curr );

$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 "<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级别的代码利用PDO技术防御SQL注入
对于防护CSRF,则要求用户输入原始密码,攻击者在不知道原始密码的情况下,无法进行CSRF攻击。

posted @ 2020-08-16 21:38  renletao  阅读(409)  评论(0编辑  收藏  举报