解密協議層的攻擊——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 2 Host: example.com\r\n 3 Content-Length: 44\r\n 4 5 GET /secret HTTP/1.1\r\n 6 Host: example.com\r\n 7 \r\n
由於GET請求,伺服器將不對Content-Length進行處理,同時因為Pipeline的存在,後端伺服器會將該資料包視為兩個GET請求。分別為:
請求——1
1 GET / HTTP/1.1\r\n 2 Host: example.com\r\n
請求——2
1 GET /secret HTTP/1.1\r\n 2 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獲取長度。
構造資料包
1 POST / HTTP/1.1\r\n 2 Host: example.com\r\n 3 Content-Length: 8\r\n 4 Content-Length: 7\r\n 5 6 12345\r\n 7 a
這個時候,當後端伺服器接受到資料包時,Content-Length長度為7。實際上接受到的body為12345\r\n,而我們前面所提到的,代理伺服器會與後端伺服器重用TCP通道,這個時候a就會拼接到下一個請求。這個時候如果存在一個使用者發起GET請求。則該使用者GET請求實際為:
1 aGET / HTTP/1.1\r\n 2 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後邊所提到的情況)。
1 POST / HTTP/1.1\r\n 2 Host: example.com\r\n 3 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n 4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n 5 Accept-Language: en-US,en;q=0.5\r\n 6 Connection: keep-alive\r\n 7 Content-Length: 6\r\n 8 Transfer-Encoding: chunked\r\n 9 \r\n 10 0\r\n 11 \r\n 12 G
因前後伺服器規範不同,解析如下:
請求——1 (代理伺服器的解析)
1 POST / HTTP/1.1\r\n 2 Host: example.com\r\n 3 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n 4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n 5 Accept-Language: en-US,en;q=0.5\r\n 6 Connection: keep-alive\r\n 7 Content-Length: 6\r\n 8 Transfer-Encoding: chunked\r\n 9 \r\n 10 0\r\n 11 \r\n 12 G
請求——2 (代理伺服器的解析)
G
其中G被留在快取區中,當無其他使用者請求時候,該資料包會不會產生解析問題,但TCP重用情況,當一個正常請求過來時候。將出現如下情況:
1 GPOST / HTTP/1.1\r\n 2 Host: example.com\r\n 3 ....
這個時候HTTP包,再一次透過TCP通道進行走私。
場景4:TE-CL
即代理伺服器處理Transfer-Encoding請求,後端伺服器處理Content-Length請求。
-
構造資料包
1 POST / HTTP/1.1\r\n 2 Host: example.com\r\n 3 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n 4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n 5 Accept-Language: en-US,en;q=0.5\r\n 6 Content-Length: 4\r\n 7 Transfer-Encoding: chunked\r\n 8 \r\n 9 12\r\n 10 GPOST / HTTP/1.1\r\n 11 \r\n 12 0\r\n 13 \r\n
由於Transfer-Encoding遇到0\r\n\r\n才結束解析。此時後端將解析Content-Length,真正到達後端資料將為:
Plain Text
1 POST / HTTP/1.1\r\n 2 Host: example.com\r\n 3 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n 4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n 5 Accept-Language: en-US,en;q=0.5\r\n 6 Content-Length: 4\r\n 7 \r\n 8 12\r\n
同時將出現第二個資料包:
1 GPOST / HTTP/1.1\r\n 2 \r\n 3 0\r\n 4 \r\n
當收到存在兩個請求頭的請求包時,前後端伺服器都處理Transfer-Encoding請求頭,這確實是實現了RFC的標準。不過前後端伺服器畢竟不是同一種,這就有了一種方法,我們可以對傳送的請求包中的Transfer-Encoding進行某種混淆操作(這裡主要指Content-Length),從而使其中一個伺服器不處理Transfer-Encoding請求頭。從某種意義上還是CL-TE或者TE-CL。
-
構造資料包
1 POST / HTTP/1.1\r\n 2 Host: example.com\r\n 3 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0\r\n 4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n 5 Content-length: 4\r\n 6 Transfer-Encoding: chunked\r\n 7 Transfer-encoding: cow\r\n 8 \r\n 9 5c\r\n 10 GPOST / HTTP/1.1\r\n 11 Content-Type: application/x-www-form-urlencoded\r\n 12 Content-Length: 15\r\n 13 \r\n 14 x=1\r\n 15 0\r\n 16 \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都統一實現編碼規範,可以很好地避免該類問題的產生。
參考連結
*%20CON%2024/DEF%20CON%2024%20presentations/DEF%20CON%2024%20-%20Regilero-Hiding-Wookiees-In-Http.pdf
*
*https://regilero.github.io/english/security/2019/10/17/securityapachetrafficserverhttp_smuggling/
*
*
*http://blog.zeddyu.info/2019/12/05/HTTP-Smuggling/
*
*
推薦閱讀
歡迎點選【 京東科技 】,瞭解開發者社群
更多精彩技術實踐與獨家乾貨解析
歡迎關注【京東科技開發者】公眾號
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912185/viewspace-2757634/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 協議層的攻擊:HTTP請求走私協議HTTP
- 分析HTTP請求以降低HTTP走私攻擊HTTP資料接收不同步攻擊的風險HTTP
- 淺析HTTP走私攻擊HTTP
- HTTP協議如何發起請求HTTP協議
- HTTP 協議六種請求方法HTTP協議
- 網路層協議及ARP攻擊協議
- HTTP協議的請求與資料抓包HTTP協議
- CTFHub web前置技能HTTP協議請求方式WebHTTP協議
- 通過企業服務治理中心呼叫第三方介面差點造成http請求走私攻擊HTTP
- 【技術向】HTTP/1與HTTP/2(H2C)走私攻擊HTTP
- Python 之requests封裝通用http協議介面請求Python封裝HTTP協議
- Http協議什麼時候發生options請求?HTTP協議
- TCP/IP協議的SYN攻擊TCP協議
- 前端必知必會HTTP請求系列(二)簡單一點的HTTP協議前端HTTP協議
- ARP協議介紹與ARP協議的攻擊手法協議
- 08 CSRF偽造請求攻擊
- HTTP協議請求工作流程是什麼?linux學習HTTP協議Linux
- 層層剖析一次 HTTP POST 請求事故HTTP
- Swift:面向協議的網路請求Swift協議
- 每秒3.98億次請求,HTTP/2漏洞導致創紀錄的DDoS攻擊HTTP
- http協議請求方法有哪些?網路安全技術入門HTTP協議
- SQL Server儲存過程模擬HTTP請求POST和GET協議SQLServer儲存過程HTTP協議
- 請求協議中的content-type頭協議
- Laravel 底層是如何處理HTTP請求LaravelHTTP
- 常用物聯網應用層協議(1)——先說HTTP協議協議HTTP
- (轉)解密 Golang 的 Request 物件:深入理解 HTTP 請求的關鍵解密Golang物件HTTP
- 解密HTTP協議:探索其組成部分與工作原理解密HTTP協議
- 從TCP協議的原理論rst復位攻擊TCP協議
- 02 前端HTTP協議(圖解HTTP) 之 簡單的HTTP協議前端HTTP協議圖解
- http協議HTTP協議
- HTTP 協議HTTP協議
- http請求概述HTTP
- Jsoup http請求JSHTTP
- go http請求GoHTTP
- 常見RDP協議攻擊方法 防禦措施協議
- 怎麼解決網站被DDOS攻擊 利用7層協議進行防禦網站協議
- HTTP的請求過程HTTP
- golang 的 http 請求池GolangHTTP