HTTP Request Smuggling 請求走私

參考文章

淺析HTTP走私攻擊
SeeBug-協議層的攻擊——HTTP請求走私
HTTP 走私漏洞分析
HTTP-Request-Smuggling

簡單介紹

攻擊者通過構造特殊結構的請求,干擾網站服務器對請求的處理,從而實現攻擊目標

前提知識

注:以下文章中的前端指的是(代理服務器、CDN、WAF,負載均衡,Nginx,HAproxy等)

Persistent Connection:持久連接,Connection: keep-alive
比如打開一個網頁,我們可以在瀏覽器控制端看到瀏覽器發送了許多請求(HTML、圖片、css、js),而我們知道每一次發送HTTP請求需要經過 TCP 三次握手,發送完畢又有四次揮手。當單個用戶同時需要發送多個請求時,這一點消耗或許微不足道,但當有許多用戶同時發起請求的時候,便會給服務器造成很多不必要的消耗。為了解決這一問題,在 HTTP 協議中便新加了 Connection: keep-alive 這一個請求頭,當有些請求帶着 Connection: close 的話,通信完成之後,服務器才會中斷 TCP 連接。如此便解決了額外消耗的問題,但是服務器端處理請求的方式仍舊是請求一次響應一次,然後再處理下一個請求,當一個請求發生阻塞時,便會影響後續所有請求,為此 Pipelining 異步技術解決了這一個問題

Pipelining:能一次處理多個請求,客戶端不必等到上一個請求的響應后再發送下一個請求。服務器那邊一次可以接收多個請求,需要遵循先入先出機制,將請求和響應嚴格對應起來,再將響應發送給客戶端

但是這樣也會帶來一個問題————如何區分每一個請求才不會導致混淆————前端與後端必須短時間內對每個數據包的邊界大小達成一致。否則,攻擊者就可以構造發送一個特殊的數據包發起攻擊。那麼如何界定數據包邊界呢?
有兩種方式: Content-LengthTransfer-Encoding.

Content-Length:CL,請求體或者響應體長度(十進制)。字符算一個,CRLF(一個換行)算兩個。通常如果 Content-Length 的值比實際長度小,會造成內容被截斷;如果比實體內容大,會造成 pending,也就是等待直到超時。

Transfer-Encoding:TE,其只有一個值 chunked (分塊編碼)。分塊編碼相當簡單,在頭部加入 Transfer-Encoding: chunked 之後,就代表這個報文採用了分塊編碼。這時,報文中的實體需要改為用一系列分塊來傳輸。每個分塊包含十六進制的長度值和數據,長度值獨佔一行,長度不包括它結尾的 CRLF(\r\n),也不包括分塊數據結尾的 CRLF,但是包括分塊中的換行,值算2。最後一個分塊長度值必須為 0,對應的分塊數據沒有內容,表示實體結束。
例如:

POST /langdetect HTTP/1.1
Host: fanyi.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:77.0) Gecko/20100101 Firefox/77.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 93
Transfer-Encoding: chunked

2;逗號後面是註釋
qu
3;3表示後面的字符長度為3(十六進制),不算CRLF(\r\n回車換行)
ery
1
=
2
ja
2
ck
0;0表示實體結束


注:根據 RFC 標準,如果接收到的消息同時具有傳輸編碼標頭字段和內容長度標頭字段,則必須忽略內容長度標頭字段,當然也有不遵循標準的例外。

根據標準,當接受到如 Transfer-Encoding: chunked, error 有多個值或者不識別的值時的時候,應該返回 400 錯誤。但是有一些方法可以繞過
(導致既不返回400錯誤,又可以使 Transfer-Encoding 標頭失效):

Transfer-Encoding: xchunked

Transfer-Encoding : chunked

Transfer-Encoding: chunked

Transfer-Encoding: x

Transfer-Encoding:[tab]chunked

GET / HTTP/1.1
 Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked

Transfer-Encoding
 : chunked

產生原因

HTTP規範提供了兩種不同方式來指定請求的結束位置,它們是 Content-Length 標頭和 Transfer-Encoding 標頭。當前/後端對數據包邊界的校驗不一致時,
使得後端將一個惡意的殘缺請求需要和下一個正常的請求進行拼接,從而吞併了其他用戶的正常請求。如圖:

那麼前/後端校驗不一致有那些情況呢呢呢呢?

類型

  1. CL-TE: 前端: Content-Length,後端: Transfer-Encoding

BURP實驗環境

第一次請求:

第二次請求:

原理:前端服務器通過 Content-Length 界定數據包邊界,檢測到數據包無異常通過,然後傳輸到後端服務器,後端服務器通過 Transfer-Encoding 界定數據包邊界,導致 R0oKi3 字段被識別為下一個數據包的內容,而被送到了緩衝區,由於內容不完整,會等待後續數據,當正常用戶的請求傳輸到後端時,與之前滯留的惡意數據進行了拼接,組成了 R0OKI3POST ,為不可識別的請求方式,導致403。

  1. TE-CL: 前端: Transfer-Encoding,後端: Content-Length

BURP實驗環境
記得關 burp 的 Update Content-Length 功能

第一次請求:

第二次請求:

原理:跟 CL-TE 相似

  1. TE-TE: 前端: Transfer-Encoding,後端: Transfer-Encoding

BURP實驗環境
記得關 burp 的 Update Content-Length 功能

第一次請求:

第二次請求:

原理:前端服務器通過第一個 Transfer-Encoding 界定數據包邊界,檢測到數據包無異常通過,然後傳輸到後端服務器,後端服務器通過第二個 Transfer-Encoding 界定數據包邊界,結果為一個不可識別的標頭,然後便退而求其次使用 Content-Length 校驗,結果就跟 TE-CL 形式無異了。同樣若是前端服務器校驗第二個,後端服務器校驗第一個,那結果也就跟 CL-TE 形式無異了。

  1. CL-CL: 前端: Content-Length,後端: Content-Length

在RFC7230規範中,規定當服務器收到的請求中包含兩個 Content-Length,而且兩者的值不同時,需要返回400錯誤。但難免會有服務器不嚴格遵守該規範。假設前端和後端服務器都收到該類請求,且不報錯,其中前端服務器按照第一個Content-Length的值對請求進行為數據包定界,而後端服務器則按照第二個Content-Length的值進行處理。

這時攻擊者可以惡意構造一個特殊的請求:

POST / HTTP/1.1
Host: example.com
Content-Length: 11
Content-Length: 5

123
R0oKi3

原理:前端服務器獲取到的數據包的長度11,由此界定數據包邊界,檢測到數據包無異常通過,然後傳輸到後端,而後端服務器獲取到的數據包長度為5。當讀取完前5個字符后,後端服務器認為該請求已經讀取完畢。便去識別下一個數據包,而此時的緩衝區中還剩下 R0oKi3,它被認為是下一個請求的一部分,由於內容不完整,會等待後續數據,當正常用戶的請求傳輸到後端時,與之前滯留的惡意數據進行了拼接,攻擊便在此展開。

  1. CL 不為 0 的 GET 請求:

假設前端服務器允許 GET 請求攜帶請求體,而後端服務器不允許 GET 請求攜帶請求體,它會直接忽略掉 GET 請求中的 Content-Length 頭,不進行處理。這就有可能導致請求走私。
比如發送下面請求:

GET / HTTP/1.1
Host: example.com
Content-Length: 72

POST /comment HTTP/1.1
Host: example.com
Content-Length:666

msg=aaa

前端服務器通過讀取Content-Length,確認這是個完整的請求,然後轉發到後端服務器,而後端服務器因為不對 Content-Length 進行判斷,於是在後端服務器中該請求就變成了兩個:
第一個:

GET / HTTP/1.1
Host: example.com
Content-Length: 72

第二個:

POST /comment HTTP/1.1
Host: example.com
Content-Length:666

msg=aaa

而第二個為 POST 請求,假定其為發表評論的數據包,再假定後端服務器是依靠 Content-Length 來界定數據包的,那麼由於數據包長度為 666,那麼便會等待其他數據,等到正常用戶的請求包到來,便會與其拼接,變成 msg=aaa……………… ,然後會將显示在評論頁面,也就會導致用戶的 Cookie 等信息的泄露。

PortSwigger 其他實驗

  1. 使用 CL-TE 繞過前端服務器安全控制

BURP實驗環境
坑點:有時候實體數據里需要添加一些別的字段或者空行,不然會出一些很奇怪的錯誤,所以我在弄的時候參照了seebug 404Team
實驗要求:獲取 admin 身份並刪除 carlos 用戶

第一步:實驗提示我們 admin 管理面版在 /admin 目錄下,直接訪問,显示:

第二步:利用 CL-TE 請求走私繞過前端服務器安全控制

  • 第一次發包

坑點:數據實體一定要多一些其他字段或者多兩行空白,不然報 Invalid request 請求不合法

0

GET /admin HTTP/1.1


# 若是多了兩行空白,那麼 foo: bar 字段可以不要

提示 admin 要從 localhost 登陸

  • 改包后多發幾次得到

  • 改包刪除用戶

  • 再次請求 /admin 頁面,發現 carlos 用戶已不存在

    坑點:這裏再次請求的時候記得多加兩個空行改變一下 Content-Length 的值,不然會显示不出來,神奇 BUG?

原理:網站進行身份驗證的處理是在前端服務器,當直接訪問 /admin 目錄時,由於通過不了前端驗證,所以會返回 Blocked。利用請求走私,便可以繞過前端驗證,直接在後端產生一個訪問 /admin 目錄的請求包,當發起下一個請求時,響應的數據包對應的是走私的請求包,如此便可以查看 admin 面板的頁面數據,從而達到繞過前端身份驗證刪除用戶的目的。

  1. 使用 TE-CL 繞過前端服務器安全控制

BURP實驗環境

實驗過程與上一個實驗相仿,不過要記得關 burp 的 Update Content-Length

這裏:不知道為什麼一定要加 Content-Length 和其他的一些詞,不加的話會显示 Invalid request 請求不合法 ?????????

  1. 獲取前端服務器重寫請求字段(CL-TE)

BURP實驗環境

摘自seebug 404Team
在有的網絡環境下,前端代理服務器在收到請求后,不會直接轉發給後端服務器,而是先添加一些必要的字段,然後再轉發給後端服務器。這些字段是後端服務器對請求進行處理所必須的,比如:

  • 描述TLS連接所使用的協議和密碼

  • 包含用戶IP地址的XFF頭

  • 用戶的會話令牌ID
    總之,如果不能獲取到代理服務器添加或者重寫的字段,我們走私過去的請求就不能被後端服務器進行正確的處理。那麼我們該如何獲取這些值呢。PortSwigger提供了一個很簡單的方法,主要是三大步驟:

  • 找一個能夠將請求參數的值輸出到響應中的POST請求

  • 把該POST請求中,找到的這個特殊的參數放在消息的最後面

  • 然後走私這一個請求,然後直接發送一個普通的請求,前端服務器對這個請求重寫的一些字段就會显示出來。

  • 第一步:找一個能夠將請求參數的值輸出到響應中的POST請求

  • 第二步:利用 CL-TE 走私截獲正常數據包經前端服務器修改后發送過來的內容,並輸出在響應包中

這一步的原理:由於我們走私構造的請求包為:

POST / HTTP/1.1
Content-Length: 100

search=66666

從這裏可以看到,Content-Length 的值為 100,而我們的實體數據僅為 search=66666,遠沒有 100,於是後端服務器便會進入等待狀態,當下一個正常請求到來時,會與之前滯留的請求進行拼接,從而導致走私的請求包吞併了下一個請求的部分或全部內容,並返回走私請求的響應。

  • 第三步:在走私的請求上添加這個字段,然後走私一個刪除用戶的請求。

  • 查看 /admin 頁面,發現用戶已被刪除

能用來干什麼

  1. 賬戶劫持 CL-TE
    BURP實驗環境
  • 構造特殊請求包,形成一個走私請求

  • 查看評論

原理:(跟 獲取前端服務器重寫請求字段 相似)
我們走私構造的請求包為:

POST /post/comment HTTP/1.1
Host: aca41ff41e89d28f800d3e82001a00c8.web-security-academy.net
Content-Length: 900
Cookie: session=XPbI3LJQJCoBcQOvsLdfyCNbOKqsGudy

csrf=Nk6OsCxcNIUdfnrpQuy9N3WO0zLLcAWU&postId=4&name=aaa&email=aaa%40aaa.com&website=&comment=aaaa


可以看到 Content-Length 值為 900,而我們的實體數據僅為 csrf=Nk6OsCxcNIUdfnrpQuy9N3WO0zLLcAWU&postId=4&name=aaa&email=aaa%40aaa.com&website=&comment=aaaa,遠不足900,於是後端服務器便會進入等待狀態,當下一個正常請求到來時,會與之前滯留的請求進行拼接,從而導致走私的請求包吞併了下一個請求的部分或全部內容,並且由於是構造發起評論的請求包,所以數據會存入數據庫,從而打開頁面便會看到其他用戶的請求包內容,獲取其敏感數據,由於環境只有我一個人在玩,所以只能獲取到自己的敏感數據。

注意:一定要將 comment=aaaa 放在最後

  1. Reflected XSS + Smuggling 造成無需交互的 XSS(CL-TE)
    BURP實驗環境
  • 首先反射型 XSS 在文章頁面

  • 構造請求走私 payload

  • 導致無交互 XSS

  1. 惡意重定向
    環境暫無

許多應用程序執行從一個 URL 到另一個URL的重定向,會將來自請求的 Host 標頭的主機名放入重定向URL。一個示例是 Apache 和 IIS Web 服務器的默認行為,在該行為中,對不帶斜杠的文件夾的請求將收到對包含該斜杠的文件夾的重定向:

請求
GET /home HTTP/1.1
Host: normal-website.com

響應
HTTP/1.1 301 Moved Permanently
Location: https://normal-website.com/home/

通常,此行為被認為是無害的,但是可以在走私請求攻擊中利用它來將其他用戶重定向到外部域。例如:

 POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 54
Transfer-Encoding: chunked

0

GET /home HTTP/1.1
Host: attacker-website.com
Foo: X 

走私的請求將觸發重定向到攻擊者的網站,這將影響後端服務器處理的下一個用戶的請求。例如:

正常請求
GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com

惡意響應
HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/ 

若用戶請求的是一個 JavaScript 文件,該文件是由網站上的頁面導入的。攻擊者可以通過在響應中返回自己的 JavaScript 文件來完全破壞受害者用戶。

4.緩存投毒

一般來說,前端服務器出於性能原因,會對後端服務器的一些資源進行緩存,如果存在HTTP請求走私漏洞,則有可能使用重定向來進行緩存投毒,從而影響後續訪問的所有用戶。

BURP實驗環境

實驗參考

檢測

檢測請求走私漏洞的明顯方法是發出一個模糊的請求,然後發出正常的“受害者”請求,然後觀察後者是否得到意外的響應。但是,這極易受到干擾。

如果另一個用戶的請求在我們的受害者請求之前命中,他們將得到損壞的響應,我們將不會發現該漏洞。這意味着在具有大量流量的實時站點上,很難證明請求走私存在而不會在此過程中影響眾多真正的用戶。即使在沒有其他流量的站點上,您也可能會因應用程序級別的怪癖終止連接而導致漏報。

為了解決這個問題,作者開發了一種檢測策略,該策略使用一系列消息,這些消息使易受攻擊的後端系統掛起並使連接超時。這種技術幾乎沒有誤報,抵制應用程序級別的怪癖,最重要的是幾乎沒有影響其他用戶的風險。

假設前端服務器使用Content-Length頭,後端使用Transfer-Encoding頭。我將此定位稱為CL.TE。我們可以通過發送以下請求來檢測潛在的請求走私:

POST / HTTP/1.1
Host: example.com
Content-Length: 4
Transfer-Encoding: chunked

1
R
x

由於較短的Content-Length,前端將僅轉發到 R 丟棄後續的 X,而後端將在等待下一個塊大小時超時。這將導致明顯的時間延遲。
如果超時說明兩個服務器為CL.TE,正常響應就是CL.CL,被拒絕就可能是TE.TE或者TE.CL,那麼只需要在拒絕的時候,再使用第二個請求,TE.TE就會正常響應,TE.CL就會超時。

如果兩個服務器同步(TE.TE或CL.CL),請求將被前端拒絕或由兩個系統無害地處理。最後,如果以相反的方式發生(TE.CL),前端將拒絕該消息,而不會將其轉發到後端,這要歸功於無效的塊大小“Q”。這可以防止後端中毒。

我們可以使用以下請求安全地檢測TE.CL:

POST / HTTP/1.1
Host: example.com
Content-Length: 6
Transfer-Encoding: chunked

0


X

如果以相反的方式發生(CL.TE),則此方法將使用X毒化後端套接字,可能會損害合法用戶。幸運的是,通過首先運行先前的檢測方法,我們可以排除這種可能性。

這些請求可以適應目標解析中的任意差異,並且它們用於通過HTTP Request Smuggler自動識別請求走私漏洞。HTTP Request Smuggler是為幫助此類攻擊而開發的開源Burp Suite擴展。它們現在也被用在Burp Suite的核心掃描儀中。雖然這是服務器級漏洞,但單個域上的不同接口通常會路由到不同的目標,因此應將此技術單獨應用於每個接口。

修復

  1. 禁用後端連接的重用,以便每個後端請求通過單獨的網絡連接發送。
  2. 使用HTTP / 2進行後端連接,因為此協議可防止對請求之間的邊界產生歧義。
  3. 前端服務器和後端服務器使用完全相同的Web服務器軟件,以便它們就請求之間的界限達成一致。
    以上的措施有的不能從根本上解決問題,而且有着很多不足,就比如禁用代理服務器和後端服務器之間的 TCP 連接重用,會增大後端服務器的壓力。使用 HTTP/2 在現在的網絡條件下根本無法推廣使用,哪怕支持 HTTP/2 協議的服務器也會兼容 HTTP/1.1。從本質上來說,HTTP 請求走私出現的原因並不是協議設計的問題,而是不同服務器實現的問題,個人認為最好的解決方案就是嚴格的實現 RFC7230-7235 中所規定的的標準,但這也是最難做到的。

HTTP 參數污染也能算是一種請求走私 HTTP參數污染

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?