最近一直在研究一些比较有意思的攻击方法与思路,在查阅本地文档的时候(没错,本地,我常常会将一些有意思的文章可是没时间看就会被我保存pdf到本地),一篇2019年Black hat的议题——HTTP请求走私,进入个人视野,同时我也查阅到在2020 Blackhat中该攻击手法再次被分析。我对此产生浓厚学习兴趣,因而便有了这篇文章。html
HTTP请求走私是一种HTTP协议的攻击利用方法,该攻击产生的缘由在于HTTP代理链中HTTP Server的实现中存在不一致的问题。前端
- 2004年,_@Amit Klein提出_HTTP Response Splitting 技术,为HTTP Smuggling攻击雏形;
- 2005年, 第一次被@Watchfire所提出, 并对其进行了详细介绍;
- 2016年, DEFCON 24上,@regilero在他的议题—— Hiding Wookiees in HTTP 中在对前面报告进行丰富与扩充;
- 2019年, Blackhat USA上,PortSwigger的@James Kettle在其议题—— HTTP DESYNC ATTACKS SMASHING INTO THE CELL NEXT DOOR 中对当前网络环境进行了分析,同时在其利用上加入chunked技术,对现有攻击面进行了拓展;
- 2020年, Blackhat USA上,@Amit Klein在其议题——_HTTP Request Smuggling in 2020_ 中最新变种手法进行分析,同时对各种环境场景下进行了分析。
HTTP协议请求走私并不像其余web攻击手法那么直观,而是在更加复杂的网络环境中,因不一样服务器基于不一样的RFC标准实现的针对HTTP协议包的不一样处理方式而产生的一种安全风险。git
在对其漏洞进行分析前,首先须要了解目前被普遍使用的HTTP 1.1协议特性——Keep-Alive、Pipeline技术。github
简单来讲,在HTTP 1.0及其之前版本的协议中,在每次进行交互的时候,C/S两端都须要进行TCP的三次握手连接。而现在的web页面大部分主要仍是由大量静态资源所组成。若是依然按照HTTP 1.0及其之前版本的协议设计,会致使服务器大量的负载被浪费。因而在HTTP 1.1中,增长了Keep-Alive、Pipeline技术。web
根据RFC7230规范中 section-6.3 能够得知,HTTP 1.1中默认使用persistent connections方式。其实现手法是在HTTP通讯包中加入Connection: Keep-Alive标识:在一次HTTP通讯后不会关闭TCP链接,而在后续相同目标服务器请求中复用该空闲的TCP通道,避免了因为新建TCP链接产生的时延和服务器资源消耗,提高用户资源访问速度。apache
而在Keep-Alive中后续又有了Pipeline机制,这样客户端就能够像流水线同样不用等待某个包的响应而持续的向服务器发包。而服务器也会遵循先进先出原则对客户端请求进行响应。segmentfault
如图,咱们能够看到相比于no pipelining模式,pipelining模式下服务器在响应时间上有了很大的提高。后端
现现在,为了提升用户浏览速度、增强服务稳定性、提高使用体验以及减轻网络负担。大部分厂商都会使用CDN加速服务或负载均衡LB等部署业务。当用户访问服务器静态资源时,将直接从CDN上获取详情,当存在真正服务器交互时,才会与后端服务器产生交互。如图所示:缓存
可是,该模式中reverse proxy部分将长期与back-end部分通讯,通常状况下这部分链接会重用TCP通道。通俗来讲,用户流量来自四面八方,user端到reverse proxy端通讯会创建多条TCP通道,而rever proxy与back-end端通讯ip固定,这二者重用TCP链接通道来通讯便瓜熟蒂落了。安全
在这种场景下,当不一样服务器实现时参考的RFC标准不一样时,咱们向reverse proxy发送一个比较模糊的HTTP请求时,由于reverse proxy与back-end基于不一样标准进行解析,可能产生reverse proxy认为该HTTP请求合法,并转发到back-end,而back-end只认为部分HTTP请求合法,剩下的多余请求,便就算是夹带走私的HTTP请求了。当该部分对正经常使用户的请求形成了影响以后,就实现了HTTP走私攻击。如图所示:深色为正常请求,橙色为走私请求,绿色为正经常使用户请求。一块儿发包状况下,走私的请求内容被拼接到正常请求中。
分块传输编码(Chunked transfer encoding) 是超文本传输协议(HTTP)中的一种数据传输机制,容许 HTTP 的数据能够分红多个部分。
以下图所示,为jdcloud.com未进行数据包进行chunked。
当对jdcloud.com进行分块时,以下图所示。
注:后续文章中所提到CL=Content-Length,TE=Transfer-Encoding,如需使用burpsuite进行数据包调试时,需去除Repeater中Update Content-Length选项。
主要指在GET中设置Content-Length长度,使用body发送数据。固然这里也不只仅限制与GET请求中,只是GET的理解比较典型,因此咱们用在作例子。
在 RFC7230 Content-Length 部分提到:
For example, a Content-Length header field is normally sent in a POST request even when the value is 0 (indicating an empty payload body). A user agent SHOULD NOT send a Content-Length header field when the request message does not contain a payload body and the method semantics do not anticipate such a body.
在最新的 RFC7231 4.3.1 GET 中也仅仅提了一句:
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
从官方规范文档能够了解到:RFC规范并未严格的规范Server端处理方式,对该类请求的规范也适当进行了放松,可是也是部分状况。因为这些中间件没有一个严格的标准依据,因此也会产生解析差别致使HTTP Smuggling攻击。
GET / HTTP/1.1rn Host: example.comrn Content-Length: 44rn GET /secret HTTP/1.1rn Host: example.comrn rn
因为GET请求,服务器将不对Content-Length进行处理,同时由于Pipeline的存在,后端服务器会将该数据包视为两个GET请求。分别为:
请求——1
GET / HTTP/1.1rn Host: example.comrn
请求——2
GET /secret HTTP/1.1rn Host: example.comrn
这就致使了请求走私。
在RFC7230的第_3.3.3_节中的第四条中,规定当服务器收到的请求中包含两个Content-Length,并且二者的值不一样时,须要返回400错误。
If a message is received without Transfer-Encoding and with either multiple Content-Length header fields having differing field-values or a single Content-Length header field having an invalid value, then the message framing is invalid and the recipient MUST treat it as an unrecoverable error. If this is a request message, the server MUST respond with a 400 (Bad Request) status code and then close the connection. If this is a response message received by a proxy, the proxy MUST close the connection to the server, discard the received response, and send a 502 (Bad Fielding & Reschke Standards Track [Page 32] RFC 7230 HTTP/1.1 Message Syntax and Routing June 2014 Gateway) response to the client. If this is a response message received by a user agent, the user agent MUST close the connection to the server and discard the received response.
可是某些服务器并没遵循规范进行实现,当服务器未遵循该规范时,先后服务器都不会响应400。可能形成代理服务器使用第一个Content-Length获取长度,然后端按照第二个Content-Length获取长度。
POST / HTTP/1.1rn Host: example.comrn Content-Length: 8rn Content-Length: 7rn 12345rn a
这个时候,当后端服务器接受到数据包时,Content-Length长度为7。实际上接受到的body为12345rn,而咱们前面所提到的,代理服务器会与后端服务器重用TCP通道,这个时候a就会拼接到下一个请求。这个时候若是存在一个用户发起GET请求。则该用户GET请求实际为:
aGET / HTTP/1.1rn Host: example.comrn
同时该用户也会收到一个相似aGET request method not found的报错响应,其实这样就已经实现了一次HTTP协议走私攻击,对正经常使用户形成了影响,并且后续能够扩展成相似于CSRF的攻击方式。
可是两个Content-Length这种请求包仍是太过于理想化了,通常的服务器都不会接受这种存在两个请求头的请求包,可是在RFC2616的第_4.4_节中,规定:
The transfer-length of a message is the length of the message-body as it appears in the message; that is, after any transfer-codings have been applied. When a message-body is included with a message, the transfer-length of that body is determined by one of the following (in order of precedence):If a Transfer-Encoding header field (section 14.41) is present and has any value other than "identity", then the transfer-length is defined by use of the "chunked" transfer-coding (section 3.6), unless the message is terminated by closing the connection.
也就是说,当Content-Length与Transfer-Encoding同时被定义使用时,可忽略Content-Length。也就是说当Transfer-Encoding的加入,两个Content-Length并不影响代理服务器与后端服务器的响应。
这里的状况是指代理服务器处理Content-Length,后端服务器会遵照RFC2616的规定,处理Transfer-Encoding的状况(这里也就是场景2后边所提到的状况)。
POST / HTTP/1.1rn Host: example.comrn User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0rn Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8rn Accept-Language: en-US,en;q=0.5rn Connection: keep-alivern Content-Length: 6rn Transfer-Encoding: chunkedrn rn 0rn rn G
请求——1 (代理服务器的解析)
POST / HTTP/1.1rn Host: example.comrn User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0rn Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8rn Accept-Language: en-US,en;q=0.5rn Connection: keep-alivern Content-Length: 6rn Transfer-Encoding: chunkedrn rn 0rn rn G
请求——2 (代理服务器的解析)
G
其中G被留在缓存区中,当无其余用户请求时候,该数据包会不会产生解析问题,但TCP重用状况,当一个正常请求过来时候。将出现以下状况:
GPOST / HTTP/1.1rn Host: example.comrn ....
这个时候HTTP包,再一次经过TCP通道进行走私。
即代理服务器处理Transfer-Encoding请求,后端服务器处理Content-Length请求。
POST / HTTP/1.1rn Host: example.comrn User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0rn Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8rn Accept-Language: en-US,en;q=0.5rn Content-Length: 4rn Transfer-Encoding: chunkedrn rn 12rn GPOST / HTTP/1.1rn rn 0rn rn
因为Transfer-Encoding遇到0rnrn才结束解析。此时后端将解析Content-Length,真正到达后端数据将为:
POST / HTTP/1.1rn 2Host: example.comrn 3User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0rn 4Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8rn 5Accept-Language: en-US,en;q=0.5rn 6Content-Length: 4rn 7rn 812rn
同时将出现第二个数据包:
GPOST / HTTP/1.1rn rn 0rn rn
当收到存在两个请求头的请求包时,先后端服务器都处理Transfer-Encoding请求头,这确实是实现了RFC的标准。不过先后端服务器毕竟不是同一种,这就有了一种方法,咱们能够对发送的请求包中的Transfer-Encoding进行某种混淆操做(这里主要指Content-Length),从而使其中一个服务器不处理Transfer-Encoding请求头。从某种意义上仍是CL-TE或者TE-CL。
POST / HTTP/1.1rn Host: example.comrn User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0rn Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8rn Content-length: 4rn Transfer-Encoding: chunkedrn Transfer-encoding: cowrn rn 5crn GPOST / HTTP/1.1rn Content-Type: application/x-www-form-urlencodedrn Content-Length: 15rn rn x=1rn 0rn rn
使用PortSwigger的实验环境环境进行实际攻击演示。
*靶场连接:
https://portswigger.net/web-s...
This lab involves a front-end and back-end server, and the front-end server doesn't support chunked encoding. There's an admin panel at /admin, but the front-end server blocks access to it.To solve the lab, smuggle a request to the back-end server that accesses the admin panel and deletes the user carlos.
实验目的:访问admin页,并利用认证对carlos用户进行删除。
*连接:
https://portswigger.net/web-s...
This lab involves a front-end and back-end server, and the front-end server doesn't support chunked encoding.There's an admin panel at /admin, but it's only accessible to people with the IP address 127.0.0.1. The front-end server adds an HTTP header to incoming requests containing their IP address. It's similar to the X-Forwarded-For header but has a different name.
To solve the lab, smuggle a request to the back-end server that reveals the header that is added by the front-end server. Then smuggle a request to the back-end server that includes the added header, accesses the admin panel, and deletes the user carlos.
实验目的同上,不过这里在前端服务器作了限制。不支持chunked。同时前端到后端作了检查,在headers中自定义了一个相似于X-Forwarded-For的头。
*连接:
https://portswigger.net/web-s...
This lab involves a front-end and back-end server, and the front-end server doesn't support chunked encoding.To solve the lab, smuggle a request to the back-end server that causes the next user's request to be stored in the application. Then retrieve the next user's request and use the victim user's cookies to access their account.
实验目的:经过写页面的方式,获取下一个请求数据包中cookie数据。
*连接:
https://portswigger.net/web-s...
This lab involves a front-end and back-end server, and the front-end server doesn't support chunked encoding.The application is also vulnerable to _reflected XSS_ via the User-Agent header.
To solve the lab, smuggle a request to the back-end server that causes the next user's request to receive a response containing an XSS exploit that executes alert(1).
应用场景:当业务存在反射型XSS时,可经过缓存投毒的方式在其余用户页面写入脏数据。
SETP一、 进入任意评论区发现页面存在userAgent回显,经过走私协议修改userAgent便可。
*连接:
https://portswigger.net/web-s...
This lab involves a front-end and back-end server, and the front-end server doesn't support chunked encoding. The front-end server is configured to cache certain responses.To solve the lab, perform a request smuggling attack that causes the cache to be poisoned, such that a subsequent request for a JavaScript file receives a redirection to the exploit server. The poisoned cache should alert document.cookie.
应用场景:劫持下一用户请求页面。(实际场景中可劫持跳转至钓鱼等页面)
从前面的案例咱们能够看到HTTP请求走私的危害性,那么如何防护呢?
可是这些修复方法又存在一些现实困难:
其实否则,上云就是个很好的方案。云主机、CDN、WAF都统一实现编码规范,能够很好地避免该类问题的产生。
* https://media.defcon.org/DEF%...
* https://portswigger.net/resea...
* https://regilero.github.io/en...
* https://paper.seebug.org/1048
* https://tools.ietf.org/html/r...
* http://blog.zeddyu.info/2019/...
* https://tools.ietf.org/html/r...
* https://tools.ietf.org/html/r...
欢迎点击【京东科技】,了解开发者社区
更多精彩技术实践与独家干货解析
欢迎关注【京东科技开发者】公众号