HTTP 主机头攻击
HTTP Host 头的作用是帮助识别客户端想要与哪个后端组件进行通信。如果请求没有包含 Host 头,或者 Host 头以某种方式格式不正确,这可能导致将传入请求路由到目标应用程序时出现问题。
虚拟主机
单个 Web 服务器托管多个网站或应用程序。这可能是多个由单个所有者拥有的网站,也可能是不同所有者的网站托管在单个共享平台上。这种情况不如以前常见,但一些基于云的 SaaS 解决方案中仍然存在。
无论在哪种情况下,尽管这些不同的网站将具有不同的域名,但它们都与服务器共享一个共同的 IP 地址。在单个服务器上以这种方式托管的网站被称为“虚拟主机”。
通过中介路由流量
当网站托管在不同的后端服务器上,但客户端和服务器之间的所有流量都通过一个中介系统路由时。这可能是一个简单的负载均衡器或某种类型的反向代理服务器。这种设置在客户端通过内容分发网络(CDN)访问网站的情况下尤为普遍
HTTP 主机头攻击
HTTP Host 头攻击利用处理 Host 头值不安全的易受攻击的网站。如果服务器隐式信任 Host 头,并且未能正确验证或转义它,攻击者可能能够使用此输入注入有害的有效负载,以操纵服务器端行为。涉及直接将有效负载注入 Host 头的攻击通常被称为“Host 头注入”攻击。
HTTP Host 头漏洞通常源于对头信息不是用户可控的假设存在缺陷。这导致了对 Host 头的隐式信任,并导致对其值的验证或转义不足,尽管攻击者可以使用 Burp Proxy 等工具轻松修改它。
为了防止 HTTP 主机头攻击,最简单的方法是在服务器端代码中完全避免使用 Host 头。请务必检查每个 URL 是否真的需要是绝对路径。你通常会发现在某些情况下,使用相对路径就足够了。这种简单的更改可以帮助你特别防止网络缓存中毒漏洞。
预防 HTTP 主机头攻击
保护绝对 URL:当您必须使用绝对 URL 时,应在配置文件中手动指定当前域名,并使用此值而不是 Host 头。这种方法将消除密码重置中毒等威胁。
验证 Host 头:
如果必须使用 Host 头,请确保正确验证它。这应该包括将其与允许的域名白名单进行比对,并拒绝或重定向对未识别主机的请求。您应查阅框架文档以获取如何操作的指导。例如,Django 框架在设置文件中提供了 ALLOWED_HOSTS
选项。这种方法将降低您受到 Host 头注入攻击的风险。
不支持 Host 重写头:
检查您不支持可能被用于构建这些攻击的附加头信息,特别是 X-Forwarded-Host
。请记住,这些可能默认支持。
白名单允许的域名:
为防止对内部基础设施的基于路由的攻击,您应配置您的负载均衡器或任何反向代理,仅将请求转发到允许的域名白名单。
请注意仅限内部使用的虚拟主机:
使用虚拟主机时,应避免将仅限内部使用的网站和应用托管在与面向公众内容相同的同一服务器上。否则,攻击者可能通过操纵 Host 头访问内部域名。
绕过验证的漏洞
例如,某些解析算法会省略 Host 头中的端口号,这意味着只有域名被验证。如果您还能提供非数字端口号,您可以在不改变域名的情况下确保您到达目标应用程序,同时可能通过端口号注入有效载荷。
GET /example HTTP/1.1
Host: vulnerable-website.com:bad-stuff-here
其他网站可能会尝试应用匹配逻辑以允许任意子域名。在这种情况下,您可能可以通过注册一个以与白名单中域名相同的字符序列结尾的任意域名来完全绕过验证:
GET /example HTTP/1.1
Host: notvulnerable-website.com
利用您已经入侵的较不安全的子域名:
GET /example HTTP/1.1
Host: hacked-subdomain.vulnerable-website.com
注入重复的 Host 头
GET /example HTTP/1.1
Host: vulnerable-website.com
Host: bad-stuff-here
假设前端优先考虑第一个头信息实例,而后端更喜欢最后一个实例。在这种场景下,您可以使用第一个头信息确保您的请求被路由到目标,并使用第二个头信息将有效载荷传递到服务器端代码
提供绝对 URL
供绝对 URL 和 Host 头同时存在时,可能会引起歧义,这也会导致不同系统之间的差异。官方上,在路由请求时应优先考虑请求行,但在实际操作中,这并不总是如此。您可以通过与重复 Host 头类似的方式利用这些差异。
GET https://vulnerable-website.com/
HTTP/1.1 Host: bad-stuff-here
添加换行
可以通过在空格字符缩进 HTTP 头来揭示古怪的行为。一些服务器会将缩进的头部解释为换行,因此将其视为前一个头部值的组成部分。其他服务器将完全忽略缩进的头部
由于对这种情况的处理高度不一致,不同系统处理您的请求时通常会有差异。例如,考虑以下请求:
GET /example HTTP/1.1
Host: bad-stuff-here
Host: vulnerable-website.com
网站可能会阻止带有多个 Host 头部的请求,但您可能可以通过如下方式缩进其中一个来绕过此验证。如果前端忽略了缩进的头部,则请求将被处理为对 vulnerable-website.com
的普通请求。现在假设后端忽略了前导空格,并在重复的情况下优先考虑第一个头部。这种差异可能允许您通过“包装”的 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-Host
X-Forwarded-Server
X-HTTP-Host-Override
Forwarded
访问内部网站使用虚拟主机暴力破解
公司有时会犯下错误,将公开可访问的网站和私有内部网站托管在同一台服务器上。服务器通常具有公共和私有 IP 地址。由于内部主机名可能解析为私有 IP 地址,因此仅通过查看 DNS 记录通常无法检测到这种场景:
www.example.com: 12.34.56.78
intranet.example.com: 10.0.0.132
通过格式错误的请求行进行 SSRF
例如,一个反向代理可能会从请求行中获取路径,在其前面加上 http://backend-server
前缀,并将请求路由到那个上游 URL。如果路径以 /
字符开头,这没问题,但如果以 @
字符开头呢?
GET @private-intranet/example HTTP/1.1
结果的上游 URL 将是 http://backend-server@private-intranet/example
,大多数 HTTP 库将其解释为请求访问 private-intranet
,用户名为 backend-server