HTTP2Shell(CVE-2026-111497)排查指南:pip-audit 曾漏報 Brotli,如何檢查軟件堆疊
HTTP2Shell (CVE-2026-111497): pip-audit Missed Brotli. How to Check Your Stack
HTTP2Shell(CVE-2026-111497)影響 Brotli 1.0.3 至 1.2.0,指南列出 Python、Node、Bun 和 Docker 映像的檢查方法及修補版本。
文章把排查範圍由 Python 依賴延伸至 Node、Bun、Envoy 及容器映像內置副本,並列出修補版本,方便核對多種執行環境。
如果你在星期四執行 pip-audit,得到一份沒有發現問題的報告後便不再理會,請再執行一次。CVE-2026-111497 是 Google Brotli 解碼器中的越界寫入漏洞。Aikido 現在在標題中稱它為 HTTP2Shell;網上其他地方大多稱它為 Node2Shell。它的 CVSS v4 評分為 9.2,影響 Brotli 1.0.3 至 1.2.0。截至 10 月 23 日,Aikido 報道,pip-audit、Dependabot 和 Safety 都沒有標示有漏洞的 Python 套件,因為安全公告資料庫尚未將這個 CVE 對應至 PyPI 上的 Brotli 或 brotlicffi。
這項對應現已加入。不過,掃描器仍不適合用來判斷「我是否受影響?」,因為大多數有漏洞的版本根本不是 Python 套件。以下是我會改用的檢查方式,涵蓋 Python、Node、Bun 和 Docker 映像檔,以及已修正的版本。
CVE-2026-111497 究竟是甚麼
漏洞位於 c/dec/decode.c。在有漏洞的版本中,DecodeBlockTypeAndLength()會傳回純布林值,因此 BROTLI_FALSE 可能代表「這個資料流格式錯誤」,也可能代表「我需要更多輸入」。呼叫它的程式無法分辨兩者。Node.js 安全公告描述了接下來的情況:無效的區塊切換會被回報為暫時需要更多輸入,而串流解碼器會不斷將位元組追加至一個八位元組的內部緩衝區,並寫入緩衝區末端之外。Node 列出的可能後果包括程式崩潰、記憶體資料洩露和遠端程式碼執行。
上游修正是 google/brotli 的提交 0ed4268。它新增了錯誤代碼 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH (-17)。根據 Aikido 的文章,一名研究人員比較了 1.2.0 和 1.2.1,發現兩者只有版本號更新,以及該提交對解碼器所作的修改,之後以 AddressSanitizer 編譯兩者。1.2.0 接收一段約 23 MB 的特製資料流後仍不斷要求更多資料,即使每次只輸入一個位元組也一樣。1.2.1 則每次都以 -17 拒絕該資料流。
Node 將這項漏洞報告歸功於 OpenAI Codex Security(Khai Tran 和 Sylvie Mayer)。Aikido 表示,OpenAI 在 2 月發現了這個漏洞,當時 Brotli 原始碼中的根本原因已修正,但沒有人將它視為安全問題,因此直到現在才有版本包含這項修正。HN 討論串中的留言者質疑 2 月這個日期,因此我會視之為未經確認。
觸發漏洞不需要 HTTP,但你最可能透過 HTTP 遭到攻擊
HN 討論串花了不少篇幅嘲笑「HTTP2Shell」這個名稱。這是解壓縮器漏洞,正如 pimterry 在討論串中指出,你可以自行解碼 Brotli,在完全不涉及 HTTP 的情況下觸發漏洞。有人提出另一個名稱 Broccoli2Shell,它已經有自己的網站。我比較喜歡這個名稱。
不過,實際上的大部分風險來自 HTTP 內容協商。任何會解碼來自不受信任對等端的 Content-Encoding: br 的程式都有可能受影響,而且流量的兩個方向都一樣:
- 入站:惡意用戶端向會解壓縮請求本文的伺服器傳送 Brotli 壓縮的請求本文。啟用了本文解析的 Node 應用程式屬於這類情況;開啟了請求解壓縮功能的 ASP.NET 也一樣。
- 出站:惡意或遭入侵的伺服器向會自動解碼 Brotli 的用戶端傳送 Brotli 回應。在 Python 中,這包括 requests、httpx 和 aiohttp。
在 HN 上,bawolff 認為出站情況的嚴重程度低得多,因為攻擊者首先必須設法讓你的程式碼呼叫他們的伺服器。對於只會呼叫 Stripe 的支付服務來説,這個説法合理。但對爬蟲、Webhook 傳送程式、連結預覽工具、RSS 擷取器,或任何會執行 requests.get(user_supplied_url) 的程式來説,這個説法就不成立。如果你的服務會擷取用戶提供的網址,攻擊者便能控制該伺服器。
Python:只要安裝了 Brotli,requests 就會解碼 Brotli
urllib3(因此 requests 亦然)只要 Brotli 或 brotlicffi 可以匯入,便會解碼 Brotli。你無須自行選擇啟用。在安裝了 Brotli 1.1.0、requests 2.34.2 和 urllib3 2.8.0 的 venv 中,requests 預設會傳送以下內容:
>>> requests.utils.default_headers()["Accept-Encoding"]
'gzip, deflate, br'
解除安裝 Brotli 後,br 就會消失。httpx 在安裝 brotli extra 時也會這樣做;安裝 Brotli 後,aiohttp 也會這樣做,而 Aikido 列出了 Scrapy 及任何以這些用戶端為基礎構建的軟件。Python Brotli 套件每月下載量達 82.8 million 次,我懷疑其中很多下載都不是由親自輸入 pip install brotli 的人造成。
因此,請檢查環境中是否有任何套件引入了 Brotli,以及引入的是哪個版本。requests 的版本號無法告訴你這一點。
如何在 5 分鐘內檢查是否使用了有漏洞的 Brotli
Python:檢查你關注的每個 venv、CI 執行器及映像:
pip show Brotli brotlicffi
如果 Brotli 低於 1.2.1,或 brotlicffi 低於 1.2.1.0,你就有漏洞(GHSA-9v8h-qqgm-4m35)。其中一個套件有一行 WARNING: Package(s) not found 就可以。
要查明是甚麼套件引入了它:
pipdeptree -r -p Brotli
pipdeptree -r -p brotlicffi
查閲鎖定檔,比逐一啟動每個環境更快:
grep -i brotli poetry.lock uv.lock requirements*.txt 2>/dev/null
它往往會在容器映像中出現。覆寫 entrypoint,避免映像本身的啟動命令造成幹擾:
docker run --rm --entrypoint pip <image> show Brotli brotlicffi
Node 和 Bun:
node -v # need 22.23.4, 24.22.1 or 26.12.1+
bun -v # need 1.4.4+
Node 20 已於 2026 年 4 月 30 日終止支援,而已終止支援的版本系列不會再獲得安全更新。Node 內置自己的 Brotli,因此作業系統套件在這方面並不重要。單一可執行檔應用程式和已編譯的 Bun 二進制檔也一樣:它們各自內置 Brotli 副本,必須重新建置。
已修正版本
| 元件 | 修正版本 | 注意事項 |
|---|---|---|
| Node.js | 22.23.4, 24.22.1, 26.12.1 | 重建映像及 SEA 二進制檔 |
| libbrotli | 1.2.1(較舊版本系列則為 1.0.10 / 1.1.1) | Linux 發行版可能會回移修正,但不提升版本號 |
Python Brotli
|
1.2.1 | 重新產生鎖定檔 |
brotlicffi |
1.2.1.0 | 相同 |
| Bun | 1.4.4 | 重新編譯 bun build --compile 輸出 |
| Envoy | 1.40.0, 1.39.3, 1.38.6, 1.37.8 | 側車會隨服務網格更新,而非隨你的應用程式更新 |
| Istio | 1.29.9, 1.30.6, 1.31.2 | 檢查其隨附的 Envoy 版本 |
| curl / libcurl | 供應商提供的、以 Brotli 1.2.1 建置的版本 | 靜態建置版本需要重新建置 |
Python:
pip install -U "Brotli>=1.2.1"
pip install -U "brotlicffi>=1.2.1.0"
之後重新產生 Poetry、uv 或 pip-tools 鎖定檔,並重建所有包含該套件的映像。在手提電腦上更新套件,並不能修補上星期二已推出的容器。
pip-audit 現在能否偵測 CVE-2026-111497?
對 PyPI 套件而言,可以。OSV 條目現在列出了 brotli 和 brotlicffi,別名為 PYSEC-2026-4322 和 PYSEC-2026-4323。該安全公告於 2026 年 10 月 22 日發布,並於 2026 年 10 月 24 日最後修改。如果你的每週掃描在對應資料出現前已執行,結果便會在 9.2 分的漏洞旁顯示綠色剔號。
我更大的不滿在於,pip-audit 本來就無法檢查以下所有內容:
- 靜態連結至 Node 二進制檔中的 Brotli
- 已編譯 Bun 可執行檔內的副本
- Istio 服務網格中的 Envoy 側車
- 靜態建置的 curl 所用的 libbrotli
- 已回移修正但版本字串沒有改變的 Linux 發行版套件(HN 討論串進行期間,Ubuntu 的 libbrotli1 修正仍在審核中,嚴重程度為中等)
HN 討論串中的人亦列出 Git for Windows、Wireshark、Visual Studio 和 .NET 等產品有隨附 Brotli。Python 依賴項掃描器只會報告 Python 依賴項,而這個漏洞存在於一個 C 程式庫中,供應商會在大約一半的技術堆疊中內置這個程式庫。
如果今天無法修補,可採用的權宜措施:移除 br
- 輸入流量:在任何元件解碼前,於代理伺服器、負載平衡器或中介軟件拒絕任何
Content-Encoding含有br的請求,並以不區分大小寫的方式比對。Nginx 和 Apache 預設不會解碼 Brotli 請求內文(Apache 的mod_brotli只會壓縮),因此可在這些位置封鎖它。 - 對外流量(Python):移除安裝的
Brotli和brotlicffi,刪除httpx[brotli]等額外套件,重新產生鎖定檔,然後重新啟動。用戶端會改用 gzip 和 deflate。這對捆綁在其他執行環境中的 Brotli 毫無作用。 - 一般而言,對外流量:從
Accept-Encoding移除br並不足夠。惡意伺服器可以不論你是否要求,都傳送 Brotli,因此你必須在回應本文自動解碼前,拒絕br回應。
有人在 HN 詢問,Node 應用程式前方的 TLS 終止負載平衡器能否提供保護。討論串中的答案是否定的:終止 TLS 與解碼本文毫無關係,而且大部分負載平衡器都會原封不動地轉送本文。除非你明確設定負載平衡器丟棄 br,否則應假設它毫無作用。
Hacker News(新聞網站,不是論壇)報道,截至 10 月 22 日,尚未發現漏洞在野外遭利用。Aikido 亦指出,研究人員已在未經修改的 Node.js 24.22.0 版本中,利用同一段解碼器程式碼發動攻擊,因此我不會預期這種情況能維持太久。
你的掃描器有否標示出 Brotli,還是你手動在容器映像中找到它?我很好奇,大家找到的副本數量,與實際運行的服務數量相比有多少。
來源
來源:DEV Community · dev.to