协议层的攻击——HTTP请求走私

释放双眼,带上耳机,听听看~!
作者:mengchen@知道创宇404实验室日期:2019年10月10日原文链接:https://paper.seebug.org/1048/1.前言最近在学习研究BlackHat的议题,其中有一篇议题——"HTTPDesyncAttacks:SmashingintotheCellN

作者:mengchen@知道创宇404实验室

日期:2019年10月10日

原文链接https://paper.seebug.org/1048/

1. 前言

最近在学习研究BlackHat的议题,其中有一篇议题——”HTTP Desync Attacks: Smashing into the Cell Next Door”引起了我极大地兴趣,在其中,作者讲述了HTTP走私攻击这一攻击手段,并且分享了他的一些攻击案例。我之前从未听说过这一攻击方式,决定对这一攻击方式进行一个完整的学习梳理,于是就有了这一篇文章。

当然了,作为这一攻击方式的初学者,难免会有一些错误,还请诸位斧正。

2. 发展时间线

最早在2005年,由Chaim Linhart,Amit Klein,Ronen Heled和Steve Orrin共同完成了一篇关于HTTP Request Smuggling这一攻击方式的报告。通过对整个RFC文档的分析以及丰富的实例,证明了这一攻击方式的危害性。

https://www.cgisecurity.com/lib/HTTP-Request-Smuggling.pdf

在2016年的DEFCON 24 上,@regilero在他的议题——Hiding Wookiees in HTTP中对前面报告中的攻击方式进行了丰富和扩充。

https://media.defcon.org/DEF%20CON%2024/DEF%20CON%2024%20presentations/DEF%20CON%2024%20-%20Regilero-Hiding-Wookiees-In-Http.pdf

在2019年的BlackHat USA 2019上,PortSwigger的James Kettle在他的议题——HTTP Desync Attacks: Smashing into the Cell Next Door中针对当前的网络环境,展示了使用分块编码来进行攻击的攻击方式,扩展了攻击面,并且提出了完整的一套检测利用流程。

https://www.blackhat.com/us-19/briefings/schedule/#http-desync-attacks-smashing-into-the-cell-next-door-15153

3. 产生原因

HTTP请求走私这一攻击方式很特殊,它不像其他的Web攻击方式那样比较直观,它更多的是在复杂网络环境下,不同的服务器对RFC标准实现的方式不同,程度不同。这样一来,对同一个HTTP请求,不同的服务器可能会产生不同的处理结果,这样就产生了了安全风险。

在进行后续的学习研究前,我们先来认识一下如今使用最为广泛的HTTP 1.1的协议特性——Keep-Alive&Pipeline

HTTP1.0之前的协议设计中,客户端每进行一次HTTP请求,就需要同服务器建立一个TCP链接。而现代的Web网站页面是由多种资源组成的,我们要获取一个网页的内容,不仅要请求HTML文档,还有JS、CSS、图片等各种各样的资源,这样如果按照之前的协议设计,就会导致HTTP服务器的负载开销增大。于是在HTTP1.1中,增加了Keep-AlivePipeline这两个特性。

所谓Keep-Alive,就是在HTTP请求中增加一个特殊的请求头Connection: Keep-Alive,告诉服务器,接收完这次HTTP请求后,不要关闭TCP链接,后面对相同目标服务器的HTTP请求,重用这一个TCP链接,这样只需要进行一次TCP握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。当然,这个特性在HTTP1.1中是默认开启的。

有了Keep-Alive之后,后续就有了Pipeline,在这里呢,客户端可以像流水线一样发送自己的HTTP请求,而不需要等待服务器的响应,服务器那边接收到请求后,需要遵循先入先出机制,将请求和响应严格对应起来,再将响应发送给客户端。

现如今,浏览器默认是不启用Pipeline的,但是一般的服务器都提供了对Pipleline的支持。

为了提升用户的浏览速度,提高使用体验,减轻服务器的负担,很多网站都用上了CDN加速服务,最简单的加速服务,就是在源站的前面加上一个具有缓存功能的反向代理服务器,用户在请求某些静态资源时,直接从代理服务器中就可以获取到,不用再从源站所在服务器获取。这就有了一个很典型的拓扑结构。

undefined

一般来说,反向代理服务器与后端的源站服务器之间,会重用TCP链接。这也很容易理解,用户的分布范围是十分广泛,建立连接的时间也是不确定的,这样TCP链接就很难重用,而代理服务器与后端的源站服务器的IP地址是相对固定,不同用户的请求通过代理服务器与源站服务器建立链接,这两者之间的TCP链接进行重用,也就顺理成章了。

当我们向代理服务器发送一个比较模糊的HTTP请求时,由于两者服务器的实现方式不同,可能代理服务器认为这是一个HTTP请求,然后将其转发给了后端的源站服务器,但源站服务器经过解析处理后,只认为其中的一部分为正常请求,剩下的那一部分,就算是走私的请求,当该部分对正常用户的请求造成了影响之后,就实现了HTTP走私攻击。

3.1 CL不为0的GET请求

其实在这里,影响到的并不仅仅是GET请求,所有不携带请求体的HTTP请求都有可能受此影响,只因为GET比较典型,我们把它作为一个例子。

RFC2616中,没有对GET请求像POST请求那样携带请求体做出规定,在最新的RFC7231的4.3.1节中也仅仅提了一句。

https://tools.ietf.org/html/rfc7231#section-4.3.1

sending a payload body on a GET request might cause some existing implementations to reject the request

假设前端代理服务器允许GET请求携带请求体,而后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的Content-Length头,不进行处理。这就有可能导致请求走私。

比如我们构造请求

GET / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 44\r\nGET / secret HTTP/1.1\r\nHost: example.com\r\n\r\n

前端服务器收到该请求,通过读取Content-Length,判断这是一个完整的请求,然后转发给后端服务器,而后端服务器收到后,因为它不对Content-Length进行处理,由于Pipeline的存在,它就认为这是收到了两个请求,分别是

第一个
GET / HTTP/1.1\r\n
Host: example.com\r\n

第二个
GET / secret HTTP/1.1\r\n
Host: example.com\r\n

这就导致了请求走私。在本文的4.3.1小节有一个类似于这一攻击方式的实例,推荐结合起来看下。

3.2 CL-CL

RFC7230的第3.3.3节中的第四条中,规定当服务器收到的请求中包含两个Content-Length,而且两者的值不同时,需要返回400错误。

https://tools.ietf.org/html/rfc7230#section-3.3.3

但是总有服务器不会严格的实现该规范,假设中间的代理服务器和后端的源站服务器在收到类似的请求时,都不会返回400错误,但是中间代理服务器按照第一个Content-Length的值对请求进行处理,而后端源站服务器按照第二个Content-Length的值进行处理。

此时恶意攻击者可以构造一个特殊的请求

POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 8\r\nContent-Length: 7\r\n12345\r\na

中间代理服务器获取到的数据包的长度为8,将上述整个数据包原封不动的转发给后端的源站服务器,而后端服务器获取到的数据包长度为7。当读取完前7个字符后,后端服务器认为已经读取完毕,然后生成对应的响应,发送出去。而此时的缓冲区去还剩余一个字母a,对于后端服务器来说,这个a是下一个请求的一部分,但是还没有传输完毕。此时恰巧有一个其他的正常用户对服务器进行了请求,假设请求如图所示。

GET /index.html HTTP/1.1\r\nHost: example.com\r\n

从前面我们也知道了,代理服务器与源站服务器之间一般会重用TCP连接。

这时候正常用户的请求就拼接到了字母a的后面,当后端服务器接收完毕后,它实际处理的请求其实是

aGET /index.html HTTP/1.1\r\n
Host: example.com\r\n

这时候用户就会收到一个类似于aGET request method not found的报错。这样就实现了一次HTTP走私攻击,而且还对正常用户的行为造成了影响,而且后续可以扩展成类似于CSRF的攻击方式。

但是两个Content-Length这种请求包还是太过于理想化了,一般的服务器都不会接受这种存在两个请求头的请求包。但是在RFC2616的第4.4节中,规定:如果收到同时存在Content-Length和Transfer-Encoding这两个请求头的请求包时,在处理的时候必须忽略Content-Length,这其实也就意味着请求包中同时包含这两个请求头并不算违规,服务器也不需要返回400错误。服务器在这里的实现更容易出问题。

https://tools.ietf.org/html/rfc2616#section-4.4

3.3 CL-TE

所谓CL-TE,就是当收到存在两个请求头的请求包时,前端代理服务器只处理Content-Length这一请求头,而后端服务器会遵守RFC2616的规定,忽略掉Content-Length,处理Transfer-Encoding这一请求头。

chunk传输数据格式如下,其中size的值由16进制表示。

[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]

Lab 地址:https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te

构造数据包

POST / HTTP/1.1\r\nHost: ace01fcf1fd05faf80c21f8b00ea006b.web-security-academy.net\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nCookie: session=E9m1pnYfbvtMyEnTYSe5eijPDC04EVm3\r\nConnection: keep-alive\r\nContent-Length: 6\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nG

连续发送几次请求就可以获得该响应。

undefined

由于前端服务器处理Content-Length,所以这个请求对于它来说是一个完整的请求,请求体的长度为6,也就是

0\r\n
\r\n
G

当请求包经过代理服务器转发给后端服务器时,后端服务器处理Transfer-Encoding,当它读取到0\r\n\r\n时,认为已经读取到结尾了,但是剩下的字母G就被留在了缓冲区中,等待后续请求的到来。当我们重复发送请求后,发送的请求在后端服务器拼接成了类似下面这种请求。

GPOST / HTTP/1.1\r\n
Host: ace01fcf1fd05faf80c21f8b00ea006b.web-security-academy.net\r\n
......

服务器在解析时当然会产生报错了。

3.4 TE-CL

所谓TE-CL,就是当收到存在两个请求头的请求包时,前端代理服务器处理Transfer-Encoding这一请求头,而后端服务器处理Content-Length请求头。

Lab地址:https://portswigger.net/web-security/request-smuggling/lab-basic-te-cl

构造数据包

POST / HTTP/1.1\r\nHost: acf41f441edb9dc9806dca7b00000035.web-security-academy.net\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nCookie: session=3Eyiu83ZSygjzgAfyGPn8VdGbKw5ifew\r\nContent-Length: 4\r\nTransfer-Encoding: chunked\r\n\r\n12\r\nGPOST / HTTP/1.1\r\n\r\n0\r\n\r\n

undefined

由于前端服务器处理Transfer-Encoding,当其读取到0\r\n\r\n时,认为是读取完毕了,此时这个请求对代理服务器来说是一个完整的请求,然后转发给后端服务器,后端服务器处理Content-Length请求头,当它读取完12\r\n之后,就认为这个请求已经结束了,后面的数据就认为是另一个请求了,也就是

GPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n

成功报错。

3.5 TE-TE

TE-TE,也很容易理解,当收到存在两个请求头的请求包时,前后端服务器都处理Transfer-Encoding请求头,这确实是实现了RFC的标准。不过前后端服务器毕竟不是同一种,这就有了一种方法,我们可以对发送的请求包中的Transfer-Encoding进行某种混淆操作,从而使其中一个服务器不处理Transfer-Encoding请求头。从某种意义上还是CL-TE或者TE-CL

Lab地址:https://portswigger.net/web-security/request-smuggling/lab-ofuscating-te-header

构造数据包

POST / HTTP/1.1\r\nHost: ac4b1fcb1f596028803b11a2007400e4.web-security-academy.net\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.5\r\nCookie: session=Mew4QW7BRxkhk0p1Thny2GiXiZwZdMd8\r\nContent-length: 4\r\nTransfer-Encoding: chunked\r\nTransfer-encoding: cow\r\n\r\n5c\r\nGPOST / HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 15\r\n\r\nx=1\r\n0\r\n\r\n

undefined

4. HTTP走私攻击实例——CVE-2018-8004

4.1 漏洞概述

Apache Traffic Server(ATS)是美国阿帕奇(Apache)软件基金会的一款高效、可扩展的HTTP代理和缓存服务器。

Apache ATS 6.0.0版本至6.2.2版本和7.0.0版本至7.1.3版本中存在安全漏洞。攻击者可利用该漏洞实施HTTP请求走私攻击或造成缓存中毒。

在美国国家信息安全漏洞库中,我们可以找到关于该漏洞的四个补丁,接下来我们详细看一下。

CVE-2018-8004 补丁列表

注:虽然漏洞通告中描述该漏洞影响范围到7.1.3版本,但从github上补丁归档的版本中看,在7.1.3版本中已经修复了大部分的漏洞。

4.2 测试环境

4.2.1 简介

在这里,我们以ATS 7.1.2为例,搭建一个简单的测试环境。

环境组件介绍

反向代理服务器
IP: 10.211.55.22:80
Ubuntu 16.04
Apache Traffic Server 7.1.2

后端服务器1-LAMP
IP: 10.211.55.2:10085
Apache HTTP Server 2.4.7
PHP 5.5.9

后端服务器2-LNMP
IP: 10.211.55.2:10086
Nginx 1.4.6
PHP 5.5.9

环境拓扑图

undefined

Apache Traffic Server 一般用作HTTP代理和缓存服务器,

给TA买糖
共{{data.count}}人
人已赞赏
HackerNews

一周新闻:网络钓鱼骗子转战Instagram;航旅纵横回应新功能泄露用户隐私。

2019-10-8 1:32:56

HackerNews

TeamViewer被入侵纯属误会?

2019-10-14 5:31:39

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索