Host头攻击
漏洞名称:Host头攻击
风险等级:低
问题类型:管理员设置问题
漏洞描述:
Host首部字段是HTTP/1.1新增的,旨在告诉服务器,客户端请求的主机名和端口号,主要用来实现虚拟主机技术。运用虚拟主机技术,单个主机可以运行多个站点。
例如:hacker和usagidesign两个站点都运行在同一服务器A上,不管我们请求哪个域名,最终都会被解析成服务器A的IP地址,这个时候服务器就不知道该将请求交给哪个站点处理,因此需要Host字段指定请求的主机名。
我们访问hacker域名,经DNS解析,变成了服务器A的IP,请求传达到服务器A,A接收到请求后,发现请求报文中的Host字段值为hacker,进而将请求交给hacker站点处理。
这个时候,问题就出现了。为了方便获取网站域名,开发人员一般依赖于请求包中的Host首部字段。例如,在php里用_SERVER["HTTP_HOST"],但是这个Host字段值是不可信赖的(可通过HTTP代理工具篡改)。
漏洞危害:
如果应用程序没有对Host字段值进行处理,就有可能造成恶意代码的传入。
为了方便获取网站域名,开发人员一般依赖于请求包中的Host首部字段。例如,在php里用_SERVER["HTTP_HOST"]
。但是这个Host字段值是不可信赖的(可通过HTTP代理工具篡改),如果应用程序没有对Host字段值进行处理,就有可能造成恶意代码的传入。
很多应用是直接把这个值不做HTML编码便输出到了页面中,比如:
<link href="http://_SERVER['HOST']" (Joomla)
还有的地方还包含有secret key和token:
<a href="http://_SERVER['HOST']?token=topsecret"> (Django, Gallery, others)
这样处理问题一般会很容易遭遇到两种常见的攻击:缓存污染和密码重置。缓存污染是指攻击者通过控制一个缓存系统来将一个恶意站点的页面返回给用户。密码重置这种攻击主要是因为发送给用户的内容是可以污染的,也就是说可以间接的劫持邮件发送内容。
密码重置
拿 Gallery 这个站来做例子。当我们进行密码重置的时候,网站会给我们发送一个随机的key:
$user -> hash = random::hash() ; $message -> confirm_url = url::abs_site("password/do_reset?key=$user->hash") ;
当用户点击重置密码的链接时,肯定可以说明点的是自己的账户。
这个地方的漏洞是: url::abs_site
这一部分使用的Host header是来自用户重置密码的请求,那么攻击者可以通过一个受他控制的链接来污染密码重置的邮件。
> POST /password/reset HTTP/1.1 > Host: evil.com > ... > csrf=1e8d5c9bceb16667b1b330cc5fd48663&name=admin
当然这种攻击方式一定要能骗取用户点击访问这个受污染的链接,如果用户警觉了没有点击,那么攻击就会失败。当然你自己也可以配合一些社会工程学的方法来保证攻击的成功率。
缓存污染
通过Host header来污染缓存的攻击方法最初是Carlos Beuno 在2008年提出来的。但是在现在的网络架构中,这种攻击还是比较困难的,因为现在的缓存设备都能够识别Host。比如对于下面的这两种情况他们绝对不会弄混淆:
> GET /index.html HTTP/1.1 > GET /index.html HTTP/1.1 > Host: example.com > Host: evil.com
因此为了能使缓存能将污染后的response返回给用户,我们还必须让缓存服务器看到的host header 和应用看到的host header 不一样。
比如说对于Varnish(一个很有名的缓存服务软件),可以使用一个复制的Host header。Varnish是通过最先到达的请求的host header来辨别host的,而Apache则是看所有请求的host,Nginx则只是看最后一个请求的host。这就意味着你可以通过下面这个请求来欺骗Varnish达到污染的目的:
> GET / HTTP/1.1
> Host: example.com
> Host: evil.com
应用本身的缓存也可能受到污染。比如Joomla就将取得的host值不经html编码便写进任意页面,而它的缓存则对这些没有任何处理。比如可以通过下面的请求来写入一个存储型的xss:
> GET / HTTP/1.1 > Host: cow"onerror='alert(1)'rel='stylesheet'
响应其实已经受到污染:
<link href="http://cow"onerror='alert(1)'rel='stylesheet'/" rel="canonical"/>
这时只需要浏览首页看是否有弹窗就知道缓存是否已经被污染了。
漏洞检测
Host头攻击漏洞的检测比较简单,只需要抓包,修改Host字段值,提交,查看响应中是否包含修改后的Host字段值即可。下面我分三个场景,介绍一下Host头攻击漏洞存在的表现。
1、跳转
【场景一】正常请求,响应302,Location首部字段指明跳转的地址,其中Location字段值为Host字段指定的地址。
将请求包的Host字段值修改为www.baidu.com提交,响应包中的Location地址也被更改为www.baidu.com。
2、拼接
【场景二】正常请求,正常响应,将Host字段值拼接到标签属性值中。
将请求包的Host字段值修改为www.baidu.com提交,发现服务器将www.baidu.com拼接到了script标签的src属性值中。
3、代码注入
【场景三】这其实也属于拼接,只不过在场景二的基础上写入了恶意代码。
利用Host字段写入script标签,弹出警告框。
修复建议
对Host字段进行检测
Nginx,修改ngnix.conf文件,在server中指定一个server_name名单,并添加检测。
Apache,修改httpd.conf文件,指定ServerName,并开启UseCanonicalName选项。
Tomcat,修改server.xml文件,配置Host的name属性。
Web应用程序应该使用SERVER_NAME
而不是host header
。在Apache和Nginx里可以通过设置一个虚拟机来记录所有的非法host header。在Nginx里还可以通过指定一个SERVER_NAME名单,Apache也可以通过指定一个SERVER_NAME名单并开启UseCanonicalName选项。
不要使用类似JSP中request.getServerName( )
方法引用客户端输入的hostname值。拼接生成URL时引用静态变量定义的服务器域名,或者使用相对路径生成URL。