发送不明确的请求
验证 Host 的代码和易受攻击的代码通常在应用程序的不同组件中,甚至位于不同的服务器上。通过识别和利用它们处理 Host 头的方式上的差异,你可以发出一个模棱两可的请求。
以下是几个示例,说明如何创建模棱两可的请求。
注入重复的 Host 头
一种可能的方法是尝试添加重复的 Host 头。诚然,这通常只会导致你的请求被阻止。但是,由于浏览器不太可能发送这样的请求,你可能会偶尔发现开发人员没有预料到这种情况。在这种情况下,你可能会发现一些有趣的行为怪癖。
不同的系统和技术将以不同的方式处理这种情况,但具体使用哪个 Host 头可能会存在差异,你可以利用这些差异。考虑以下请求:
GET /example HTTP/1.1 Host: vulnerable-website.com Host: bad-stuff-here
假设转发服务优先使用第一个标头,但是后端服务器优先使用最后一个标头。在这种情况下,你可以使用第一个报头来确保你的请求被路由到预期的目标,并使用第二个报头将你的有效负载传递到服务端代码中。
提供一个绝对的 URL 地址
虽然请求行通常是指定请求域上的相对路径,但许多服务器也被配置为理解绝对 URL 地址的请求。
同时提供绝对 URL 和 Host 头所引起的歧义也可能导致不同系统之间的差异。规范而言,在路由请求时,应优先考虑请求行,但实际上并非总是如此。你可以像重复 Host 头一样利用这些差异。
GET https://vulnerable-website.com/ HTTP/1.1 Host: bad-stuff-here
请注意,你可能还需要尝试不同的协议。对于请求行是包含 HTTP 还是 HTTPS URL,服务器的行为有时会有所不同。
添加 line wrapping
你还可以给 HTTP 头添加空格缩进,从而发现奇怪的行为。有些服务器会将缩进的标头解释为换行,因此将其视为前一个标头值的一部分。而其他服务器将完全忽略缩进的标头。
由于对该场景的处理极不一致,处理你的请求的不同系统之间通常会存在差异。考虑以下请求:
GET /example HTTP/1.1 Host: bad-stuff-here Host: vulnerable-website.com
网站可能会阻止具有多个 Host 标头的请求,但是你可以通过缩进其中一个来绕过此验证。如果转发服务忽略缩进的标头,则请求会被当做访问 vulnerable-website.com
的普通请求。现在让我们假设后端忽略前导空格,并在出现重复的情况下优先处理第一个标头,这时你就可以通过 "wrapped" Host 头传递任意值。
其他技术
这只是发布有害且模棱两可的请求的许多可能方法中的一小部分。例如,你还可以采用 HTTP 请求走私技术来构造 Host 头攻击。请求走私的详细内容请查看该主题文章。
注入覆盖 Host 的标头
即使不能使用不明确的请求重写 Host 头,也有其他在保持其完整的同时重写其值的可能。这包括通过其他的 HTTP Host 标头注入有效负载,这些标头的设计就是为了达到这个目的。
正如我们已经讨论过的,网站通常是通过某种中介系统访问的,比如负载均衡器或反向代理。在这种架构中,后端服务器接收到的 Host 头可能是这些中间系统的域名。这通常与请求的功能无关。
为了解决这个问题,前端服务器(转发服务)可以注入 X-Forwarded-Host
头来标明客户端初始请求的 Host 的原始值。因此,当 X-Forwarded-Host
存在时,许多框架会引用它。即使没有前端使用此标头,也可以观察到这种行为。
你有时可以用 X-Forwarded-Host
绕过 Host 头的任何验证的并注入恶意输入。
GET /example HTTP/1.1 Host: vulnerable-website.com X-Forwarded-Host: bad-stuff-here
尽管 X-Forwarded-Host
是此行为的实际标准,你可能也会遇到其他具有类似用途的标头,包括:
X-Host
X-Forwarded-Server
X-HTTP-Host-Override
Forwarded
从安全角度来看,需要注意的是,有些网站,甚至可能是你自己的网站,无意中支持这种行为。这通常是因为在它们使用的某些第三方技术中,这些报头中的一个或多个是默认启用的。
如何利用 HTTP Host 头
一旦确定可以向目标应用程序传递任意主机名,就可以开始寻找利用它的方法。
在本节中,我们将提供一些你可以构造的常见 HTTP Host 头攻击的示例。
- 密码重置中毒
- Web 缓存中毒
- 利用典型的服务器端漏洞
- 绕过身份验证
- 虚拟主机暴力破解
- 基于路由的 SSRF
密码重置中毒
攻击者有时可以使用 Host 头进行密码重置中毒攻击。更多内容参见本系列相关部分。
通过 Host 头的 Web 缓存中毒
在探测潜在的 Host 头攻击时,你经常会遇到看似易受攻击但并不能直接利用的情况。例如,你可能会发现 Host 头在没有 HTML 编码的情况下反映在响应标记中,甚至直接用于脚本导入。反射的客户端漏洞(例如 XSS )由 Host 标头引起时通常无法利用。攻击者没法强迫受害者的浏览器请求不正确的主机。
但是,如果目标使用了 web 缓存,则可以通过缓存向其他用户提供中毒响应,将这个无用的、反射的漏洞转变为危险的存储漏洞。
要构建 web 缓存中毒攻击,需要从服务器获取反映已注入负载的响应。不仅如此,你还需要找到其他用户请求也同时使用的缓存键。如果成功,下一步是缓存此恶意响应。然后,它将被提供给任何试图访问受影响页面的用户。
独立缓存通常在缓存键中包含 Host 头,因此这种方法通常在集成的应用程序级缓存上最有效。也就是说,前面讨论的技术有时甚至可以毒害独立的 web 缓存系统。
Web 缓存中毒有一个独立的专题讨论。
利用典型的服务端漏洞
每个 HTTP 头都是利用典型服务端漏洞的潜在载体,Host 头也不例外。例如,你可以通过 Host 头探测试试平常的 SQL 注入。如果 Host 的值被传递到 SQL 语句中,这可能是可利用的。
访问受限功能
某些网站只允许内部用户访问某些功能。但是,这些网站的访问控制可能会做出错误的假设,允许你通过对 Host 头进行简单的修改来绕过这些限制。这会成为其他攻击的切入点。
暴力破解使用虚拟主机的内部网站
公司有时会犯这样的错误:在同一台服务器上托管可公开访问的网站和私有的内部网站。服务器通常有一个公共的和一个私有的 IP 地址。由于内部主机名可能会解析为私有的 IP 地址,因此仅通过查看 DNS 记录无法检测到这种情况:
www.example.com:12.34.56.78 intranet.example.com:10.0.0.132
在某些情况下,内部站点甚至可能没有与之关联的公开 DNS 记录。尽管如此,攻击者通常可以访问他们有权访问的任何服务器上的任何虚拟主机,前提是他们能够猜出主机名。如果他们通过其他方式发现了隐藏的域名,比如信息泄漏,他们就可以直接发起请求。否则,他们只能使用诸如 Burp intruiter 这样的工具,通过候选子域的简单单词表对虚拟主机进行暴力破解。
基于路由的 SSRF
有时还可能使用 Host 头发起高影响、基于路由的 SSRF 攻击。这有时被称为 "Host header SSRF attacks" 。
经典的 SSRF 漏洞通常基于 XXE 或可利用的业务逻辑,该逻辑将 HTTP 请求发送到从用户可控制的输入派生的 URL 。另一方面,基于路由的 SSRF 依赖于利用在许多基于云的架构中流行的中间组件。这包括内部负载均衡器和反向代理。
尽管这些组件部署的目的不同,但基本上,它们都会接收请求并将其转发到适当的后端。如果它们被不安全地配置,转发未验证 Host 头的请求,它们就可能被操纵以将请求错误地路由到攻击者选择的任意系统。
这些系统是很好的目标,它们处于一个特权网络位置,这使它们可以直接从公共网络接收请求,同时还可以访问许多、但不是全部的内部网络。这使得 Host 头成为 SSRF 攻击的强大载体,有可能将一个简单的负载均衡器转换为通向整个内部网络的网关。
你可以使用 Burp Collaborator 来帮助识别这些漏洞。如果你在 Host 头中提供 Collaborator 服务器的域,并且随后从目标服务器或其他路径内的系统收到了 DNS 查询,则表明你可以将请求路由到任意域。
在确认可以成功地操纵中介系统以将请求路由到任意公共服务器之后,下一步是查看能否利用此行为访问内部系统。为此,你需要标识在目标内部网络上使用的私有 IP 地址。除了应用程序泄漏的 IP 地址外,你还可以扫描属于该公司的主机名,以查看是否有解析为私有 IP 地址的情况。如果其他方法都失败了,你仍然可以通过简单地强制使用标准私有 IP 范围(例如 192.168.0.0/16 )来识别有效的 IP 地址。
通过格式错误的请求行进行 SSRF
自定义代理有时无法正确地验证请求行,这可能会使你提供异常的、格式错误的输入,从而带来不幸的结果。
例如,反向代理可能从请求行获取路径,然后加上了前缀 http://backend-server
,并将请求路由到上游 URL 。如果路径以 /
开头,这没有问题,但如果以 @
开头呢?
GET @private-intranet/example HTTP/1.1
此时,上游的 URL 将是 http://backend-server@private-intranet/example
,大多数 HTTP 库将认为访问的是 private-intranet
且用户名是 backend-server
。
Password reset poisoning
密码重置中毒是一种技术,攻击者可以利用该技术来操纵易受攻击的网站,以生成指向其控制下的域的密码重置链接。这种行为可以用来窃取重置任意用户密码所需的秘密令牌,并最终危害他们的帐户。
密码重置是如何工作的
几乎所有需要登录的网站都实现了允许用户在忘记密码时重置密码的功能。实现这个功能有好几种方法,其中一个最常见的方法是:
- 用户输入用户名或电子邮件地址,然后提交密码重置请求。
- 网站检查该用户是否存在,然后生成一个临时的、唯一的、高熵的 token 令牌,并在后端将该令牌与用户的帐户相关联。
- 网站向用户发送一封包含重置密码链接的电子邮件。用户的 token 令牌作为 query 参数包含在相应的 URL 中,如
https://normal-website.com/reset?token=0a1b2c3d4e5f6g7h8i9j
。 - 当用户访问此 URL 时,网站会检查所提供的 token 令牌是否有效,并使用它来确定要重置的帐户。如果一切正常,用户就可以设置新密码了。最后,token 令牌被销毁。
与其他一些方法相比,这个过程足够简单并且相对安全。然而,它的安全性依赖于这样一个前提:只有目标用户才能访问他们的电子邮件收件箱,从而使用他们的 token 令牌。而密码重置中毒就是一种窃取此 token 令牌以更改其他用户密码的方法。
如何构造一个密码重置中毒攻击
如果发送给用户的 URL 是基于可控制的输入(例如 Host 头)动态生成的,则可以构造如下所示的密码重置中毒攻击:
- 攻击者根据需要获取受害者的电子邮件地址或用户名,并代表受害者提交密码重置请求,但是这个请求被修改了 Host 头,以指向他们控制的域。我们假设使用的是
evil-user.net
。 - 受害者收到了网站发送的真实的密码重置电子邮件,其中包含一个重置密码的链接,以及与他们的帐户相关联的 token 令牌。但是,URL 中的域名指向了攻击者的服务器:
https://evil-user.net/reset?token=0a1b2c3d4e5f6g7h8i9j
。 - 如果受害者点击了此链接,则密码重置的 token 令牌将被传递到攻击者的服务器。
- 攻击者现在可以访问网站的真实 URL ,并使用盗取的受害者的 token 令牌,将用户的密码重置为自己的密码,然后就可以登录到用户的帐户了。
在真正的攻击中,攻击者可能会伪造一个假的警告通知来提高受害者点击链接的概率。
即使不能控制密码重置的链接,有时也可以使用 Host 头将 HTML 注入到敏感的电子邮件中。请注意,电子邮件客户端通常不执行 JavaScript ,但其他 HTML 注入技术如悬挂标记攻击可能仍然适用。