CSRF 漏洞测试

CSRF简介

CSRF中文名:跨站请求伪造,英文译为:Cross-site request forgery,CSRF攻击就是attacker(攻击者)利用victim(受害者)尚未失效的身份认证信息(cookie、session等),以某种方式诱骗victim点击attacker精心制作的恶意链接或者访问包含恶意攻击代码的页面,当victim触发成功之后,恶意代码会被执行,浏览器默默的向目标service发出请求加载着victim尚未失效的身份认证信息,导致victim替attacker完成了非法操作比如:在某些论坛上发布大量的恶意言论、网站用户密码被恶意篡改、账户金额被盗取、删除网站个人信息~~~~~

漏洞测试

通过DVWA平台,对CSRF漏洞进行测试,让大家走进CSRF的世界

环境:dvwa服务IP      :192.168.43.146

           attacker服务IP :192.168.43.150

low level

查看源码:

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( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); // Feedback for the user echo "
Password Changed.
"; } else { // Issue with passwords matching echo "
Passwords did not match.
"; } ((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就可以直接进行攻击

用户正常修改密码:

用户非正常修改密码:

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不上了

medium level

查看源码:

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( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); // 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.
"; } ((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的网站密码被恶意篡改

high level

查看源码:

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( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
' ); // Feedback for the user echo "
Password Changed.
"; } else { // Issue with passwords matching echo "
Passwords did not match.
"; } ((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浏览存在恶意攻击的网页

攻击成功

impossible level

查看源码:

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 "
Password Changed.
"; } else { // Issue with passwords matching echo "
Passwords did not match or current password incorrect.
"; } } // Generate Anti-CSRF token generateSessionToken(); ?>
代码审计发现:用户修改网站密码不仅需要user_token的验证还需要用户输入当前密码进行验证,因此,attacker不能对用户进行CSRF攻击

总结

学过CSRF和XSS之后,你可能会有疑惑,它们两个一样吗,不用多说,相信大家看名字就知道不一样

CSRF攻击是直接利用用户尚未失效的cookie,并伪造特殊的请求对用户造成危害的一种攻击手段

XSS攻击是直接盗取用户的cookie,所造成的一种攻击手段

两者看似相似,当又有一些不一样的地方

转载于:https://www.cnblogs.com/qftm/p/10317162.html

你可能感兴趣的:(CSRF 漏洞测试)