关于http:解密协议层的攻击HTTP请求走私

4次阅读

共计 10470 个字符,预计需要花费 27 分钟才能阅读完成。

最近始终在钻研一些比拟有意思的攻打办法与思路,在查阅本地文档的时候 (没错,本地,我常常会将一些有意思的文章然而没工夫看就会被我保留 pdf 到本地),一篇 2019 年 Black hat 的议题——HTTP 申请走私,进入我的视线,同时我也查阅到在 2020 Blackhat 中该攻打手法再次被剖析。我对此产生浓重学习趣味,于是便有了这篇文章。

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 协定包的不同解决形式而产生的一种平安危险。

在对其破绽进行剖析前,首先须要理解目前被宽泛应用的 HTTP 1.1 协定个性——Keep-Alive、Pipeline 技术。

简略来说,在 HTTP 1.0 及其以前版本的协定中,在每次进行交互的时候,C/ S 两端都须要进行 TCP 的三次握手链接。而现在的 web 页面大部分次要还是由大量动态资源所组成。如果仍然依照 HTTP 1.0 及其以前版本的协定设计,会导致服务器大量的负载被节约。于是在 HTTP 1.1 中,减少了 Keep-Alive、Pipeline 技术。

KEEP-ALIVE

依据 RFC7230 标准中 section-6.3 能够得悉,HTTP 1.1 中默认应用 persistent connections 形式。其实现手法是在 HTTP 通信包中退出 Connection: Keep-Alive 标识:在一次 HTTP 通信后不会敞开 TCP 连贯,而在后续雷同指标服务器申请中复用该闲暇的 TCP 通道,防止了因为新建 TCP 连贯产生的时延和服务器资源耗费,晋升用户资源访问速度。

PIPELINE

而在 Keep-Alive 中后续又有了 Pipeline 机制, 这样客户端就能够像流水线一样不必期待某个包的响应而继续的向服务器发包。而服务器也会遵循先进先出准则对客户端申请进行响应。

如图,咱们能够看到相比于 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 数据包格局

分块传输编码(Chunked transfer encoding) 是超文本传输协定(HTTP)中的一种数据传输机制,容许 HTTP 的数据能够分成多个局部。

如下图所示,为 jdcloud.com 未进行数据包进行 chunked。

当对 jdcloud.com 进行分块时,如下图所示。

常见攻打

注:后续文章中所提到 CL=Content-Length,TE=Transfer-Encoding,如需应用 burpsuite 进行数据包调试时,需去除 Repeater 中 Update Content-Length 选项。

场景 1:GET 申请中 CL 不为 0 状况

次要指在 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

这就导致了申请走私。

场景 2:CL-CL

在 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 并不影响代理服务器与后端服务器的响应。

场景 3:CL-TE

这里的状况是指代理服务器解决 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 通道进行走私。

场景 4:TE-CL

即代理服务器解决 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 的试验环境环境进行理论攻打演示。

复用 TCP 进行管理员操作

* 靶场链接:

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 用户进行删除。

  • SETP 1、因间接拜访 /admin 目录被提醒拦挡,同时题目提醒 CL.TE。这里通过结构 CL.TE 格局数据包,尝试拜访。/admin 路由。

  • SETP 2、拜访提醒管理员界面只容许为本地用户拜访,尝试间接拜访 localhost, 并获取到删除用户路由地址。

  • SETP 3、通过结构申请拜访即可,最终再次拜访 /admin 显示页面曾经没有删除 carlos 用户选项。

联合业务获取用户申请包 -1

* 链接:

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 的头。

  • SETP1、通过页面 search 处间接结构走私数据包,在页面返回两头服务器到后端服务器数据包内容 (走私数据包长度以后为 200,若理论场景中显示不全则可通过减少 CL 长度解决),获取到 X -uNiqsg-Ip 头。同时, 这里之所以抉择 search 处,次要是因为该处在页面存在输入。

  • SETP2、通过伪造同样申请发包即可。

联合业务获取用户申请包 -

* 链接:

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 数据。

  • SETP1、发现 post?postId= 路由下存在写页面操作,通过批改数据包。

  • SETP2、拜访以后页面查看 website 处即可获取到下一个申请包的数据。(这里同样也能够管制下一个申请包数据在评论区,只需将最初一个评论参数 comment 放至最初即可)

反射 XSS

* 链接:

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 时,可通过缓存投毒的形式在其余用户页面写入脏数据。

SETP1、进入任意评论区发现页面存在 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.

利用场景:劫持下一用户申请页面。(理论场景中可劫持跳转至钓鱼等页面)

  • SETP1、缓存注入批改 Host 为歹意申请。

对于进攻

从后面的案例咱们能够看到 HTTP 申请走私的危害性,那么如何进攻呢?

  • 禁用代理服务器与后端服务器之间的 TCP 连贯重用。
  • 应用 HTTP/ 2 协定。
  • 前后端应用雷同的服务器。

然而这些修复办法又存在一些事实艰难:

  • HTTP/ 2 推广过于艰难,只管 HTTP/ 2 兼容 HTTP/1.1。
  • 勾销 TCP 重用将增大服务器负载,服务器资源吃不消。
  • 应用雷同的服务器,在一些厂商其实也很难实现。其次要起因还是前后端实现规范不统一的问题。

那么没有解决方案了嘛?

其实不然,上云就是个很好的计划。云主机、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…

举荐浏览

  • 如何预防勒索攻打事件?这份平安自查指南请查收
  • 探秘密码学:深刻理解对称加密与密钥协商技术
  • 京东千亿订单背地的纵深平安进攻体系

欢送点击【 京东科技 】,理解开发者社区

更多精彩技术实际与独家干货解析

欢送关注【京东科技开发者】公众号

正文完
 0