漏洞发现

  1. 漏洞产生于重置密码的功能点处

image-20250104123121291

  1. 用户输入正确的邮箱,点击重置密码后,邮箱会收到一个重置密码的链接,并带有一个能标识用户身份的唯一的加密过的令牌,这样使得该重置密码链接很难被攻击者伪造,防止被攻击者重置任意用户的密码
1
https://site.com/action-token?key=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzZWM2ODU2Z
  1. 这样一看确实没什么问题,很多白帽子也是看到这里的令牌无法被解密伪造,就觉得这里肯定没有任意用户密码重置的漏洞了
  2. 但是作者在头部添加了一个头部:X-Forwarded-Host,就发现了此漏洞
  3. 原本该系统的Host字段假设是:https://site.com,作者在重置密码时,就是要发送重置密码链接的那一步,抓包,并在请求头中添加了一个**X-Forwarded-Host**字段,值为一个自己可控的公网地址,假设是:https://hacker.com,结果神奇的事情发生了,重置密码的链接竟然成了这样
1
https://hacker.com/action-token?key=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzZWM2ODU2Z

可以看到作者通过X-Forwarded-Host字段控制了重置密码链接的主机地址,而这意味着什么呢?

漏洞利用

如果攻击者在重置密码时,输入一个受害者的邮箱,并添加一个X-Forwarded-Host字段,值为自己可控的公网地址(比如自己的vps的ip地址),点击重置密码,受害者就会收到一封密码重置的邮件,而链接的主机地址已经被窜改为了攻击者的可控的公网地址,一旦受害者点击了该链接,就相当于请求了该攻击者的公网地址并携带着自己的密码重置的令牌,那么,攻击者就能在服务器的请求日志中获取到受害者的令牌,从而重置受害者的密码,完成任意用户接管,或者如果觉得这样不方便,还可以在公网地址上写一点逻辑,自动监控并管理受害者的令牌。

原理分析

  1. 该漏洞的原理是什么呢?核心就在于X-Forwarded-Host字段,首先解释一下HostX-Forwarded-Host的区别

Host

Host 头部字段是 HTTP 请求中的标准字段,用于指定请求所发送到的服务器的主机名。对于 HTTP/1.1 请求,这是一个必需字段。它的作用是告诉服务器请求应该路由到哪个虚拟主机,因为一个服务器可能会通过同一个 IP 地址承载多个不同的虚拟主机(如同一个 IP 地址上有多个网站)。

格式:

1
Host: example.com

作用:

  • 确定请求发送到的主机名,通常用于虚拟主机配置。
  • 如果请求使用的是负载均衡器或代理服务器,Host 字段会包含客户端请求的目标域名。

X-Forwarded-Host

X-Forwarded-Host 是一个非标准的 HTTP 头部字段,通常由代理服务器或负载均衡器添加,用于在请求经过中间服务器或代理时传递原始客户端请求的主机信息。这个头部允许后端服务器知道原始客户端请求的目标主机是什么,即使请求已经被转发。

格式:

1
X-Forwarded-Host: original.example.com

作用:

  • 当请求经过反向代理、负载均衡器或其他中间代理时,X-Forwarded-Host 会记录客户端原始请求的主机名。
  • 有助于后端服务器根据请求的主机名来做不同的处理,尤其在有多个虚拟主机的情况下。

区别:

  • Host: 用于指定实际请求的目标主机名,这是请求本身的一个标准头部,通常是请求的目的服务器。
  • X-Forwarded-Host: 用于在请求通过代理或负载均衡器时传递客户端的原始请求主机名。它的主要目的是在经过代理层后保留原始的 Host 信息。

使用场景:

  • Host 头部是每个 HTTP 请求必须包含的,它直接告诉服务器请求的目标主机。
  • X-Forwarded-Host 主要在请求经过代理服务器时使用。例如,Web 服务器后面有一个反向代理或负载均衡器,X-Forwarded-Host 能帮助后端服务器知道客户端请求的原始目标主机,以便正确路由或处理请求。

示例:

假设一个请求经过了负载均衡器,然后转发到实际的 Web 服务器,原始请求的主机名是 example.com,而负载均衡器的地址是 loadbalancer.com。那么请求中的头部可能会这样:

1
2
Host: loadbalancer.com
X-Forwarded-Host: example.com

在这种情况下,后端服务器会看到 X-Forwarded-Host 中的 example.com,并可以根据该主机名进行相应的处理。

知道了以上这些,漏洞原理就很明了,其实就是攻击者加上了一个:X-Forwarded-Host字段,指向自己的可控的公网地址,而这个字段本身是用来在业务流程里有中间代理服务器的情况下,客户端请求中间代理服务器时,指明客户端的原始请求的主机地址是什么的。那么攻击者这里加上了X-Forwarded-Host字段,后面密码重置的链接就被窜改为了攻击者的地址,说明该程序没有对X-Forwarded-Host字段做可信的检查就相信了这是客户端的原始请求的主机地址,错误地把该密码重置的直接请求目标主机地址的过程当成了正在请求中代理服务器的过程,缺乏对X-Forwarded-Host的检查

格局打开

然而其实这个漏洞也属于X-Forwarded-Host 头部滥用漏洞,通常被称为 “Host Header Injection” 漏洞的一种变种。

在一个正常的应用中,X-Forwarded-Host 是一个由中间代理或负载均衡器插入的头部字段,目的是传递原始客户端请求的主机信息。当客户端请求经过代理时,代理会将实际请求的主机信息放在 X-Forwarded-Host 中,以便后端服务器能够知道原始请求的目标主机。

然而,如果应用程序 不对 X-Forwarded-Host 头进行有效的验证和清理,攻击者可以伪造这个头部字段,将其值更改为恶意的地址(例如,攻击者控制的地址),并将其附加到请求中。这就会导致:

  • 密码重置链接被劫持:攻击者可以通过更改 X-Forwarded-Host,使得应用程序生成的密码重置链接指向攻击者的地址,进而让攻击者可以窃取用户的重置密码请求。
  • 伪造请求:应用程序可能错误地将 X-Forwarded-Host 作为可信源,从而相信请求的主机名是真实的,进而在生成动态内容(如重置链接)时引导用户到攻击者控制的域名。

漏洞的具体步骤

  1. 攻击者构造恶意请求:攻击者将请求头中的 X-Forwarded-Host 修改为其控制的公网地址(如攻击者的恶意域名)。
  2. 绕过应用的域名验证:如果应用没有对 X-Forwarded-Host 进行严格的验证,它会错误地将攻击者提供的地址作为客户端请求的目标主机。
  3. 生成恶意链接:应用程序使用 X-Forwarded-Host 中的恶意地址生成密码重置链接或其他敏感信息的链接。此链接会指向攻击者的域名,而不是原始的可信域名。
  4. 用户点击链接:最终,用户收到的密码重置邮件中包含了攻击者控制的链接,用户点击后可能会导致数据泄露、账户控制或其他安全问题。

漏洞成因

这个漏洞的根本原因在于程序没有对 X-Forwarded-Host 进行可信性验证。正常情况下,X-Forwarded-Host 应该仅在代理/负载均衡器信任的情况下被使用,并且应该对其进行严格检查,以防止恶意伪造。

  • 缺乏验证:如果应用程序没有验证 X-Forwarded-Host 的值是否可信(比如是否属于允许的域名或是否与 Host 头一致),攻击者就能伪造该字段,造成恶意重定向。
  • 错误地将其当作真实的客户端信息:攻击者能够利用这一点,欺骗应用程序,导致应用错误地生成重定向到恶意主机的链接。

防御措施

  1. **严格验证 X-Forwarded-Host**:
    • X-Forwarded-Host 进行检查,确保其值符合预期,且只有在通过信任的代理服务器或负载均衡器时才被使用。
    • 验证 X-Forwarded-Host 的值是否与 Host 一致,或者是否属于白名单中允许的域名。
  2. 禁用不必要的代理头部
    • 在没有反向代理的情况下,可以直接忽略 X-Forwarded-Host 或限制其使用。
  3. 使用应用层防护
    • 除了验证 X-Forwarded-Host,还可以在应用层进行更多的安全性检查,如对生成的链接进行域名验证,确保其指向的是安全的、已知的可信域名。
  4. 配置反向代理和负载均衡器
    • 确保反向代理和负载均衡器的配置正确,确保它们仅传递受信任的头部字段,避免恶意篡改。