当小白菜第二天醒来,又开始学习新的任务。佬们要求小白菜学习CSRF,并要求告知原理和利用与防范。

一、CSRF的概述
CSRF(Cross-site request forgery)跨站点请求伪造,简单的说就是黑客伪造合法用户身份,对用户账户进行非自愿的恶意请求,造成合法用户的个人信息泄露或者财产损失的危害等。另外CSRF相比XSS,往往被人们忽视,但在某些时候却能造成巨大的破坏。
二、CSRF的攻击原理
结合自己和其他吴佬以及其他佬们的观点,我想这么理解它。CSRF的本质就是构造URL,并且需要成功猜测用户所有重要操作的所有参数,如果无法猜测到任何有效参数,那么这次构造请求是失败的,也就无法发起一次CSRF攻击。
三、CSRF的利用与防范
当小白菜掌握CSRF基本的概念的后,就摩拳擦掌想要用自己搭建的靶机进行网站漏洞进行测试。首先挑战低难度的CSRF。靶机的佬们给出如下界面,
3.1 高风险代码
图片说明
我们发现这是用户进行修改密码的操作,于是小白菜按照常规的方法上述原理,想要自己构造一个URL欺骗用户进行点击,然后自动修改其密码,导致用户退出后重新登陆不上,达到修改用户账户的密码的操作。可惜小白菜并不知道该如何构造url和参数,于是想到查看后台代码。


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

        $html .= "<pre>Password Changed.</pre>";

    }
    else {

        // Issue with passwords matching

        $html .= "<pre>Passwords did not match.</pre>";

    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);

}
?>

在后台中我们发现,服务器仅仅是简单利用

$pass_new == $pass_conf

判断就对数据库进行修改。而这里就是小白菜想要利用的地方,一种直接暴力的构造在url上构造

http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change

把用户的密码修改成123456,但不知情的用户在关闭了网页时,下一次登陆时便发现
图片说明
登陆失败,于是小白菜便把这个消息告诉平台的大佬,佬们不屑,别急再接着下一个任务吧。
3.2 中风险代码
于是佬们开始了下一个问题,这时佬们把原有的太代码进行改进。出现如下

<?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
            $html .= "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            $html .= "<pre>Passwords did not match.</pre>";

        }

    }
    else {
        // Didn't come from a trusted source
        $html .= "<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 ){}
else {
        // Didn't come from a trusted source
        $html .= "<pre>That request didn't look correct.</pre>";
    }

于是我们利用高风险的构造的url再次访问网页,发现网站出现如下情况
图片说明
可以证明,黑客无法对可信用户的账户密码进行修改。这这是由于构造的URL的请求来源与代码执行的主机不一致造成的。于是小白菜想着,那么能不能绕过检查网页的来源呢?小白菜打开了BurpSuite,对网页的正常的请求包进行拦截,看看来源时啥
图片说明
而对于黑客构造参数的URL,合法用户进行访问时出现
图片说明
我们看不到网页的来源,于是当服务器收到浏览器发来的请求时由于无法验证网页的来源,就认定这个请求不是该用户发来的于是就,不让其进行密码修改,而小白菜构造的参数也就失效了。此时小白菜想要利用这一点,于是利用BurpSuite构造网页请求的来源,进行绕过实现密码修改。
构造来源为:

Referer: http://www.liuyuying.com/127.0.0.1/index.html

图片说明
为了给小白菜增加难度,平台的佬们再一次增加难度
3.3 低风险代码

<?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

        $html .= "<pre>Password Changed.</pre>";

    }

    else {

        // Issue with passwords matching

        $html .= "<pre>Passwords did not match.</pre>";

    }
    ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);

}
// Generate Anti-CSRF token

generateSessionToken();
?>
这次细心小白菜发现,佬们在checkToken处增加难度。
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Generate Anti-CSRF token
generateSessionToken();

如果没办法获得用户Token的话,就无法构造URL,完成对用户账户的密码进行修改。但是小白菜又不太懂,于是佬们进行请教,佬们告诉小白菜说你可以利用XSS攻击试试看看可以获取不。于是小白菜构造一个标签进行XSS攻击,为获取用户的Token。
这个标签为

<iframe src=".../csrf/" onload=alert(frames[0].document.getElementsByName('user_token')[0].value)></iframe>
![图片说明](https://uploadfiles.nowcoder.com/images/20210128/9447520_1611839681907/0283C20E1773C174EF4A7F8EA064EAAA "图片标题") 
但是小白菜出现这个问题,嵌入iframe标签无法进行用户的token获取,页面数据长度太长。暂时无法处理,于是胆怯地向平台的佬们告知。佬们于是告诉小白菜,那么讲一讲CSRF的防御措施吧。

3.4 CSRF防范
由挑战难度任务不同,小白菜知道要防范黑客进行CSRF攻击,可以对HTTP包头进行Referer继续验证,或者让服务器生成一个足够随机Anti-Token,并对发起请求浏览器进行用户Token验证,如果验证成功,才可以对用户进行账户修改或对用户账户密码进行修改等操作。另外,小白菜通过在靶机上训练,知道可以利用二次验证的办法,让用户输入之前的密码,才能对密码进行修改,而之前的密码黑客无法得知,于是CSRF的攻击便失去了作用。