瀏覽器驅動的異步攻擊:HTTP 請求走私(上)
示例探究通過自動檢測CSD 漏洞,我確定了一系列真正的易受攻擊的網站。在這一節中,我將研究其中四個更有趣的方法,並看看這種方法是如何發揮作用的。
Akamai——堆棧化HEAD在第一個案例中,我們將利用一個簡單的漏洞影響許多基於Akamai 構建的網站。作為示例目標,我將使用www.capitalone.ca。
當Akamai 發出重定向時,它會忽略請求的Content-Length 標頭,並將任何消息正文留在TCP/TLS 套接字上。 Capitalone.ca 使用Akamai 將對/assets 的請求重定向到/assets/,因此我們可以通過向該端點發出POST 請求來觸發CSD:
為了構建一個漏洞利用,我們將使用HEAD 方法將一組HTTP 標頭與text/html 的Content-Type 和一個由反映Location 標頭中的查詢字符串的標頭組成的'body'組合起來:
如果這是服務器端異步攻擊,我們可以在就此打住。然而,要想成功實現客戶端異步,我們需要解決兩個複雜問題。
第一個問題是初始重定向響應。為了執行注入的JavaScript,我們需要受害者的瀏覽器將響應呈現為HTML,但是301 重定向會被瀏覽器自動跟踪,從而阻止攻擊。一個簡單的解決方案是指定模式:'cors',它會故意觸發CORS 漏洞。這可以防止瀏覽器跟隨重定向並使我們能夠通過調用catch() 而不是then() 來恢復攻擊序列。在catch block中,我們將使用location='https://www.capitalone.ca/' 觸發瀏覽器導航。我們可能傾向於使用iframe來進行導航,但可以使用跨網站攻擊緩解措施,例如同網站cookie。
第二個複雜的問題是所謂的“堆棧響應問題”。瀏覽器有一種機制,如果接收到的響應數據多於預期,則刪除連接。這極大地影響了對多個響應進行排隊的技術的可靠性,例如我們在這裡使用的HEAD方法。為了解決這個問題,我們需要延遲對HEAD 請求的404 響應。幸運的是,在這個目標上,我們可以很容易地實現這一點,方法是添加一個具有隨機值的參數來充當緩存攻擊器,觸發緩存未命中並產生約500 毫秒的延遲。利用結果如下所示:
已向Akamai 報告了此漏洞,但我不確定何時修復。
Cisco Web VPN——客戶端緩存攻擊我們的下一個目標是Cisco ASA WebVPN,它可以忽略幾乎所有端點上的Content-Length,因此我們只需向主頁發出POST 請求即可觸發異步。為了利用它,我們將使用Host-header 重定向小工具:
最簡單的攻擊是使用此重定向感染套接字,將受害者導航到/+CSCOE+/logon.html,並希望瀏覽器嘗試使用感染的套接字導入/+CSCOE+/win.js,然後被重定向,最終從我們的網站導入惡意JS。不幸的是,這是非常不可靠的,因為瀏覽器很可能會使用被感染的套接字進行初始導航。為了避免這個問題,我們將執行客戶端緩存攻擊。
首先,我們使用重定向感染套接字,然後將瀏覽器直接導航到/+CSCOE+/win.js:
請注意,此頂級導航對於繞過緩存分區至關重要- 嘗試使用fetch() 將攻擊漏洞的緩存。
瀏覽器將使用被感染的套接字,接收惡意重定向,並將其保存在本地緩存中以供https:/redacted/+CSCOE+/win.js 使用。然後,它將遵循重定向並返回我們的網站https://psres.net/+webvpn+/index.html。我們將瀏覽器重定向到登錄頁面https://redacted/+CSCOE+/logon.html,當瀏覽器開始出現登錄頁面時,它會嘗試導入/+CSCOE+/win.js 並發現它已經將其保存在其緩存中。資源加載將遵循緩存的重定向並向https://psres.net/+webvpn+/index.html 發出第二個請求。此時,我們的服務器可以使用一些惡意JavaScript 進行響應,這些JavaScript 將在目標網站的上下文中執行。
為了使這種攻擊起作用,攻擊者的網站需要在同一端點上同時提供重定向和惡意JS。我採取了一種懶惰的方法,並使用JS/HTML 多語言解決了這個問題——Chrome 似乎並不介意不正確的Content-Type:
我在2021 年11 月10 日向思科報告了這個問題,他們最終在2022 年3 月2 日宣布棄用該產品,他們不會修復它,但仍會為其標記為CVE-2022-20713。
Verisign——碎片化的chunk在尋找不同步矢量時,最好不要超出探測有效端點的範圍,而是鼓勵服務器使用不同尋常的代碼路徑。在嘗試使用像/.%2f 這樣的半格式漏洞的URL 時,我發現我可以通過POST 到/%2f 來觸發verisign.com 上的CSD。
我最初嘗試使用基於HEAD 的方法,類似於之前在Akamai 上使用的方法。不幸的是,這種方法依賴於基於Content-Length 的響應,並且服務器向所有沒有正文的請求發送分塊響應。此外,它拒絕了包含Content-Length 的HEAD 請求。最終,經過測試,我發現服務器會對HEAD 請求發出基於CL 的響應,前提是它們使用了Transfer-Encoding: chunked。
這在服務器端異步中幾乎沒有用,但是由於受害者的瀏覽器在我的控制之下,我可以準確地預測下一個請求的大小,並在單個chunk中使用它:
此攻擊是使用以下JavaScript 觸發的:
此漏洞於2022 年7 月21 日被成功修復。
Pulse SecureVPNPulse Secure VPN會忽略對靜態文件(如/robots.txt)的POST 請求的Content-Length。就像Cisco Web VPN 一樣,這個目標有一個主機標頭重定向小工具,我將使用它來劫持JavaScript 導入。但是,這次的重定向是不可緩存的。因此客戶端緩存攻擊是不可用的。
由於我們的目標是資源負載並且沒有攻擊客戶端緩存的預期,因此攻擊時機至關重要。我們需要受害者的瀏覽器成功加載目標網站上的頁面,然後使用攻擊連接加載JavaScript 子資源。
固有的競爭條件使這種攻擊不可靠,所以如果我們只有一次嘗試,它注定會失敗——我們需要設計一個環境,可以進行多次嘗試。為了實現這一點,我將創建一個單獨的窗口,並在攻擊者頁面上保留一個句柄。
在大多數目標頁面上,如果試圖劫持JS導入失敗,將導致瀏覽器緩存真正的JavaScript文件,在緩存的JS過期之前,該頁面不會受到此類攻擊。我能夠通過定位/dana-na/meeting/meeting_testjs.cgi來避免這個問題,它從/dana-na/meeting/url_meeting/appletRedirect.js加載JavaScript,這實際上並不存在,所以它返回一個404,並沒有保存在瀏覽器的緩存中。我還用一個冗長的標頭填充了注入的請求,以緩解堆棧響應漏洞。
這導致以下攻擊流程:
1.打開一個新窗口。
2.向目標發出無害的請求以建立新的連接,從而使計時更加一致。
3.將窗口導航到位於/meeting_testjs.cgi 的目標頁面。
4.120 毫秒後,使用重定向小工具創建三個攻擊連接。
5.5 毫秒後,在渲染/meeting_testjs.cgi 時,受害者可能會嘗試導入/appletRedirect.js 並被重定向到提供惡意JS的x.psres.net。
6.如果沒有,請重試攻擊。
最終的攻擊腳本如下:
基於暫停的異步如上所述,在HTTP 請求中間暫停並觀察服務器的反應可以揭示通過篡改請求的實際內容無法獲得的有用信息。事實證明,暫停還可以通過觸發漏洞的請求超時實現來創建新的異步漏洞。
這個漏洞類是不可見的,除非你的工具有比目標服務器更高的超時時間。我非常幸運地發現了它,因為我的工具應該有2秒的超時,但由於一個漏洞,它恢復到10秒的超時。我的管道還碰巧包含一個運行Varnish的單獨網站,該網站配置了自定義的5 秒超時。
VarnishVarnish 緩存有一個稱為synth() 的特性,它可以讓你在不將請求轉發到後端的情況下發出響應。下面是一個用來阻止訪問文件夾的規則示例:
當處理匹配synth規則的部分請求時,如果Varnish在15秒內沒有收到數據,它將超時。當這種情況發生時,即使它只從套接字讀取了一半的請求,它也會讓連接保持打開狀態以便重用。這意味著,如果客戶機繼續處理HTTP請求的後半部分,它將被解釋為一個新的請求。
要在易受攻擊的前端觸發基於暫停的異步,首先發送你的標頭,承諾正文,然後等待。最終你會收到一個響應,當你最終發送你的請求正文時,它會被解釋為一個新的請求:
Apache在這個發現之後,我碰到了Turbo Intruder 的請求超時,發現同樣的技術也適用於Apache。與Varnish一樣,它在服務器自己生成響應而不是讓應用程序處理請求的端點上很容易受到攻擊。發生這種情況的一種方法是使用服務器級重定向:
如果你發現一個服務器容易受到基於暫停的異步的影響,你有兩個利用它的選項,具體取決於它是前端還是後端。
服務器端如果易受攻擊的服務器在後端運行,你可能會觸發服務器端異步。為此,你需要一個將請求流傳輸到後端的前端。特別是,它需要在不緩衝整個請求正文的情況下以HTTP 標頭轉發。
這裡有一個小問題。前端不會讀取超時響應並將其傳遞給我們,直到看到我們發送完整的請求。因此,我們需要發送我們的標頭,暫停一段時間,然後在沒有提示的情況下繼續其餘的攻擊序列。我不知道有任何安全測試工具支持像這樣部分延遲請求,所以我在Turbo Intruder 中實現了支持。隊列接口現在有三個新參數:
pause before指定一個Turbo應該暫停的偏移量。
pauseMarker 是一種替代方案,它採用Turbo 在發出後應暫停的字符串列表。
pauseTime 指定暫停多長時間,以微秒為單位;
那麼,哪些前端實際上具有這種請求流?一個著名的前端是Amazon 的Application Load Balancer (ALB),但還有一個額外的障礙。如果ALB 收到對部分請求的響應,它將拒絕重用連接。
幸運的是,這種機制中有一個固有的競爭條件。你可以通過延遲請求的後半部分來充分利用ALB 後面的Varnish,使其在後端超時的同時到達前端。
匹配超時在利用ALB 背後的Apache 時還有一個額外的複雜性,兩台服務器的默認超時時間都是60 秒。這就為發送請求的第二部分留下了極短的時間窗口。
我試圖通過發送一些被前端規範化的數據來解決這個問題,以便在不影響後端計時器的情況下重置前端的計時器。不幸的是,塊大小填充、塊擴展或TCP 重複/無序數據包都沒有達到這個目標。
最後,為了證明這個概念,我使用Turbo Intruder 發起了一次緩慢但持續的攻擊。 66個小時後,這個實驗最終成功了。
MITM 攻擊由於基於暫停的異步攻擊使用合法的HTTP 請求,人們很自然地想知道它們是否可以用來觸發客戶端異步。我探索了使瀏覽器在發出請求的中途暫停的選項,但儘管Streaming Fetch 聽起來很有希望,但它還沒有實現,最終測試失敗。
然而,有一種方法絕對可以延遲瀏覽器請求——主動MITM 攻擊。 TLS 旨在防止數據在傳輸過程中被解密或修改,但它是通過TCP 捆綁的,沒有什麼可以阻止攻擊者延遲整個數據包。這可以稱為盲目MITM 攻擊,因為它不依賴於解密任何流量。
攻擊流程與常規的客戶端異步攻擊非常相似。用戶訪問攻擊者控制的頁面,該頁面向目標應用程序發出一系列跨域請求。第一個HTTP 請求被故意填充到非常大,以至於操作系統將其拆分為多個TCP 數據包,從而使活動的MITM 能夠延遲最終數據包,從而觸發基於暫停的異步。由於存在填充,攻擊者只需根據數據包的大小就能識別出需要暫停的數據包。
我能夠使用默認配置和一個重定向規則成功地對一個獨立的基於apache的網站執行此攻擊:
從客戶端看,除了請求填充之外,它看起來像使用HEAD 小工具的常規客戶端異步:
在執行盲目MITM 的攻擊者係統上,我使用tc-NetEm 實現了延遲:
通過修改請求填充和數據包大小過濾器,我在目標瀏覽器上實現了大約90% 的成功率:
總結本文所涉及的主題和技術具有進一步研究的潛力:
1.使用瀏覽器發出的請求觸發客戶端異步的新方法;
2.一種基於暫停的服務器端異步漏洞的有效且可靠的檢測方法;
3.更多用於客戶端不同步攻擊的利用小工具;
4.使用CSD-chaining 的真實PoC;
5.一種需要MITM 來延遲瀏覽器請求的方法;
6.一種在HTPT/2 可用時強制瀏覽器使用HTTP/1 的方法;
緩解措施你可以通過使用HTTP/2 端到端來緩解本文中的大多數攻擊。 HTTP/2中也可能存在類似的漏洞,但可能性要小得多。我不建議前端支持HTTP/2,然後重寫HTTP/1.1請求來與後端通信。這確實緩解了客戶端異步攻擊,但它無法緩解服務器端基於暫停的攻擊,並且還引入了其他的威脅。
如果你的公司通過轉發代理路由員工的流量,請確保支持並啟用上游HTTP/2。注意,使用正向代理還引入了超出本文範圍的一系列額外的請求走私風險。
HTTP/1.1 的明文性質使它看起來很簡單,並使開發人員實現自己的服務器。不幸的是,即使是HTTP/1.1 的極簡實現也容易出現嚴重漏洞,特別是如果它支持連接重用或部署在單獨的前端之後。