Host头漏洞概览
Host头的作用
Host头是HTTP协议中的一个请求头,通常位于请求行后。在HTTP 1.1中,请求必须包含Host头。一个HTTP请求的前两行看上去是这样的:
通常,当用户通过域名请求一个站点时,首先从DNS查询域名对应的IP地址(不考虑多个IP地址的情况,这种解析通常用于负载均衡),然后通过IP地址访问服务器。
在这种模型下,一个IP地址只能对应一个服务器的一个端口(通常站点不会要求用户手动输入端口号,只使用默认的80或443端口)。那么,如何仅用一个IP地址(或者说,一台主机)运营多个站点?一种方案是使用Host头来分辨用户访问的站点。例如,一台服务器上运行着两个站点,https://www.site-1.net/
和https://www.site-2.net/
,当HTTP请求来到时,服务器可以通过Host头来判断用户访问的站点,并转发给相应的程序进行处理。
除了虚拟主机,反向代理也可以利用类似的技术。
Host头漏洞的产生
Host头也可能被滥用,从而导致Host头漏洞的产生。需要记住,Host头是由客户端提供的,而任何来自客户端的数据都是不可信的。
不带过滤的反向代理
反向代理服务器如果没有对Host头作验证,可能会导致内网探测、SSRF等漏洞,请见未授权访问、基于路由的SSRF。
将Host头用作域名
除了上述的虚拟主机和反向代理,一个常见的场景是把Host头当作本站域名来用。例如,当后端服务器想要生成一个指向本站的链接:
// 伪代码,HOST_HEAD是请求中的Host值
url = "https://" + HOST_HEAD + "/my-resource"
当中间件生成一个302跳转响应:
location = PROTOCOL + "://" + HOST_HEAD + "/target"
这种情况常见于http提升到https,或默认路由非根目录等情况,中间件将访问根目录的用户跳转到默认路由:
或者,想要嵌入本站的某个JavaScript脚本:
<script src="https://<?php echo $_SERVER['HTTP_HOST'] ?>/script/my.js">
Host头漏洞的测试方法
首先,测试Host头漏洞时,需要确保修改Host字段后,请求包仍然能够被送到目标站点,否则,攻击就没有意义了。如上节所述,探测Host头漏洞的总体方法是,观察被修改后的恶意Host头是否会被服务器不当使用。例如,当服务器需要返回一个302响应,要求浏览器跳转到指定地址时,服务器是如何构造目标URL的?是否会利用不安全的Host头进行构造?再例如,重置密码时,服务器需要构造一个指向本站点的、并附带有秘密token作为参数的URL,利用邮件将这个URL发给用户。在这个过程中,构造URL的域名是从哪里来的?如果用户提供的Host头被不当使用了,那么就有可能引发Host头攻击。当然,服务器很可能包含一些过滤和验证手段,如果直接修改行不通,可以尝试以下方法绕过。
检查Host头的验证是否充分
如果修改Host头后,请求被站点发现并拒绝服务,可以尝试几种绕过手法。
- 有些情况下,Host字段值的端口号部分不会被检查,可以在此放置恶意内容,例如
Host: example.net:evil-here
; - 尝试在正常的域名前添加恶意内容,因为部分站点为了确保子域名不被屏蔽,只会匹配以固定域名结尾,即
/*mydomain.com/
; - 尝试匹配子域名,例如
/*.mydomain.com/
。
带有歧义的Host头
由于检查Host头合法性的代码和正真使用Host头的代码是不同的,甚至处于两个模块或两个软件中,因此可以构造具有歧义的Host头,利用两者处理歧义的方式不同绕过检查。
- 在请求中加入两条Host字段;
- 在请求行中加入完整URL,而在Host字段添加恶意内容。部分网页服务器可以处理请求行中的完整URL;
- 加入两条Host字段,并给第一条增加缩进。有些服务器会将缩进后的内容看成上一行的追加,有些服务器会将其忽略;
- 加入以下可能覆盖Host字段的请求头:
- X-Host
- X-Forwarded-Server
- X-HTTP-Host-Override
- Forwarded
如果经测试发现站点存在可疑的行为,就可以继续研究是否能构成实际威胁。
Host头漏洞的危害
根据Acunetix的报告,Host头漏洞的发生率为2.5%,并不高,但有可能导致较为严重的后果。Host头漏洞可能引发以下几种攻击。
密码重置污染
一种密码重置的基本流程是:
- 用户提交请求,包含用户名、邮箱;
- 服务器验证邮件和用户名是否对应,如果不对应,则拒绝请求;
- 服务器生成一个秘密token,并利用这个token构造一个URL,类似于
https://www.example.net/password-reset?token=RANDOM_TOKEN
; - 将这个URL通过邮件发送给用户,用户点击这个URL,就可以进行密码更新;
- 更新完成后,销毁token。
在这个流程中,服务器需要构造一个指向本站点的URL,这就有潜在的Host头攻击的可能性。假设恶意用户通过上述的某个方法,成功绕过验证,使服务器接受恶意数据,那么他可以:
- 在密码重置的页面中,填写他人的用户名和邮箱,例如
admin
和[email protected]
; - 发送请求包时,篡改Host头,例如改成
www.evil.com
; - 服务器错误地使用Host头来生成URL,构造出类似于
https://www.evil.com/password-reset?token=SECRET_CODE
的URL; - 用户不慎访问了错误的URL;
- 恶意用户查看
evil.com
的访问记录,即可获得上述URL,也就获得了SECRET_CODE
; - 恶意用户访问
https://www.example.net/password-reset?token=SECRET_TOKEN
,即可修改admin
的密码。
PortSwigger的实验中,还有一种利用”Dangling markup”技术(常用于构造反射型XSS)实现的密码重置污染,由于lab有详细的解析,这里不多叙述。链接可见参考内容5。
网页缓存污染
有时,为了网站的访问速度和可用性,站点会使用CDN或其他缓存技术。粗略来讲,当用户访问一个站点(实际是其缓存服务器),缓存服务器会判断用户请求的页面是否已经被缓存,如果是,则直接返回,否则向真实的网站服务器请求页面,再转发给用户并缓存下来,等待下一次请求。
网页缓存污染的问题出在:缓存服务器保存的页面来自于服务器,如果该次请求是由恶意用户发起,则会污染缓存,造成存储型的、持续产生影响的漏洞。例如下面这个会引发XSS的页面:
<!-- HTML code -->
<p>您好,本站的域名是<b id="domain"></b></p>
// JavaScript
// getHost通过访问php后端,从Host头获得域名
domain.textContent = getHost();
在没有缓存的情况下,攻击者无法有效利用这个漏洞,因为攻击者无法使别人的浏览器发起带有攻击报文的请求。但是在有缓存服务器的情况下,带有XSS攻击代码的页面可能被缓存服务器保留,其他用户访问该站点时就有可能中招。
未授权访问
部分站点或部分服务,例如控制面板、管理员站点,仅允许从本地访问,但仅仅通过Host字段是否为localhost
来判定,恶意用户将Host字段改为localhost
即可绕过鉴权。
基于路由的SSRF
中间件作为反向代理服务器使用时,需要了解客户端请求的站点的真实服务器是什么,这就需要解析Host头,然后代替用户向Web服务器发起HTTP请求,但如果对Host头的值过滤不当,则会造成SSRF攻击。
测试时,将Host头的值修改为自己的公开服务器地址,如果检测到HTTP流量,则说明中间件没有合理过滤Host头,且发起了请求。
该漏洞会允许攻击者通过中间件的服务器探测内网:
从而暴露内网站点。
SSRF还有可能引发其他漏洞,例如针对其它主机、内网主机甚至本主机的DoS和DDoS。
其它典型漏洞
可尝试其它典型的漏洞,例如XSS、SQL注入等等。
Host头攻击的防御方法
- 不要把Host头当作域名来用。预先配置好站点的域名,需要用到时,使用本地配置,而非从Host头中读取;
- 验证Host头。使用白名单的方法验证Host头是否合法,并拒绝带有不可识别的Host头的请求;
- 不要支持能够覆盖Host头字段的头。除了Host头,其他头部字段也有可能造成Host头攻击,特别是
X-Forwarded-Host
等,有时是默认开启的; - 注意保护仅供内网使用的站点,防止反向代理服务器将请求推送到内网站点。
参考内容
- Host头攻击 – PortSwigger
- 什么是DNS负载均衡 – Nginx
- SSRF引发的DDoS攻击 – Omkar Hiremath
- SSRF引发针对本机的DoS – Ax Sharma
- 通过Dangling markup实施密码重置污染 – PortSwigger
- 2021春网页漏洞报告 – Acunetix
本页面的图片使用Drawio绘制。