← 返回上一頁
Linux/Unix 資安防護

HTTP2Shell 處置:Brotli 解碼漏洞在 Python、Node.js 的檢查與緩解

本頁目錄
文章資訊

適用讀者:維運 Python/Node.js 服務、容器映像或反向代理的工程師

測試版本:Python 3.12.15、brotli 1.2.0/1.2.1、requests 2.32.5、urllib3 2.8.0、Node.js 24.22.0/24.22.1、nginx 1.30.6、curl 7.76.1

測試環境:Rocky Linux 9.4(kernel 5.14)、Podman 5.4.0、4 vCPU/3 GB RAM

資訊截至:2026-10-25(事件仍在發展,各發行版修補進度以官方公告為準)

10 月 21 日 Google Brotli 一口氣釋出 1.0.10、1.1.1、1.2.1 三個修補版,同一天 Node.js 發佈安全更新,修掉編號 CVE-2026-111497 的 Brotli 解碼器越界寫入(out-of-bounds write)。一開始大家叫它 Node2Shell,以為是 Node.js 的事;隔天 PyPI 的 brotli 套件也被列入,到 10 月 23 日 Envoy、Istio、Bun、curl 靜態連結版都上了受影響清單,名字也跟著變成 HTTP2Shell。

這個漏洞真正麻煩的地方不在 CVSS 9.2,而是「誰會解 Brotli」很難盤點:requests、urllib3、Node 的 fetch()、curl --compressed 都會在收到 Content-Encoding: br 時自動解碼,開發者多半不知道自己的程式碼路徑上有這一段。所以處置重點是先把解碼點找出來,能升級的升級並重建映像,暫時升不了的就在入口擋掉 br。

這篇整理第一手公告的受影響版本與修補版,再用一台 Rocky Linux 9.4 實測 Python、Node.js、系統 libbrotli 三層的檢查方式,以及 nginx 拒收 br 請求的前後差異。讀完可以直接拿指令去掃自己的主機與映像。

重點摘要
  • Brotli 1.0.3~1.2.0 的解碼器都受影響,修補版是 1.0.10/1.1.1/1.2.1;Node.js 修補版是 22.23.4、24.22.1、26.12.1。
  • 實測 Node.js 24.22.1 的 process.versions.brotli 仍顯示 1.2.0,用這個值判斷會誤報,要看 Node 版本或新增的 zlib 常數。
  • 移除 Python brotli 套件後,requests 不再送 br、也不再解碼 br 回應;但 Node fetch() 沒宣告 br 照樣會解。
  • Red Hat 截至 10/25 把 RHEL 8/9/10 的 brotli 標為 Affected,實測 Rocky 9 最新的 libbrotli 1.0.9-10 也還沒有修補,系統層只能先靠緩解。
  • 截至 10/25,CVE-2026-111497 不在 CISA KEV,目前看到的只有研究人員的示範,還沒有實際攻擊的公開報告。

一、事件摘要與時間軸

CVE-2026-111497 是共用函式庫的漏洞,揭露順序是從下游的 Node.js 往上游的 Brotli、再擴散到各語言與代理軟體,所以前兩天的報導只講 Node.js。根據 Aikido 整理的時間軸,這個 bug 其實 2026 年 2 月就被 OpenAI 的研究人員找到,上游原始碼也改了,只是當時沒被當成資安問題,一直沒有發正式版本,直到 10 月才補 CVE 並發修補版。

2026-02
問題被找到
上游修了原始碼,但沒有當成資安問題,也沒有發正式版
2026-10-21
Brotli 1.0.10/1.1.1/1.2.1 與 Node.js 安全更新同日釋出
Node.js 22.23.4、24.22.1、26.12.1,嚴重度 High
2026-10-22
NVD 收錄、GHSA-9v8h-qqgm-4m35 發佈
PyPI brotli、brotlicffi 列入;Google CNA 給 CVSS v4 9.2;Red Hat 評為 Important
2026-10-23
改稱 HTTP2Shell
Envoy、Istio、Bun、curl 靜態連結版等陸續列入受影響清單
2026-10-25
本文實測當天
CISA KEV(2026.10.23 版)未收錄;RHEL 9 brotli 仍為 Affected

各方的嚴重度評分不一樣,看公告時要知道差在哪:

來源 評分 說明
Google(CNA) CVSS v4 9.2 Critical AV:N/AC:L/AT:P/PR:N/UI:N,機密性、完整性、可用性皆 High;CWE-787、CWE-252
Node.js High 列在 10 月安全更新的 4 個 High 之一
Red Hat Important,CVSS v3.1 8.1 認為要送出畸形串流才觸發,加上 ASLR、NX、FORTIFY_SOURCE、SELinux,把 Attack Complexity 評為 High
CISA KEV 未收錄 以 2026-10-25 抓取的 catalog 2026.10.23 版為準

Node.js 內建的 Brotli 解碼器有缺陷,畸形的壓縮輸入會寫超過內部解碼緩衝區的結尾。攻擊者只要能提供被應用程式用 node:zlib 解壓的 Brotli 資料,就能破壞行程記憶體,導致崩潰、記憶體外洩或遠端執行程式碼。

— Node.js, October 2026 Security Releases

二、影響範圍與受影響版本

影響範圍要看「程式有沒有在解 Brotli」,光看有沒有裝 Brotli 不夠:只做壓縮的元件不受影響,會自動解碼的 HTTP client 與 body parser 才是重點。Aikido 特別提到 Apache 的 mod_brotli 與 nginx 預設都只壓縮不解碼,反而是應用程式那一層的函式庫常常默默在解。

元件 受影響範圍 修補版本
Google Brotli(libbrotli) 1.0.3~1.2.0 1.0.10、1.1.1、1.2.1
PyPI brotli ≥ 1.0.3、< 1.2.1 1.2.1
PyPI brotlicffi ≥ 1.0.3、< 1.2.1.0 1.2.1.0
Node.js 22.x、24.x、26.x(EOL 版本不修) 22.23.4、24.22.1、26.12.1
Bun 1.4.4 之前 1.4.4
Envoy 各支援中分支 1.40.0、1.39.3、1.38.6、1.37.8
Istio 各支援中分支 1.29.9、1.30.6、1.31.2
RHEL 8/9/10 brotli、nodejs 模組、Firefox、Thunderbird Red Hat 標為 Affected 截至 10/25 尚未發佈 RHSA(只有 Hardened Images 的 RHSA-2026:87420)

會碰到攻擊者可控 Brotli 資料的路徑大致有三種,下圖是我整理的攻擊面:

HTTP2Shell 攻擊面:出站回應、入站請求、檔案三條路徑進入 Brotli decoder
圖 1:三條會讓攻擊者可控的 br 串流進到解碼器的路徑。出站的 HTTP client 最容易被忽略,因為只要對方伺服器回 br 就會自動解。
  • 出站:服務去呼叫外部 API、爬網頁、抓 webhook,對方(或中間人、被入侵的上游)回 Content-Encoding: br,requests/urllib3/aiohttp/Node fetch()/curl --compressed 會自動解碼。
  • 入站:服務接受 Content-Encoding: br 的 request body,例如 Express 5 的 body-parser、啟用解壓 filter 的 Envoy/Istio。
  • 檔案與訊息:程式主動呼叫 zlib.brotliDecompress() 或 brotli.decompress() 處理使用者上傳的檔案、佇列訊息。

三、技術成因

問題出在 c/dec/decode.c 對錯誤狀態的判斷:遇到無效的 block switch 時,解碼器回報的是「需要更多輸入」這種暫時狀態,而不是永久的格式錯誤,於是串流解碼器持續把輸入位元組往一個只有 8 bytes 的內部緩衝區塞,最後寫超過邊界。這是典型的 CWE-252(沒檢查回傳值)連帶造成 CWE-787(越界寫入)。

修補方式也很直接:上游 commit 0ed4268a 把這個情況改成格式錯誤直接中止。Node.js 這次是把這個 commit cherry-pick 進內建的 deps/brotli,再多加一個 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH 錯誤常數,並沒有把整份 Brotli 升到 1.2.1,這點在檢查時會造成誤判,第四章會實測。

要注意這是正常壓縮器不會產生的畸形串流,所以「我只解自己系統壓出來的資料」的場景風險低;風險集中在來源不受控的資料。Red Hat 也是用這個理由把 Attack Complexity 評為 High,但他們同時說明各種加固只會增加利用難度,崩潰和部分影響仍然無法排除。

四、如何檢查自己是否受影響

我在一台 Rocky Linux 9.4 測試機上用 Podman 建了兩組 Python 映像(brotli 1.2.0 與 1.2.1)、兩組 Node.js 映像(24.22.0 與 24.22.1),從系統層、套件層、映像層各跑一輪檢查。下面是我的檢查順序:

HTTP2Shell 處置流程:盤點、套件層、系統層、映像檢查,再依有無修補版決定升級或緩解
圖 2:先盤點解碼點,再分層檢查;有修補版就升級並重建映像,沒有就先上緩解,最後兩條路都要回頭驗證。

4.1 系統層:libbrotli 與連結它的程式

先看主機本身。Rocky 9.4 預設就裝了 libbrotli 1.0.9,正好落在 1.0.3~1.2.0 的範圍內,而且 curl 和 libcurl 都直接連結 libbrotlidec:

rocky9 — libbrotli
$ rpm -qa | grep -i brotli
libbrotli-1.0.9-6.el9.x86_64
$ rpm -q --whatrequires "libbrotlidec.so.1()(64bit)"
woff2-1.0.2-15.el9.x86_64
freetype-2.10.4-9.el9.x86_64
libsoup-2.72.0-8.el9.x86_64
libcurl-7.76.1-29.el9_4.1.x86_64
$ curl -V | head -1
curl 7.76.1 (x86_64-redhat-linux-gnu) libcurl/7.76.1 OpenSSL/3.0.7 zlib/1.2.11 brotli/1.0.9 libidn2/2.3.0 ...
$ curl -sv --compressed -o /dev/null http://127.0.0.1:18081/ 2>&1 | grep -i accept-encoding
> Accept-Encoding: deflate, gzip, br

這裡踩了一個小雷:第一次直接跑 rpm -q --whatrequires libbrotli 得到「沒有軟體包需要 libbrotli」,看起來像沒人用,但 RPM 的相依是記在 soname 上,要查 libbrotlidec.so.1()(64bit) 才會列出 libcurl、freetype 這些真正的使用者。

接著想用 dnf updateinfo 查有沒有對應的安全公告,結果指令卡住超過兩分鐘沒反應。看了 log 才發現是主機上另一個第三方 repo 在等 GPG key 匯入確認(這樣可以嗎 [y/N]),非互動執行時就永遠停在那裡。排除那個 repo 後才查得到:

rocky9 — dnf
$ dnf -q --assumeno --disablerepo="ookla*" updateinfo list --cve CVE-2026-111497 | wc -l
0
$ dnf -q --assumeno --disablerepo="ookla*" list --available --showduplicates libbrotli | tail -2
libbrotli.i686                        1.0.9-10.el9_8                      baseos
libbrotli.x86_64                      1.0.9-10.el9_8                      baseos
$ dnf -q --assumeno --disablerepo="ookla*" changelog --count 1 libbrotli
* 一  7月 27 2026 Miro Hrončok <[email protected]> - 1.0.9-10
- Accept output_buffer_limit as an alias for max_output_length
  in Decompressor.process() for forward compatibility with upstream 1.2.0
- Resolves: RHEL-221150

repo 裡最新的 1.0.9-10 是 7 月的版本,changelog 跟這個 CVE 無關,updateinfo 也查不到公告。對照 Red Hat 的 CVE 頁面,RHEL 9 的 brotli 截至 10/25 仍是 Affected、沒有 RHSA。也就是說,Rocky/RHEL 9 主機目前沒有套件可以升,curl 這類系統工具只能先靠第五章的緩解。

踩雷紀錄

發行版常把修補 backport 到舊版號,所以 RHEL 系看到 1.0.9 不代表一定沒修;反過來,版號沒變也不代表修好了。判斷依據要以 changelog 與廠商 CVE 頁面為準,不要只比版號。

4.2 Python:套件版本與 HTTP client 行為

Python 這邊先寫一支小腳本,同時檢查 brotli、brotlicffi 版本,以及 urllib3 會送出的 Accept-Encoding(requests 底層用的就是 urllib3):

# check_py.py:檢查 brotli / brotlicffi 版本與 urllib3 實際送出的 Accept-Encoding
import importlib.metadata as md
from urllib3.util import make_headers
import urllib3

def ver(p):
    try:
        return md.version(p)
    except md.PackageNotFoundError:
        return None

FIXED = {"brotli": (1, 2, 1), "brotlicffi": (1, 2, 1, 0)}
for pkg, fixed in FIXED.items():
    v = ver(pkg)
    if v is None:
        print(f"{pkg:11s} not installed")
        continue
    t = tuple(int(x) for x in v.split(".")[:len(fixed)])
    state = "VULNERABLE" if (1, 0, 3) <= t[:3] and t < fixed else "ok"
    print(f"{pkg:11s} {v:10s} {state}")
print("urllib3    ", urllib3.__version__)
print("Accept-Encoding sent by urllib3/requests:", make_headers(accept_encoding=True)["accept-encoding"])

在 1.2.0 與 1.2.1 兩個映像裡各跑一次:

python:3.12-slim — brotli 1.2.0 vs 1.2.1
$ podman run --rm py-vuln python /app/check_py.py
brotli      1.2.0      VULNERABLE
brotlicffi  not installed
urllib3     2.8.0
Accept-Encoding sent by urllib3/requests: gzip,deflate,br
$ podman run --rm py-fixed python /app/check_py.py
brotli      1.2.1      ok
brotlicffi  not installed
urllib3     2.8.0
Accept-Encoding sent by urllib3/requests: gzip,deflate,br

只要環境裡裝了 brotli,urllib3 就會主動宣告支援 br。為了確認它真的會自動解,我起了一個測試用的小伺服器,回應一段正常、由 brotli.compress() 產生的 br 內容:

requests 自動解碼 br 回應
$ podman run --rm --network labnet py-vuln python -c 'import requests; r=requests.get("http://brsrv:8080/"); print(r.status_code, r.headers["Content-Encoding"], repr(r.text))'
200 br 'hello from lab, decoded by client\n'
$ podman logs brsrv
server got Accept-Encoding: gzip, deflate, br

程式碼裡一行都沒提到 Brotli,回應卻已經經過 Brotli 解碼器。這就是「出站」路徑容易被忽略的原因。

接著用 pip-audit 看掃描工具抓不抓得到。Aikido 在 10 月 23 日提醒當時 CVE 與 OSV 還沒對應到 PyPI 套件,pip-audit、Dependabot 可能漏報;我 10 月 25 日實測時已經抓得到:

pip-audit(2026-10-25)
$ pip-audit 2>&1 | grep -iE "brotli|Name|----"
Name     Version ID                  Fix Versions
-------- ------- ------------------- ------------
brotli   1.2.0   PYSEC-2026-4322     1.2.1
brotli   1.2.0   GHSA-9v8h-qqgm-4m35 1.2.1

如果你的 CI 在 10/23 前跑過掃描並顯示乾淨,建議重跑一次,資料庫已經更新了。

4.3 Node.js:版本號會騙人

Node.js 的直覺做法是看 process.versions.brotli,但實測兩個版本的結果都是 1.2.0:

node:24.22.0-slim vs node:24.22.1-slim
$ ./scan_images.sh node:24.22.0 node:24.22.1 py-vuln py-fixed
== node:24.22.0
node v24.22.0 bundled brotli 1.2.0
== node:24.22.1
node v24.22.1 bundled brotli 1.2.0
== py-vuln
pip  brotli             1.2.0
== py-fixed
pip  brotli             1.2.1

一開始我以為是 24.22.1 的映像沒修到,後來去翻 Node.js 的 CHANGELOG_V24 才確認:24.22.1 是 cherry-pick 上游 commit 0ed4268a,deps/brotli/c/common/version.h 的版本號仍是 1.2.0。所以用 SBOM 或掃描器比對「內建 Brotli 版本」的話,修好的 Node 也會被標成有漏洞。比較可靠的是看 Node 本身的版本,或檢查這次新加的錯誤常數:

用 zlib 常數判斷是否已修補
$ podman run --rm node:24.22.0 node -p 'process.version + " BLOCK_SWITCH constant: " + ("BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH" in require("zlib").constants)'
v24.22.0 BLOCK_SWITCH constant: false
$ podman run --rm node:24.22.1 node -p 'process.version + " BLOCK_SWITCH constant: " + ("BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH" in require("zlib").constants)'
v24.22.1 BLOCK_SWITCH constant: true

再用 Node 內建的 fetch() 打同一台測試伺服器,結果更值得注意:

Node fetch() 對 br 回應的處理
$ podman run --rm --network labnet -v ./:/w:Z node:24.22.0 node /w/fetch_test.mjs http://brsrv:8080/
v24.22.0 brotli 1.2.0 status 200 content-encoding br
body: "hello from lab, decoded by client\n"
$ podman logs --tail 1 brsrv
server got Accept-Encoding: gzip, deflate

Node 的 fetch() 送出的 Accept-Encoding 只有 gzip, deflate,沒有宣告 br,但伺服器硬回 br 時它照樣解碼。這直接印證了 Aikido 的提醒:只把 br 從 Accept-Encoding 拿掉擋不住惡意伺服器,Node.js 唯一可靠的做法是升級。

展開 scan_images.sh(逐一檢查映像內的 Brotli)
#!/bin/bash
# 逐一檢查映像內的 Brotli 版本(Python 套件 / Node 內建 / 系統 libbrotli)
set -uo pipefail
for img in "$@"; do
  echo "== $img"
  podman run --rm --entrypoint sh "$img" -c '
    command -v node >/dev/null && echo "node $(node -v) bundled brotli $(node -p process.versions.brotli)"
    command -v pip >/dev/null && pip list 2>/dev/null | grep -iE "^brotli(cffi)? " | sed "s/^/pip  /"
    for f in /usr/lib/*/libbrotlidec.so.* /usr/lib64/libbrotlidec.so.* /usr/lib/libbrotlidec.so.*; do [ -f "$f" ] && echo "lib  $f"; done
    true'
done

Node 那一行只是列出資訊,判斷是否修補請以 Node 版本或上面的 zlib 常數為準。Docker 環境把 podman 換成 docker 即可。

五、修補與緩解

能升級的元件直接升級並重建映像,升不了的才用緩解頂著。修補版前面的表格已經列了,這裡補三件實務上常漏的事:

  1. 映像要重建:在 Dockerfile 裡 pip install brotli 或 FROM node:24 的映像,只要沒重新 build,裡面就還是舊版。base image 也要重新 pull,不要吃到 build cache。
  2. lock 檔要更新:brotli 常是 httpx[brotli]、Scrapy、aiohttp 的間接相依,pip install -U 沒用的話要改 requirements.txt/poetry.lock/uv.lock 裡釘住的版本。
  3. 靜態連結要另外查:自行編譯的 curl、Envoy 等若靜態連結 Brotli,套件管理員看不到,要回到建置流程確認。

5.1 Python:升級或先移除 brotli

升級就是 pip install -U "brotli>=1.2.1"(brotlicffi 則是 >=1.2.1.0)。真的暫時不能升時,Aikido 建議的緩解是直接移除 brotli 套件。我在 1.2.0 的映像裡移除後,再打一次會硬回 br 的測試伺服器:

移除 brotli 後的 requests 行為
$ pip uninstall -y -q brotli && python /app/check_py.py
brotli      not installed
brotlicffi  not installed
urllib3     2.8.0
Accept-Encoding sent by urllib3/requests: gzip,deflate
$ python -c 'import requests; r=requests.get("http://brsrv:8080/"); print(r.status_code, r.headers["Content-Encoding"], r.content[:16])'
200 br b'\x1b#\x00\xf8\x05jr\x9e\xd0\xcdS\xe9\xf7Y\x90\xb5'

拿到的是沒解開的原始 br 位元組,表示解碼器根本沒被呼叫,這個緩解在 Python 是有效的。代價是遇到只回 br 的正常伺服器時,程式拿到的內容是壞的,要評估呼叫對象能不能接受。

5.2 入站:在反向代理拒收 br 請求

對外服務如果會解 request body(Express 5 body-parser、啟用解壓的 Envoy filter 等),最快的緩解是在 nginx 這層把 Content-Encoding 含 br 的請求直接回 415。nginx 本身不解 request body,所以它擋在前面不會引入新的解碼點。我用 nginx 1.30.6 擋在一個會把收到的 header 原樣回顯的後端前面,設定改動如下:

/etc/nginx/nginx.conf
events {}
http {
+  map $http_content_encoding $reject_br {
+    default 0;
+    "~*(^|,)\s*br\s*(,|$)" 1;
+  }
  server {
    listen 8081;
    location / {
+      if ($reject_br) { return 415 "br request body not accepted\n"; }
      proxy_pass http://127.0.0.1:8080;
+      proxy_set_header Accept-Encoding "gzip";
    }
  }
}

前後各用三種 Content-Encoding 測一次,第三種故意混大小寫並放在清單後面,確認正規式不會被繞過:

nginx 1.30.6 — 修改前後
### 修改前
$ for ce in gzip br "gzip, BR"; do curl -s -w " [HTTP %{http_code}]\n" -X POST -H "Content-Encoding: $ce" --data-binary test http://127.0.0.1:18081/; done
upstream got POST Content-Encoding=gzip Accept-Encoding=None [HTTP 200]
upstream got POST Content-Encoding=br Accept-Encoding=None [HTTP 200]
upstream got POST Content-Encoding=gzip, BR Accept-Encoding=None [HTTP 200]
### 修改後
$ nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ for ce in gzip br "gzip, BR"; do curl -s -w " [HTTP %{http_code}]\n" -X POST -H "Content-Encoding: $ce" --data-binary test http://127.0.0.1:18081/; done
upstream got POST Content-Encoding=gzip Accept-Encoding=gzip [HTTP 200]
br request body not accepted [HTTP 415]
br request body not accepted [HTTP 415]

修改前三種都直接送到後端;修改後含 br 的兩種在 nginx 就被擋下,gzip 照常通過。proxy_set_header Accept-Encoding "gzip" 那行是讓後端回給 nginx 的內容不要用 br,避免 nginx 之後若加了其他模組時多一個解碼點,也可以不加。

5.3 出站:限制服務能連到哪裡

出站路徑最難擋,因為 Node fetch() 不宣告 br 也會解。升級前能做的是降低「對方是惡意伺服器」的機率:用 egress 規則(K8s NetworkPolicy、防火牆、出站 proxy 白名單)限制服務只能連到已知的 API 端點,把爬蟲、任意 URL 預覽、webhook 測試這類「連到使用者指定網址」的功能先排進第一批升級。

六、加固建議

這次事件暴露的是「依賴的依賴」盤點問題,修完 CVE-2026-111497 之後,下面幾件事值得排進例行工作:

項目 做法 為什麼
映像定期重建 每週或每次 base image 更新就重 build,不靠 cache 這次修補要靠重建才會進到映像
SBOM 加上 runtime 版本 Node、Python、Bun 的 runtime 版本也進 SBOM Node 內建 Brotli 版本號不變,只看 library 版本會誤判
掃描資料庫要重跑 CVE 剛出來的 48 小時內每天重掃 pip-audit 10/23 抓不到、10/25 抓得到
解碼點白名單 列出哪些服務接受壓縮的 request body 下次 zlib、zstd 出事時可以直接查表
出站 egress 管控 服務只能連到登記過的外部網域 惡意伺服器回應類的漏洞都能降風險
主機 repo 健康檢查 第三方 repo 的 GPG key 先匯入好,排程跑 dnf updateinfo 確認不會卡住 實測時就因為一個 repo 等確認,讓安全公告查詢整個卡死
資安風險

緩解只是爭取時間。入站 415 擋不到出站回應,移除 Python brotli 擋不到 Node.js,系統 libbrotli 則還在等發行版修補。修補版一出就要排程升級並重建,不要讓緩解變成長期設定。

七、仍未明朗之處與反方觀點

截至 2026-10-25,有幾件事還沒有定論,處置時要保留調整空間。

利用難度的評估分歧。 Google 給 CVSS v4 9.2,Red Hat 則認為需要畸形串流、加上系統層加固,把 Attack Complexity 評為 High、整體降到 Important(8.1)。Aikido 引述研究人員已在未修改的 Node.js 24.22.0 上示範利用,但這是研究成果,目前沒有實際攻擊的公開報告,CISA KEV 也還沒收錄。對 Python 擴充套件,原始揭露者也表示沒有往記憶體破壞與 RCE 方向深入。我的判斷是:對外接收 br body 或會連到不受控網址的服務照 Critical 處理,內部批次程式可以排在後面。

發行版的修補時程未知。 RHEL 8/9/10 的 brotli、nodejs 模組、Firefox、Thunderbird 目前都是 Affected,沒有預計日期。Debian、Ubuntu、Alpine 的狀態本文沒有逐一實測,請以各自的 security tracker 為準。

命名混亂。 Node2Shell、HTTP2Shell,還有人叫 Broccoli2Shell,都是同一個 CVE。內部通報時直接寫 CVE 編號,避免有人以為是三個不同漏洞重複開單。

反方觀點:是不是反應過度? 有人認為只有解「不受控來源」的 br 才有風險,多數內部服務根本碰不到惡意串流。這個說法對單一服務成立,但實測顯示解碼點常藏在 HTTP client 的預設行為裡,開發者自己都不知道。在盤點清楚之前,很難保證「我們沒有在解不受控的 br」,所以盤點本身就是處置的一部分。

八、結論

CVE-2026-111497 是一個「函式庫小、影響面大」的漏洞:上游一個錯誤狀態判斷,經過 Node.js、Python、Envoy、curl 一路擴散。依實測結果,處置順序可以這樣排:

  1. Node.js 立刻升級到 22.23.4/24.22.1/26.12.1 並重建映像,因為 fetch() 沒宣告 br 也會解,沒有其他可靠的緩解;檢查時不要看 process.versions.brotli。
  2. Python 升級到 brotli 1.2.1(brotlicffi 1.2.1.0),升不了就先移除套件;重跑 pip-audit 確認資料庫已更新。
  3. 系統 libbrotli(RHEL/Rocky 9 等)目前沒有修補套件,對外服務先在反向代理拒收 br 請求、收緊出站連線,持續追 Red Hat CVE 頁面。

接下來一週要觀察的指標:CISA KEV 是否收錄、RHEL/Debian/Ubuntu 何時發佈修補套件、各雲端託管服務(API Gateway、Service Mesh)的修補公告,以及是否出現實際攻擊報告。

參考資料

分享這篇
X LinkedIn Facebook Hacker News Reddit

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料。