Introduction

跨域资源共享 (CORS) 是一种允许 Web 应用程序安全地从不同域请求资源的机制。这在 Web 安全中至关重要,因为它可以防止一个页面上的恶意脚本通过浏览器访问另一个网页上的敏感数据。

同源策略 (SOP) 是一种安全措施,用于限制网页与不同来源的资源进行交互。来源由方案 (scheme)、主机名 (hostname) 和 URL 端口定义。

目标

  1. 了解 CORS 和 SOP 的基本原理。
  2. 识别并理解 CORS 和 SOP 配置的安全隐患。
  3. 在受控环境中利用与 CORS 和 SOP 相关的漏洞。
  4. 了解并应用缓解和预防这些漏洞的措施。

先决条件

  1. 对 Web 应用程序架构和服务端脚本有基本的了解。
  2. 熟悉 Web 服务器配置和 HTTP 标头。
  3. 了解 JavaScript 的 XMLHttpRequest (XHR) 或 Fetch API。

Understanding SOP

同源策略

同源策略(SOP)是一种指示 Web 浏览器如何在网页之间进行交互的策略。根据此策略,只有当两个网页共享同一来源时,一个网页上的脚本才能访问另一个网页上的数据。此“来源”通过 URI 方案、主机名和端口号的组合来标识。下图展示了 URL 及其所有功能的样子(它并非在每个请求中都使用所有功能)。

What URL looks like

此策略旨在防止一个页面上的恶意脚本通过浏览器访问另一个网页上的敏感数据。

SOP 示例

  1. 同域,不同端口:来自 https://test.com:80 的脚本可以访问来自 https://test.com:80/about 的数据,因为两者共享相同的协议、域和端口。但是,由于端口不同,它无法访问来自 https://test.com:8080 的数据。
  2. HTTP/HTTPS 交互:在 http://test.com(非安全 HTTP)上运行的脚本不允许访问 https://test.com(安全 HTTPS)上的资源,即使它们共享相同的域,因为协议不同。

常见误解

  1. SOP 的范围:人们普遍误以为 SOP 仅适用于脚本。实际上,它适用于所有网页元素,包括嵌入的图片、样式表和框架,并根据资源的来源限制其交互方式。
  2. SOP 限制所有跨域交互:另一个误解是 SOP 完全阻止所有跨域交互。虽然 SOP 确实限制了某些交互,但现代 Web 应用程序通常会利用各种技术(例如 CORS、postMessage 等)来实现安全可控的跨域通信。
  3. 同域即同源:人们通常认为,如果两个 URL 共享同一个域,那么它们就是同源的。然而,SOP 还会考虑协议和端口,因此两个具有相同域但不同协议或端口的 URL 会被视为不同源。

SOP 决策流程

SOP decision process

上面的流程图展示了浏览器在 SOP 下执行的检查顺序:首先检查协议是否匹配,然后检查主机名,最后检查端口号。如果三者都匹配,则允许该资源;否则,则阻止该资源。此图简化了概念,使其更易于理解和记忆。

Understanding CORS

跨域资源共享

跨域资源共享 (CORS) 是一种由 HTTP 标头定义的机制,允许服务器指定如何从不同来源请求资源。同源策略 (SOP) 默认限制网页只能向同一域名发出请求,而 CORS 允许服务器声明此策略的例外情况,允许网页在受控条件下从其他域名请求资源。

CORS 通过服务器作为响应的一部分发送给浏览器的一组 HTTP 标头来运行。这些标头告知浏览器服务器的 CORS 策略,例如允许哪些来源访问资源、允许哪些 HTTP 方法以及是否可以在请求中包含凭据。需要注意的是,服务器不会阻止或允许基于 CORS 的请求;相反,它会处理请求并在响应中包含 CORS 标头。然后,浏览器会解释这些标头,并通过根据指定的规则授予或拒绝网页的 JavaScript 对响应的访问权限来执行 CORS 策略。

CORS 中涉及的不同 HTTP 标头

  1. Access-Control-Allow-Origin:此标头指定哪些域可以访问资源。例如,Access-Control-Allow-Origin: example.com 仅允许来自 example.com 的请求。
  2. Access-Control-Allow-Methods:指定请求期间可以使用的 HTTP 方法(GET、POST 等)。
  3. Access-Control-Allow-Headers:指示实际请求期间可以使用的 HTTP 标头。
  4. Access-Control-Max-Age:定义预检请求结果的缓存时长。
  5. Access-Control-Allow-Credentials:此标头指示浏览器在请求中包含 Cookie、HTTP 身份验证或客户端 SSL 证书等凭据时,是否将响应公开给前端 JavaScript 代码。如果将 Access-Control-Allow-Credentials 设置为 true,则允许浏览器在请求中包含凭据的情况下访问来自服务器的响应。需要注意的是,使用此标头时,Access-Control-Allow-Origin 不能设置为 *,并且必须指定明确的域名以确保安全性。

CORS 的常见应用场景

CORS 通常应用于以下场景:

  1. API 和 Web 服务:当一个域中的 Web 应用程序需要访问托管在另一个域中的 API 时,CORS 会启用此交互。例如,位于 example-client.com 的前端应用程序可能需要从 example-api.com 获取数据。
  2. **内容分发网络 (CDN)**:许多网站使用 CDN 来加载 jQuery 或字体等库。CORS 使这些资源能够在不同域之间安全地共享。
  3. Web 字体:要跨域使用 Web 字体,必须设置 CORS 标头,以便网站从集中位置加载字体。
  4. 第三方插件/小工具:在网站上启用社交媒体按钮或来自外部来源的聊天机器人等功能。
  5. 多域用户身份验证:提供单点登录 (SSO) 或使用令牌(如 OAuth)跨域验证用户的服务依赖 CORS 来安全地交换身份验证数据。

简单请求与预检请求

CORS 中主要有两种类型的请求:简单请求和预检请求。

  1. 简单请求:这些请求符合 CORS 设定的某些标准,因此被称为“简单请求”。它们的处理方式与同源请求类似,但有一些限制。如果请求使用 GET、HEAD 或 POST 方法,并且 POST 请求的“Content-Type”标头为“application/x-www-form-urlencoded”、“multipart/form-data”或“text/plain”,则该请求被视为简单请求。此外,请求不应包含未列入 CORS 安全列表的自定义标头。简单请求会使用“Origin”标头直接发送到服务器,响应将根据“Access-Control-Allow-Origin”标头执行 CORS 策略。重要的是,如果站点先前设置了 Cookie 和 HTTP 身份验证数据,即使“Access-Control-Allow-Credentials”标头未设置为 true,简单请求中也会包含这些凭据。

  2. 预检请求:这些是 CORS 请求,浏览器在发送实际请求之前会使用 OPTIONS 请求进行“预检”,以确保服务器愿意根据其 CORS 策略接受该请求。当请求不符合“简单请求”的条件时,例如使用 GET、HEAD 或 POST 以外的 HTTP 方法,或者 POST 请求使用了简单请求允许值以外的其他“Content-Type”,或者包含自定义标头时,就会触发预检。预检 OPTIONS 请求包含“Access-Control-Request-Method”和“Access-Control-Request-Headers”等标头,用于指示实际请求的方法和自定义标头。服务器必须响应

  3. 适当的 CORS 标头,例如 Access-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Allow-Origin,用于指示实际请求是否被允许。如果预检成功,且 Access-Control-Allow-Credentials 设置为 true,浏览器将发送包含凭据的实际请求。

    CORS 请求流程

Process of CORS request

以上流程图展示了 CORS 请求的基本流程。

  1. 浏览器首先向服务器发送 HTTP 请求。
  2. 然后,服务器根据其允许的来源列表检查 Origin 标头。
  3. 如果来源允许,服务器将使用相应的 Access-Control-Allow-Origin 标头进行响应。
  4. 如果来源不允许,浏览器将阻止跨源请求。

ACAO in depth

Access-Control-Allow-Origin 标头

Access-Control-Allow-Origin 或 ACAO 标头是跨域资源共享 (CORS) 策略的重要组成部分。服务器使用它来指示网站上的资源是否允许来自不同来源的网页访问。此标头是服务器提供的 HTTP 响应的一部分。

当浏览器发出跨域请求时,它会在 HTTP 请求中包含请求站点的来源。然后,服务器会根据其 CORS 策略检查此来源。如果来源被允许,服务器会在响应中包含 Access-Control-Allow-Origin 标头,指定允许的来源或通配符 (*),通配符表示允许任何来源。

ACAO 配置

  1. 单源:
  • 配置:Access-Control-Allow-Origin: https://example.com
  • 含义:仅允许来自 https://example.com 的请求。这是一种安全的配置,因为它将访问限制在已知的、受信任的来源。
  1. 多源:
  • 配置:根据允许的源列表动态设置。
  • 含义:允许来自特定源的请求。虽然这比单源更灵活,但需要谨慎管理以确保仅包含受信任的源。
  1. 通配符源:
  • 配置:Access-Control-Allow-Origin: *
  • 含义:允许来自任何源的请求。这是最不安全的配置,应谨慎使用。它适用于不包含敏感信息的可公开访问资源。
  1. 使用凭据:
  • 配置:将 Access-Control-Allow-Origin 设置为特定源(不允许使用通配符),同时将 Access-Control-Allow-Credentials: true 设置为特定源。
  • 含义:允许在跨源请求中发送凭据,例如 Cookie 和 HTTP 身份验证数据。不过,需要注意的是,对于某些 GET 和 POST 请求等简单请求,浏览器会发送不带 Access-Control-Allow-Credentials 标头的 Cookie 和身份验证数据。对于使用 GET/POST 以外的方法或自定义标头的预检请求,Access-Control-Allow-Credentials 标头必须为 true,浏览器才能发送凭据。

ACAO 流程

Access-Control-Allow-Origin Flow

上面的流程图展示了服务器端确定“Access-Control-Allow-Origin”标头的简化流程。首先,它会检查 HTTP 请求是否包含源。如果不包含,则设置通配符(“*”)。如果存在源,服务器会检查该源是否在允许的源列表中。如果在,则服务器将 ACAO 标头设置为该特定源;否则,服务器不会设置 ACAO 标头,从而有效地拒绝访问。这有助于直观地了解 CORS 策略实施背后的决策过程。

Common Misconfigurations

常见的 CORS 配置错误

CORS 配置错误会在 Web 应用程序中造成严重的安全漏洞。了解这些常见的配置错误对于开发人员和安全专业人员都至关重要。我们将探讨几种典型的配置错误及其利用方式。

  1. 空源配置错误:当服务器接受来自“空”源的请求时,就会发生这种情况。这种情况可能发生在请求的来源不是标准浏览器环境的情况下,例如来自文件(“file://”)或数据 URL。攻击者可以制作一封包含指向恶意 HTML 文件的链接的钓鱼邮件。当受害者打开该文件时,它会向易受攻击的服务器发送请求,服务器会错误地将这些请求视为来自“空”源。服务器应配置为明确验证“空”源,除非必要且了解其含义,否则不应信任该源。
  2. 来源检查中的错误正则表达式:来源检查中配置不正确的正则表达式可能会导致接受来自非预期来源的请求。例如,像“/example.com$/”这样的正则表达式会错误地允许“badexample.com”。攻击者可以注册一个与有缺陷的正则表达式匹配的域名,并创建一个恶意网站向目标服务器发送请求。另一个糟糕的正则表达式示例可能与子域名有关。例如,如果允许以“example.com”开头的域名,攻击者可以使用“example.com.attacker123.com”。应用程序应确保用于验证来源的正则表达式模式经过全面测试,并且足够具体,以排除意外匹配。
  3. 信任任意提供的来源:某些服务器配置为在“Access-Control-Allow-Origin”响应标头中回显“Origin”标头值,从而有效地允许任何来源。攻击者可以构造一个具有受控来源的自定义 HTTP 请求。由于服务器回显了此来源,攻击者的站点可以绕过 SOP 限制。与其回显来源,不如维护一个允许来源的白名单并对其进行验证。

安全处理原产地检查

Secure handling of origin checks

上述流程图展示了一种处理 CORS 请求的安全方法。它首先检查来源是否为“null”,如果为“null”,则拒绝此类请求。如果不是,则检查来源是否在预定义的白名单中。如果来源在白名单中,服务器会将“Access-Control-Allow-Origin”设置为来源并继续处理请求。否则,服务器将拒绝请求,确保只允许白名单中的来源。此方法可以最大限度地降低 CORS 相关漏洞的风险。

注意: 务必理解 CORS 配置中的“安全性”与具体情况高度相关。虽然使用白名单并拒绝未指定的来源可以增强安全性,但在某些情况下,将“Access-Control-Allow-Origin”设置为“*”(允许所有来源)是一种有效且安全的选择。例如,对于不包含敏感信息且不依赖 Cookie 或身份验证令牌进行访问控制的可公开访问资源,可以安全地使用通配符 ACAO 标头。

Lab Connection

网络设置

首先将 corssop.thmexploit.evilcors.thmcorssop.thm.evilcors.thm 添加到您的 /etc/hosts 文件。我们使用多个域名是为了模拟不同域名之间的交互。

主机地址 IP 地址 应用程序名称 - 描述
corssop.thm 10.10.240.244 易受攻击的网站
explore.evilcors.thm 10.10.240.244 漏洞利用服务器 - 您可以在此处保存漏洞利用代码并将其直接发送给受害者。
corssop.thm.evilcors.thm 10.10.240.244 托管网站 - 受害者可在此处访问上述网站中保存的漏洞利用代码。

注意:漏洞利用服务器和托管网站位于同一 Web 服务器下。为了演示以下任务,我们将使用漏洞利用服务器来保存将在托管网站上提供的漏洞利用代码。

以下是示例 hosts 文件:

/etc/hosts

127.0.0.1       localhost
127.0.1.1 tryhackme.lan tryhackme
10.10.240.244 corssop.thm exploit.evilcors.thm corssop.thm.evilcors.thm

# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

渗透服务器设置

在以下任务中,我们将从目标网站获取受害者收到的响应。执行此操作需要 Apache。如果您使用 AttackBox,Apache 已安装并运行在 81 端口上。

如果您使用自己的机器,请使用命令“sudo apt install php apache2”安装 Apache 和 PHP。完成后,请前往“/var/www/html/”目录验证其是否已正确安装。

我们将使用 PHP 捕获从受害者服务器获取的数据。以下是“receiver.php”的内容。以下脚本从“php://input”捕获数据,然后将数据保存到文本文件中。将以下脚本另存为“receiver.php”,并将其保存到“/var/www/html/”目录。

<?php
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');

$postdata = file_get_contents("php://input");

file_put_contents('data.txt', $postdata);
?>

要确保 data.txt 文件可写,请执行以下一系列命令:

/var/www/html

root@attackbox:/var/www/html# touch data.txt
root@attackbox:/var/www/html# ls
data.txt index.html receiver.php
root@attackbox:/var/www/html# chmod 0777 data.txt
root@attackbox:/var/www/html# ls -lah
total 24K
drwxr-xr-x 2 root root 4.0K Mar 12 13:17 .
drwxr-xr-x 3 root root 4.0K Jan 24 13:51 ..
-rwxrwxrwx 1 root root 0 Mar 12 13:17 data.txt
-rw-r--r-- 1 root root 11K Jan 24 13:51 index.html
-rw-r--r-- 1 root root 215 Mar 12 13:09 receiver.php

Arbitrary Origin

任意源

与其他 CORS 漏洞相比,利用任意源漏洞相对容易,因为应用程序可以接受来自任何域名的跨域请求。例如,以下是 http://corssop.thm/arbitrary.php 的漏洞代码:

if (isset($_SERVER['HTTP_ORIGIN'])){
header("Access-Control-Allow-Origin: ".$_SERVER['HTTP_ORIGIN']."");
header('Access-Control-Allow-Credentials: true');
}

上述代码实现了一个有缺陷的 CORS 策略,因为它在未进行适当验证的情况下,将客户端请求中的 Origin 标头回显到 Access-Control-Allow-Origin 标头中。攻击者可能会使用类似 http://evilcors.thm 的来源,服务器就会将其回显。

Server echo the supplied origin

要利用上述易受攻击的代码,请访问 http://exploit.evilcors.thm。漏洞利用服务器已包含一段 JavaScript 代码,用于向目标应用程序发出跨域请求。示例漏洞利用代码位于 http://corssop.thm/exploits/data_exfil.html。该漏洞利用代码使用“XMLHttpRequest”向易受攻击的应用程序发送请求并处理响应。处理后的响应将与 receiver.php 文件一起发送到 Web 服务器。

在漏洞利用代码中,将目标 URL 更改为 http://corssop.thm/arbitrary.php。

change the target to arbitrary.php

另外,请更改接收窃取数据的 Web 服务器的 URL。如果您使用的是 AttackBox,请使用 ATTACKER_IP:81 模式,因为 Apache 在 81 端口运行。如果您使用的是自己的机器,请随意更改端口。

Change the exfiltrator server

完成更新后,点击“保存”按钮。要验证漏洞利用代码是否有效,请点击“查看漏洞利用代码”按钮。这将打开一个新标签页,其中包含托管服务器中保存的漏洞利用代码。

在新打开的标签页中,依次打开“开发者工具”>“网络”。应该有两个 XHR 连接。第一个请求发送到目标网站,第二个请求发送到窃取服务器。

two XHR requests in the network tab

现在,您可以点击漏洞利用服务器主页上的“发送至受害者”按钮。

注意:受害者将自动通过 http://evilcors.thm 访问包含漏洞利用代码的恶意网站。

要检查受害者是否已成功执行漏洞利用代码,请点击导航栏中的“日志”按钮检查漏洞利用服务器的日志。日志中应该包含来自 IP 10.10.39.12 的请求,因为这是受害者的 IP 地址。

Server logs showing the victim interaction

在你的渗透服务器中,你应该会收到来自受害者的 POST 请求。此 POST 请求包含我们漏洞利用中第一个 XHR 请求的完整网页响应。

注意: 由于受害者是在内部网络中模拟的,因此 IP 地址与上图不同。因此,出站连接将使用受害者机器的 IP 地址。

Outbound connection from the victim

打开位于 /var/www/html 中的 data.txt 来查看从用户那里窃取的数据。

Exfiltrated data from victim

在现实世界中,如果目标响应包含敏感数据(例如用户数据、令牌和 API 令牌),您的 JavaScript 可以捕获这些数据,并可能将其发送到您控制的服务器。总结一下,以下是用户点击或访问托管的漏洞利用代码后的整个利用过程:

Process of exploitation

Bad Regex in Origin

来源中的错误正则表达式

利用 CORS 来源处理中的错误正则表达式(regex)是一种利用 Web 应用程序用来验证 CORS 标头中来源的、实现不佳的正则表达式模式的技术。例如,以下是 http://corssop.thm/badregex.php 的易受攻击代码:

if (isset($_SERVER['HTTP_ORIGIN']) && preg_match('#corssop.thm#', $_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: ".$_SERVER['HTTP_ORIGIN']."");
header('Access-Control-Allow-Credentials: true');
}

上述代码实施的 CORS 策略存在缺陷,因为它会验证包含“corssop.thm”一词的域名。攻击者可能会使用类似“http://corssop.thm.evilcors.thm”这样的来源,从技术上来说,该来源与该模式相符。

supplied origin in the response

我们可以重复使用之前用来利用上述漏洞代码的漏洞代码。只需将目标 URL 更改为 http://corssop.thm/badregex.php 即可。由于漏洞代码托管在 http://corssop.thm.evilcors.thm 中,因此可以绕过 CORS。

change the target to badregex.php

完成更新后,再次点击“保存”按钮。要验证漏洞利用代码是否有效,请点击“查看漏洞利用代码”按钮。这将打开一个新标签页,其中包含托管服务器中保存的漏洞利用代码。

在新打开的标签页中,依次打开“开发者工具”>“网络”。应该有两个 XHR 连接。第一个请求发送到目标网站 (badregex.php),第二个请求发送到窃取服务器。

XHR connections in the network tab

然后,您可以通过点击漏洞利用服务器主页上的“发送至受害者”按钮,将漏洞利用代码发送给受害者。

注意:受害者会自动跳转到包含漏洞利用代码的页面。但是,在本例中,受害者访问的是域名(http://corssop.thm.evilcors.thm),而不是任务 7 中之前的域名。

要检查受害者是否成功执行了漏洞利用代码,请点击导航栏中的“日志”按钮查看漏洞利用服务器的日志。日志中应该包含来自 IP 10.10.39.12 的请求,因为这是受害者的 IP 地址。

Server logs with victim interaction

In your exfiltrator server, you should receive a POST request from the victim. This POST request contains the whole webpage response of the first XHR request in our exploit.

Note: The IP differs from the previous image since the victim is simulated in an internal network. So, an outbound connection will use the IP of the machine instead.

Once the request is saved, you can check the contents of the exfiltrated data in the text file.

Exfiltrated data from the victim

So, in a nutshell, below is the entire process of the exploitation once the user clicks or visits the hosted exploit code:

Entire process of exploitation