# CORS - 配置错误和绕过
从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert) 支持HackTricks的其他方式: * 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)! * 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com) * 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family),我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family) * **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。** * 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。
## 什么是CORS? 跨源资源共享(CORS)标准**允许服务器定义谁可以访问它们的资源**以及外部来源可以使用哪些HTTP请求方法。 **同源策略**要求请求资源的服务器和托管资源的服务器共享相同的协议(例如`http://`)、域名(例如`internal-web.com`)和**端口**(例如80)。根据此策略,只有来自相同域和端口的网页才被允许访问资源。 在`http://normal-website.com/example/example.html`的上下文中应用同源策略如下所示: | 访问的URL | 允许访问? | | ----------------------------------------- | ---------------------------------- | | `http://normal-website.com/example/` | 是:相同的协议、域和端口 | | `http://normal-website.com/example2/` | 是:相同的协议、域和端口 | | `https://normal-website.com/example/` | 否:不同的协议和端口 | | `http://en.normal-website.com/example/` | 否:不同的域 | | `http://www.normal-website.com/example/` | 否:不同的域 | | `http://normal-website.com:8080/example/` | 否:不同的端口* | *Internet Explorer忽略端口号以执行同源策略,因此允许此访问。 ### `Access-Control-Allow-Origin` 头 此头可以允许**多个来源**、一个**`null`**值,或通配符**`*`**。然而,**没有浏览器支持多个来源**,并且使用通配符`*`受到**限制**。(通配符必须单独使用,不允许与`Access-Control-Allow-Credentials: true`一起使用。) 此头是**由服务器发出**,以响应网站发起的跨域资源请求,浏览器会自动添加一个`Origin`头。 ### `Access-Control-Allow-Credentials` 头 **默认情况下**,跨源请求不会携带像cookie或Authorization头这样的凭据。然而,跨域服务器可以通过将`Access-Control-Allow-Credentials`头设置为**`true`**来允许读取响应中的凭据。 如果设置为`true`,浏览器将传输凭据(cookie、授权头或TLS客户端证书)。 ```javascript var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { console.log(xhr.responseText); } } xhr.open('GET', 'http://example.com/', true); xhr.withCredentials = true; xhr.send(null); ``` ```javascript fetch(url, { credentials: 'include' }) ``` ```javascript const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://bar.other/resources/post-here/'); xhr.setRequestHeader('X-PINGOTHER', 'pingpong'); xhr.setRequestHeader('Content-Type', 'application/xml'); xhr.onreadystatechange = handler; xhr.send('Arun'); ``` ### CSRF 预检请求 ### 理解跨域通信中的预检请求 在特定条件下发起跨域请求,比如使用**非标准的 HTTP 方法**(除了 HEAD、GET、POST 之外的任何方法)、引入新的**头部**,或者使用特殊的**Content-Type 头部值**,可能需要进行预检请求。这个初步请求利用**`OPTIONS`**方法,用于通知服务器即将到来的跨域请求的意图,包括它打算使用的 HTTP 方法和头部。 **跨域资源共享(CORS)**协议规定了这种预检查,以确定请求的跨域操作的可行性,通过验证允许的方法、头部以及来源的可信度。要详细了解哪些条件可以避免需要预检请求,请参考[**Mozilla 开发者网络(MDN)**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests)提供的全面指南。 需要注意的是,**缺少预检请求并不意味着响应不需要携带授权头部**。没有这些头部,浏览器将无法处理来自跨域请求的响应。 考虑下面这个针对使用 `PUT` 方法和自定义头部 `Special-Request-Header` 的预检请求示例: ``` OPTIONS /info HTTP/1.1 Host: example2.com ... Origin: https://example.com Access-Control-Request-Method: POST Access-Control-Request-Headers: Authorization ``` 在响应中,服务器可能会返回指示接受的方法、允许的来源以及其他CORS策略细节的标头,如下所示: ```markdown HTTP/1.1 204 No Content ... Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: PUT, POST, OPTIONS Access-Control-Allow-Headers: Authorization Access-Control-Allow-Credentials: true Access-Control-Max-Age: 240 ``` - **`Access-Control-Allow-Headers`**: 该标头指定了在实际请求期间可以使用哪些标头。服务器设置此标头以指示来自客户端的请求中允许的标头。 - **`Access-Control-Expose-Headers`**: 通过此标头,服务器通知客户端除了简单响应标头之外哪些标头可以作为响应的一部分公开。 - **`Access-Control-Max-Age`**: 该标头指示预检请求的结果可以被缓存多长时间。服务器设置预检请求返回信息可以被重用的最长时间(以秒为单位)。 - **`Access-Control-Request-Headers`**: 在预检请求中使用,此标头由客户端设置,以通知服务器客户端希望在实际请求中使用哪些 HTTP 标头。 - **`Access-Control-Request-Method`**: 该标头也在预检请求中使用,由客户端设置以指示实际请求中将使用的 HTTP 方法。 - **`Origin`**: 该标头由浏览器自动设置,指示跨源请求的来源。服务器使用它来评估根据 CORS 策略是否应允许或拒绝传入请求。 请注意,通常(取决于内容类型和设置的标头),在 **GET/POST 请求中不会发送预检请求**(请求会 **直接** 发送),但如果要访问响应的 **标头/主体**,它必须包含一个允许的 _Access-Control-Allow-Origin_ 标头。\ **因此,CORS 不能防止 CSRF 攻击(但可能有所帮助)。** ### **本地网络请求预检请求** 1. **`Access-Control-Request-Local-Network`**: 此标头包含在客户端请求中,表示查询针对本地网络资源。它作为一个标记,通知服务器请求源自本地网络。 2. **`Access-Control-Allow-Local-Network`**: 作为响应,服务器使用此标头来传达所请求资源允许与本地网络之外的实体共享。它充当资源跨不同网络边界共享的绿灯,确保在维护安全协议的同时进行受控访问。 一个**允许本地网络请求的有效响应**还需要在响应中包含标头 `Access-Controls-Allow-Local_network: true`: ``` HTTP/1.1 200 OK ... Access-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: GET Access-Control-Allow-Credentials: true Access-Control-Allow-Local-Network: true Content-Length: 0 ... ``` {% hint style="warning" %} 请注意,Linux **0.0.0.0** IP 地址可用于**绕过**这些要求,以访问本地主机,因为该 IP 地址不被视为“本地”。 如果使用**本地端点的公共 IP 地址**(如路由器的公共 IP 地址),也可以**绕过本地网络要求**。因为在一些情况下,即使访问的是**公共 IP**,如果是**来自本地网络**,也会被授予访问权限。 {% endhint %} ## 可利用的配置错误 观察到将 `Access-Control-Allow-Credentials` 设置为**`true`**是大多数**真实攻击**的先决条件。此设置允许浏览器发送凭据并读取响应,增强了攻击的效果。如果没有这一设置,让浏览器发出请求而不是自己发出请求的好处将减弱,因为利用用户的 cookie 将变得不可行。 ### 异常情况:利用网络位置作为身份验证 存在一种例外情况,受害者的网络位置充当身份验证形式。这允许利用受害者的浏览器作为代理,绕过基于 IP 的身份验证以访问内部网络应用程序。这种方法与 DNS 重绑定在影响上有相似之处,但更容易利用。 ### 在 `Access-Control-Allow-Origin` 中反射 `Origin` 实际情况中,`Origin` 标头的值在 `Access-Control-Allow-Origin` 中反射的情况在理论上是不太可能的,因为这些标头的组合受到限制。然而,寻求为多个 URL 启用 CORS 的开发人员可能会通过复制 `Origin` 标头的值来动态生成 `Access-Control-Allow-Origin` 标头。这种方法可能会引入漏洞,特别是当攻击者使用一个旨在看起来合法的域名时,从而欺骗验证逻辑。 ```html ``` ### 利用 `null` Origin `null` origin 被指定用于重定向或本地 HTML 文件等情况,具有独特的位置。一些应用程序将此 origin 列入白名单以促进本地开发,无意中允许任何网站通过一个沙盒 iframe 模仿 `null` origin,从而绕过 CORS 限制。 ```html ``` ```html ``` ### 正则表达式绕过技术 在遇到域名白名单时,测试绕过机会至关重要,例如将攻击者的域名附加到白名单域名上或利用子域接管漏洞。此外,用于域验证的正则表达式可能会忽略域命名约定中的细微差别,从而提供进一步的绕过机会。 ### 高级正则表达式绕过 正则表达式模式通常集中在字母数字、点(.)和连字符(-)字符上,忽略了其他可能性。例如,设计为包含浏览器和正则表达式模式不同解释的字符的域名可以绕过安全检查。 Safari、Chrome 和 Firefox 对子域中下划线字符的处理展示了这种差异如何被利用以规避域验证逻辑。 **有关此绕过检查的更多信息和设置:** [**https://www.corben.io/advanced-cors-techniques/**](https://www.corben.io/advanced-cors-techniques/) **和** [**https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397**](https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397) ![https://miro.medium.com/v2/resize:fit:720/format:webp/1*rolEK39-DDxeBgSq6KLKAA.png](<../.gitbook/assets/image (153).png>) ### 从子域内的 XSS 开发人员经常实施防御机制来防止通过白名单允许请求信息的域进行 CORS 利用。尽管采取了这些预防措施,但系统的安全性并非绝对。在白名单域中存在一个脆弱的子域,即使只有一个,也可能通过其他漏洞(如 XSS 跨站脚本攻击)打开 CORS 利用的大门。 举例来说,考虑这样一个场景:域名 `requester.com` 被列入白名单,以访问另一个域名 `provider.com` 的资源。服务器端的配置可能如下所示: ```javascript if ($_SERVER['HTTP_HOST'] == '*.requester.com') { // Access data } else { // Unauthorized access } ``` 在这个设置中,允许`requester.com`的所有子域名访问。然而,如果一个子域名,比如`sub.requester.com`,存在XSS漏洞,攻击者可以利用这个弱点。例如,能够访问`sub.requester.com`的攻击者可以利用XSS漏洞绕过CORS策略,恶意访问`provider.com`上的资源。 ### **服务器端缓存投毒** **[来自这项研究](https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties)** 通过利用HTTP头注入来实施服务器端缓存投毒,有可能诱发存储的跨站脚本(XSS)漏洞。当应用程序未对`Origin`头部进行非法字符的清理时,特别是对于Internet Explorer和Edge用户,就会出现这种情况。这些浏览器将`\r`(0x0d)视为合法的HTTP头终止符,从而导致HTTP头注入漏洞。 考虑以下请求,其中操纵了`Origin`头部: ```text GET / HTTP/1.1 Origin: z[0x0d]Content-Type: text/html; charset=UTF-7 ``` Internet Explorer和Edge将响应解释为: ```text HTTP/1.1 200 OK Access-Control-Allow-Origin: z Content-Type: text/html; charset=UTF-7 ``` 直接利用这个漏洞通过让浏览器发送一个格式错误的标头来进行攻击是不可行的,但可以使用工具如Burp Suite手动生成一个精心构造的请求。这种方法可能会导致服务器端缓存保存响应并无意中将其提供给其他人。精心构造的有效负载旨在将页面的字符集更改为UTF-7,这是一种与XSS漏洞经常相关联的字符编码,因为它能够以一种可以在某些上下文中执行为脚本的方式对字符进行编码。 有关存储型XSS漏洞的更多信息,请参阅[PortSwigger](https://portswigger.net/web-security/cross-site-scripting/stored)。 **注意**:利用HTTP标头注入漏洞,特别是通过服务器端缓存投毒,强调了验证和清理所有用户提供的输入的重要性,包括HTTP标头。始终采用包括输入验证在内的强大安全模型以防止此类漏洞。 ### **客户端缓存投毒** **[来自这项研究](https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties)** 在这种情况下,观察到一个网页实例在没有适当编码的情况下反映了自定义HTTP标头的内容。具体来说,网页会反映出包含在`X-User-id`标头中的内容,其中可能包含恶意JavaScript,正如示例中标头包含一个SVG图像标签,旨在在加载时执行JavaScript代码。 跨域资源共享(CORS)策略允许发送自定义标头。然而,由于CORS限制,如果响应没有直接由浏览器呈现,这种注入的效用可能似乎有限。关键点在于考虑浏览器的缓存行为。如果未指定`Vary: Origin`标头,则可能会导致浏览器缓存恶意响应。随后,当导航到URL时,这个缓存的响应可能会直接呈现,绕过对初始请求的直接呈现的需求。这种机制通过利用客户端缓存增强了攻击的可靠性。 为了说明这种攻击,提供了一个JavaScript示例,旨在在网页环境中执行,比如通过JSFiddle。这个脚本执行一个简单的操作:它向指定URL发送一个包含恶意JavaScript的自定义标头的请求。在成功请求完成后,它尝试导航到目标URL,如果响应已被缓存而没有正确处理`Vary: Origin`标头,则可能触发注入脚本的执行。 以下是用于执行此攻击的JavaScript的摘要分解: ```html ``` ## 绕过 ### XSSI (跨站脚本包含) / JSONP XSSI,也称为跨站脚本包含,是一种利用脚本标签包含资源时同源策略(SOP)不适用的漏洞类型。这是因为脚本需要能够从不同的域中包含。这种漏洞允许攻击者访问和读取使用脚本标签包含的任何内容。 当涉及到动态JavaScript或JSONP(带填充的JSON)时,特别是当使用像 cookies 这样的环境权限信息进行身份验证时,这种漏洞变得特别重要。当从不同主机请求资源时,cookies 会被包含在内,使它们对攻击者可访问。 为了更好地理解和缓解这种漏洞,您可以使用 [https://github.com/kapytein/jsonp](https://github.com/kapytein/jsonp) 上提供的 BurpSuite 插件。该插件可以帮助识别和解决您的 Web 应用程序中潜在的 XSSI 漏洞。 [**在此处阅读有关不同类型 XSSI 及如何利用它们的更多信息。**](xssi-cross-site-script-inclusion.md) 尝试在请求中添加一个 **`callback`** **参数**。也许页面已准备将数据作为 JSONP 发送。在这种情况下,页面将以 `Content-Type: application/javascript` 返回数据,从而绕过 CORS 策略。 ![](<../.gitbook/assets/image (229).png>) ### 简单(无用?)绕过 绕过 `Access-Control-Allow-Origin` 限制的一种方法是请求 Web 应用程序代表您发出请求并返回响应。然而,在这种情况下,由于请求是向不同的域发出的,最终受害者的凭据不会被发送。 1. [**CORS-escape**](https://github.com/shalvah/cors-escape):此工具提供一个代理,转发您的请求及其标头,同时欺骗 Origin 标头以匹配请求的域。这有效地绕过了 CORS 策略。以下是使用 XMLHttpRequest 的示例: 2. [**simple-cors-escape**](https://github.com/shalvah/simple-cors-escape):此工具提供了另一种代理请求的方法。服务器不会原样传递您的请求,而是使用指定的参数发出自己的请求。 ### Iframe + Popup 绕过 您可以通过**创建一个 iframe**,**然后从中打开一个新窗口**来**绕过 CORS 检查**,例如 `e.origin === window.origin`。有关更多信息,请参阅以下页面: {% content-ref url="xss-cross-site-scripting/iframes-in-xss-and-csp.md" %} [iframes-in-xss-and-csp.md](xss-cross-site-scripting/iframes-in-xss-and-csp.md) {% endcontent-ref %} ### 通过 TTL 进行 DNS 重绑定 通过 TTL 进行 DNS 重绑定是一种利用操纵 DNS 记录绕过某些安全措施的技术。其工作原理如下: 1. 攻击者创建一个网页,使受害者访问该网页。 2. 然后,攻击者更改自己域的 DNS(IP)以指向受害者的网页。 3. 受害者的浏览器缓存 DNS 响应,该响应可能具有 TTL(生存时间)值,指示 DNS 记录应被视为有效的时间。 4. 当 TTL 到期时,受害者的浏览器会发出新的 DNS 请求,允许攻击者在受害者页面上执行 JavaScript 代码。 5. 通过控制受害者的 IP,攻击者可以从受害者那里获取信息,而无需向受害者服务器发送任何 cookies。 需要注意的是,浏览器具有缓存机制,即使 TTL 值较低,也可能阻止立即滥用此技术。 DNS 重绑定可用于绕过受害者执行的显式 IP 检查,或者用于用户或机器人在同一页面停留较长时间的情况,从而使缓存过期。 如果需要快速滥用 DNS 重绑定,您可以使用像 [https://lock.cmpxchg8b.com/rebinder.html](https://lock.cmpxchg8b.com/rebinder.html) 这样的服务。 要运行自己的 DNS 重绑定服务器,您可以利用类似 **DNSrebinder**([https://github.com/mogwailabs/DNSrebinder](https://github.com/mogwailabs/DNSrebinder))的工具。这涉及暴露本地端口 53/udp,创建指向其的 A 记录(例如 ns.example.com),并创建指向先前创建的 A 子域的 NS 记录(例如 ns.example.com)。然后,ns.example.com 子域的任何子域都将由您的主机解析。 您还可以在 [http://rebind.it/singularity.html](http://rebind.it/singularity.html) 上探索一个公开运行的服务器,以进一步了解和实验。 ### 通过 **DNS 缓存洪泛** 进行 DNS 重绑定 通过 DNS 缓存洪泛进行 DNS 重绑定是另一种绕过浏览器缓存机制并强制进行第二个 DNS 请求的技术。其工作原理如下: 1. 最初,当受害者发出 DNS 请求时,会收到攻击者的 IP 地址作为响应。 2. 为了绕过缓存防御,攻击者利用服务工作者。服务工作者会洪泛 DNS 缓存,有效地删除了缓存的攻击者服务器名称。 3. 当受害者的浏览器发出第二个 DNS 请求时,现在会收到 IP 地址 127.0.0.1 作为响应,通常指向本地主机。 通过使用服务工作者洪泛 DNS 缓存,攻击者可以操纵 DNS 解析过程,并迫使受害者的浏览器发出第二个请求,这次解析为攻击者所需的 IP 地址。 ### 通过 **缓存** 进行 DNS 重绑定 绕过缓存防御的另一种方法是在 DNS 提供程序中为同一子域使用多个 IP 地址。其工作原理如下: 1. 攻击者为 DNS 提供程序中的同一子域设置两个 A 记录(或一个具有两个 IP 的单个 A 记录)。 2. 当浏览器检查这些记录时,会收到两个 IP 地址。 3. 如果浏览器决定首先使用攻击者的 IP 地址,攻击者可以提供一个执行 HTTP 请求到同一域的有效载荷。 4. 但是,一旦攻击者获得受害者的 IP 地址,他们将停止响应受害者的浏览器。 5. 当浏览器意识到域名无响应时,会转而使用给定的第二个 IP 地址。 6. 通过访问第二个 IP 地址,浏览器绕过了同源策略(SOP),允许攻击者滥用此功能并获取和外泄受害者的信息。 该技术利用了浏览器在为域提供多个 IP 地址时的行为。通过策略性地控制响应并操纵浏览器选择的 IP 地址,攻击者可以利用 SOP 并从受害者那里访问信息。 {% hint style="warning" %} 请注意,为了访问 localhost,您应尝试在 Windows 中重新绑定 **127.0.0.1**,在 Linux 中重新绑定 **0.0.0.0**。\ 像 godaddy 或 cloudflare 这样的提供商不允许我使用 IP **0.0.0.0**,但 AWS route53 允许我创建一个具有 2 个 IP 的 A 记录,其中一个是 "0.0.0.0" {% endhint %} 有关更多信息,请查看 [https://unit42.paloaltonetworks.com/dns-rebinding/](https://unit42.paloaltonetworks.com/dns-rebinding/) ### 其他常见绕过方式 * 如果**不允许内部 IP**,可能会**忘记禁止 0.0.0.0**(在 Linux 和 Mac 上有效) * 如果**不允许内部 IP**,则响应一个**CNAME** 到 **localhost**(在 Linux 和 Mac 上有效) * 如果**不允许内部 IP** 作为 DNS 响应,您可以响应 **CNAMEs 到内部服务**,例如 www.corporate.internal。 ### 武器化的 DNS 重绑定 您可以在 [Gerald Doussot - State of DNS Rebinding Attacks & Singularity of Origin - DEF CON 27 Conference](https://www.youtube.com/watch?v=y9-0lICNjOQ) 中找到有关先前绕过技术及如何使用以下工具的更多信息。 [**`Singularity of Origin`**](https://github.com/nccgroup/singularity) 是一个用于执行 [DNS 重绑定](https://en.wikipedia.org/wiki/DNS\_rebinding) 攻击的工具。它包括重新绑定攻击服务器 DNS 名称的 IP 地址到目标机器的 IP 地址所需的组件,并提供攻击载荷以利用目标机器上的易受攻击软件。 ### 针对 DNS 重绑定的真正保护 * 在内部服务中使用 TLS * 请求身份验证以访问数据 * 验证 Host 标头 * [https://wicg.github.io/private-network-access/](https://wicg.github.io/private-network-access/):建议当公共服务器要访问内部服务器时始终发送预检请求 ## **工具** **模糊可能的 CORS 策略配置错误** * [https://github.com/chenjj/CORScanner](https://github.com/chenjj/CORScanner) * [https://github.com/lc/theftfuzzer](https://github.com/lc/theftfuzzer) * [https://github.com/s0md3v/Corsy](https://github.com/s0md3v/Corsy) * [https://github.com/Shivangx01b/CorsMe](https://github.com/Shivangx01b/CorsMe) ## 参考资料 * [https://portswigger.net/web-security/cors](https://portswigger.net/web-security/cors) * [https://portswigger.net/web-security/cors/access-control-allow-origin](https://portswigger.net/web-security/cors/access-control-allow-origin) * [https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#CORS) * [https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties](https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties) * [https://www.codecademy.com/articles/what-is-cors](https://www.codecademy.com/articles/what-is-cors) * [https://www.we45.com/blog/3-ways-to-exploit-misconfigured-cross-origin-resource-sharing-cors](https://www.we45.com/blog/3-ways-to-exploit-misconfigured-cross-origin-resource-sharing-cors) * [https://medium.com/netscape/hacking-it-out-when-cors-wont-let-you-be-great-35f6206cc646](https://medium.com/netscape/hacking-it-out-when-cors-wont-let-you-be-great-35f6206cc646) * [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/CORS%20Misconfiguration](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/CORS%20Misconfiguration) * [https://medium.com/entersoftsecurity/every-bug-bounty-hunter-should-know-the-evil-smile-of-the-jsonp-over-the-browsers-same-origin-438af3a0ac3b](https://medium.com/entersoftsecurity/every-bug-bounty-hunter-should-know-the-evil-smile-of-the-jsonp-over-the-browsers-same-origin-438af3a0ac3b)