FreeRDP 伺服器端五個漏洞齊修:協商失敗未真正終止連線,三個洞串起來就是免驗證 RCE
FreeRDP GitHub Security Advisory / Release 3.31.0 · 2026-08-26
FreeRDP 專案在 2026 年 8 月 26 日釋出 3.31.0,一口氣修掉伺服器端五個安全漏洞,受影響範圍是 3.0.0-beta1 到 3.30.0 的所有版本。安全研究者 sam4k 於 9 月 1 日在 oss-security 郵件論壇公開細節,指出其中三個漏洞串在一起可以做到完全免驗證的遠端程式碼執行,而且因為 3.30.x 從未發過點版,所有受影響的散布版只能直接跳號到 3.31.0 或自行回補 patch。
漏洞機制
最嚴重的一項是 GHSA-x7v6-xfx3-52j6,CVSS 3.1 給到 9.3。問題出在伺服器處理 RDP 協定協商失敗時,並沒有真的中止連線。當用戶端與伺服器缺乏相容的安全協定,伺服器會組出一個內部數值,把 PROTOCOL_FAILED_NEGO (0x80000000) 與失敗原因碼(例如 HYBRID_REQUIRED_BY_SERVER, 0x00000005)做位元組合。問題是伺服器傳完 RDP_NEG_FAILURE 回應後,又重新載入這個未清除的內部值去做位元判斷,而不是直接關閉 socket。
在只允許 NLA 的設定下,組合出來的值是 0x80000005,剛好包含了 PROTOCOL_RDSTLS 的位元(0x00000004)。這代表一個完全沒有通過驗證的用戶端,只要故意送出伺服器不支援的協定組合,反而能繞過伺服器原本要求的驗證機制,一路走完 TLS 交握與能力交換,直接進到 RDSTLS 階段——等於在驗證機制生效之前就先把協定解析器暴露出來。
能串成 RCE 的另外兩個洞,一是 RDSTLS ResetGraphics 的堆積洩漏(CVSS 6.5):伺服器序列化一個固定 340 位元組的 PDU 時,遇到填充區塊沒有真的寫入內容,只是把游標往前跳過,導致約 300 位元組未初始化的堆積記憶體被送到對端。二是通道 PDU 追蹤器的偏移量錯位(CVSS 7.5):送一個超大訊息會造成對一個函式指標物件的 8 位元組可控覆寫,不過要在特定編譯旗標下才成立。三個洞串起來,主要影響仍在預覽階段的 GNOME Remote Desktop 51(Remote Login 模式)。
另外還有兩個獨立漏洞:DRDYNVC 動態虛擬通道解析器的 use-after-free(CVSS 7.5)——查找通道時拿到指標後就先釋放鎖,若此時另一執行緒同時關閉該通道就會出事;以及智慧卡 ATR 陣列的邊界檢查缺失(CVSS 6.5)——ATR 長度沒有跟固定的 32 與 36 位元組陣列比對,造成越界讀取。
受影響版本
- FreeRDP
3.0.0-beta1~3.30.0:五個漏洞皆受影響,無任何 3.30.x 點版可用 - GNOME Remote Desktop 51 預覽版(Remote Login 模式):暴露在三漏洞串接的完整 RCE 鏈之下
| 編號 | 問題 | CVSS | GHSA |
|---|---|---|---|
| 1 | 協商失敗未終止連線 | 9.3 Critical | GHSA-x7v6-xfx3-52j6 |
| 2 | RDSTLS ResetGraphics 堆積洩漏 | 6.5 Medium | GHSA-r7jx-j9h7-j4xj |
| 3 | 通道 PDU 追蹤器偏移錯位 | 7.5 High | GHSA-6mpx-c8rj-whj5 |
| 4 | DRDYNVC 解析器 UAF | 7.5 High | GHSA-9jcm-x588-gh26 |
| 5 | 智慧卡 ATR 陣列越界 | 6.5 Medium | GHSA-q65v-4w7q-hx3r |
修補與緩解
3.31.0 release note 把這次更新形容為「巨大的 bugfix 與安全性釋出」,並直接警告散布版盡快更新,整包總共處理了 22 個 CVE/GHSA。截至郵件公開當下,上述五個漏洞的 CVE 編號都還尚未正式核發,僅有 GHSA 編號。由於 3.30.x 沒有回補點版,唯一的修補路徑就是升級到 3.31.0,或自行 cherry-pick 對應的修補 commit。
原始來源:oss-security 郵件論壇公告、FreeRDP 3.31.0 Release Note、GHSA-x7v6-xfx3-52j6
gRPC-Go 修補 HTTP/2 資料訊框碎片化漏洞:符合流量控制規則,照樣把堆積記憶體吃光
GitHub Security Advisory GHSA-vp52-pcj8-j9qc · 2026-08-19
google.golang.org/grpc 修補了一個編號 CVE-2026-84304、GHSA 編號 GHSA-vp52-pcj8-j9qc 的漏洞,影響 ≤ 1.83.0 的所有版本,已在 1.83.1 修復。這個漏洞的特別之處在於:攻擊者完全不需要違反 HTTP/2 的流量控制視窗規則,單靠把傳輸內容切成極小的碎片,就能讓伺服器端堆積記憶體被榨乾。GitHub 公告將其列為 CWE-400(不受控資源消耗),CVSS 評為 8.7 高風險。
漏洞機制
HTTP/2 把一個請求或回應的 body 拆成一個或多個 DATA frame 傳輸,每個連線與每個 stream 各自維護一個流量控制視窗(flow-control window),接收端要有足夠的視窗額度才能收下新的資料。gRPC-Go 的實作在收到每一個 DATA frame 時,都會為它配置對應的追蹤結構與佇列空間——問題是這筆「每個 frame 都要付出的管理成本」跟 frame 實際承載的資料量無關。
攻擊者可以刻意把本來一個正常大小的 payload,故意切成上百萬個小到只有 1 位元組的 DATA frame 送出。由於視窗額度算的是資料位元組總量,這種切法完全不會觸發流量控制限制,但伺服器端仍要為每一個 frame 各自配置一份追蹤結構與緩衝區。多工(multiplexing)特性又讓同一條連線上可以同時開多個 stream 各自灌入這種碎片化流量,把記憶體開銷再放大一輪,最終導致執行期 panic 或 OOM,形成遠端阻斷服務。這與另一類近期公開的 HTTP/2 記憶體放大攻擊(如透過 HPACK 壓縮搭配零視窗卡住連線的手法)概念類似,但 gRPC-Go 這個漏洞單純是「frame 數量本身」造成的管理開銷,跟標頭壓縮無關。
受影響版本
google.golang.org/grpc≤ 1.83.0:受影響google.golang.org/grpc1.83.1:已修復
修補與緩解
修補方式是接收緩衝區壓實(receive buffer compaction):當同一個 stream 累積的小型連續緩衝區開銷超過一定比例,就自動把它們合併成較大的、來自共用記憶體池的緩衝區,而不是每個碎片各自持有一份獨立配置。這個機制預設啟用,官方也留了一個逃生閥,可用環境變數 GRPC_GO_EXPERIMENTAL_ENABLE_RECEIVE_BUFFER_COMPACTION=false 關閉。對應修補合併於 PR grpc/grpc-go#9331(「Restrict memory overhead of buffering small data frames」)與 #9333,並隨附在 v1.83.1 的正式 release 中。
MLflow 的 statsmodels flavor 漏掉一道防線,pickle 反序列化保護形同虛設
GitHub Security Advisory GHSA-gqvg-gmmx-x4hm · 2026-07-27
MLflow 修補了一個目前尚未取得 CVE 編號、GHSA 編號為 GHSA-gqvg-gmmx-x4hm 的漏洞,受影響版本是 2.1.0 到 3.14.x,已在 3.15.0(2026 年 7 月 27 日合併修補)修復。問題核心是:即使管理者已經把安全開關 MLFLOW_ALLOW_PICKLE_DESERIALIZATION 設為 False,只要模型是用 mlflow.statsmodels flavor 存的,這道防線完全不會生效,攻擊者可以藉此在載入模型時觸發任意程式碼執行。GitHub 公告將其歸類為 CWE-502(反序列化不受信任的資料),CVSS 評為 8.8 高風險。
漏洞機制
MLflow 支援多種模型「flavor」,像 sklearn 這類 flavor 在載入模型、要對序列化檔案做 pickle.load 之前,都會先檢查 MLFLOW_ALLOW_PICKLE_DESERIALIZATION 這個環境變數,沒開就直接拋例外拒絕載入。statsmodels flavor 的 _load_pyfunc/_load_model 卻整段漏掉這個檢查,直接呼叫 statsmodels.iolib.smio.load_pickle() 反序列化模型檔案。
攻擊情境是:攻擊者把一份宣告為 statsmodels flavor、內含惡意 pickle 內容的 MLmodel 制品,上傳到任何可寫入的 artifact store(例如共用的 MLflow tracking server 或 model registry)。之後只要有任何流程呼叫 mlflow.pyfunc.load_model() 去載入這個模型——即使當下的環境已經把安全開關關閉——statsmodels 這條路徑仍然會不做任何檢查就執行 pickle 反序列化,而 Python pickle 天生就能在反序列化過程中執行任意程式碼。
受影響版本
- mlflow
2.1.0~3.14.x:statsmodels flavor 載入路徑受影響 - mlflow
3.15.0起:已修復
修補與緩解
修補 PR mlflow/mlflow#24686 在 mlflow/statsmodels/__init__.py 補上跟其他 flavor 一致的守門檢查,只有在安全開關開啟、或執行環境是 Databricks runtime / model serving 時才放行反序列化:
def _load_model(path):
if (
not MLFLOW_ALLOW_PICKLE_DESERIALIZATION.get()
and not is_in_databricks_runtime()
and not is_in_databricks_model_serving_environment()
):
raise MlflowException(
"Deserializing model using pickle is disallowed, but this model is saved "
"in pickle format. The workaround is to set environment variable "
"'MLFLOW_ALLOW_PICKLE_DESERIALIZATION' to 'true'."
)PR 同時在 tests/statsmodels/test_statsmodels_model_export.py 補上參數化測試,驗證安全開關關閉時,無論是走 mlflow.statsmodels.load_model 還是 pyfunc.load_model 都會被擋下。升級到 3.15.0 以上即可套用這道守門檢查;來源不受信任的 artifact store 仍應視為可執行任意程式碼的風險來源,不因單一環境變數而完全解除。