Host Header欺骗在密码找回中的利用

看到了一篇国外的blog,分享了一个找回密码功能中利用$_SERVER['HTTP_HOST']窃取token的姿势,本来我以为是email header injection,后来发现不是。雨神在他的blog 为什么$_SERVER['HTTP_HOST']有时是不可控的 中就提到$_SERVER['HTTP_HOST']是可控的:
一些同学会以为HTTP的联机就是靠包里的HOST header来连接的,所以会认为如果修改掉包里的HOST, 那么就连接不到目标服务器,所以是不可控的。 其实HTTP的联机与包里的HOST并没有啥关系, HTTP的联机是TCP/IP建立的, 所以修改掉HOST HEADER并不会把包丢到另外一个服务器上。
方便理解,这里要介绍一下HTTP_HOST和SERVER_NAME的区别: HTTP_HOST是从客户端获取的,用户可控,SERVER_NAME是从服务器配置文件获取的,如:
<?php echo $_SERVER['SCRIPT_NAME'].PHP_EOL; echo $_SERVER['HTTP_HOST'].PHP_EOL; echo $_SERVER['SERVER_NAME']; ?>
我服务器上的server_name是bugbountychina.com
image
image
当应用使用$_SERVER['HTTP_HOST']获取网站URL并拼接到找回密码的链接当中,就会产生漏洞。
当你使用网站密码重置功能,网站URL可能在将来会改变,所以要动态地生成一个邮箱找回密码的链接来匹配服务器的URL。这个时候可能就会使用$_SERVER['HTTP_HOST']
Host header 是客户端提供的,意味着攻击者可以将他们自己的网址写到重置密码的邮件当中,威胁任意用户的账户安全。下面是一个攻击场景:

  1. 攻击者确认目标用户邮箱地址
  2. 攻击者将重置密码请求中的host header设置成他们自己的网址
  3. 目标用户会接收到如下的邮件
    image
  4. 目标用户出于对公司的信任,点击了这个重置链接。由于这个链接是由host header生成的,会指向攻击者的网站。当目标用户访问了这个网站,他们的密码重置token也会被发给攻击者。
  5. 此时攻击者可以使用目标用户的token重置他们的密码

Mavenlink上项目地址是public HackerOne program

  • Mavenlink允许用户注册一个mavenlink.com的子域名去展示他们的内容,所以他们需要一个方法去动态地决定公司的子域名。
  • host header 可以设置为mavenlink.com的任意子域名,但是从mavenlink.com更改域名将会返回一个错误,而不是发送一个邮件。
  • 添加一个随机的mavenlink.com的子域名,但是这会被定向到正确的页面,没办法插入自己的域名。
  • 测试特殊字符。服务器接受host header中的一个问号,生成下面的链接
    Host: example.com?.mavenlink.com
  • 这样就可以从用户邮件中盗取密码重置token
    image

报告:
https://hackerone.com/reports/281575
结论:
发送密码重置邮件时,不要信任host header,在任何情况下也不要信任它。相反,如果需要对一个动态的URL进行解析,最好的方法是将host作为一个服务端变量储存。

原文:
https://lightningsecurity.io/blog/host-header-injection/
参考:
https://stackoverflow.com/questions/2297403/what-is-the-difference-between-http-host-and-server-name-in-php/2297421#2297421
http://www.yulegeyu.com/2017/04/24/%E4%B8%BA%E4%BB%80%E4%B9%88$_SERVER['HTTP_HOST']%E6%9C%89%E6%97%B6%E6%98%AF%E4%B8%8D%E5%8F%AF%E6%8E%A7%E7%9A%84%E3%80%82/
https://stackoverflow.com/questions/2297403/what-is-the-difference-between-http-host-and-server-name-in-php/2297421#2297421