資安雷達 2026 年 10 月 5 日

2026-10-05 — Xray-core 憑證釘選繞過先被靜默修補,後又出現 CA 釘選缺口

primary=https://github.com/net4people/bbs/issues/672 primary=https://github.com/XTLS/Xray-core/commit/4632984b66a4f2ab3d4eda8deb9c48ac9911933c primary=https://github.com/XTLS/Xray-core/security/advisories/GHSA-5wf9-h793-w73c primary=https://github.com/XTLS/Xray-core/releases/tag/v26.2.6

Xray-core 憑證釘選繞過先被靜默修補,後又出現 CA 釘選缺口

net4people/bbs #672(漏洞回報者自述)· GHSA-5wf9-h793-w73c · 2026-10-02

Xray-core 的 pinnedPeerCertSha256 曾經只要憑證鏈裡「任何位置」有一張非 CA 憑證的雜湊吻合就放行,等於釘選檢查可以被中間人繞過。這個洞在 2026-02-06 被一個標題寫「Simplify cert's verification code」的 commit 悄悄補掉,同日的 v26.2.6 release notes 也沒有提到漏洞。

回報者 dyhkwong 在 2026-10-02 的 net4people 討論串公開經過,並指出同一功能在 2026-07-03 又被發現第二個、較窄的缺口,已由 GitHub 在同日發布 advisory GHSA-5wf9-h793-w73c(severity 標為 high,未指派 CVE)。

背景:allowInsecure 被移除後,釘選成為唯一的自簽憑證做法

依回報者整理的時間線,Xray-core 先在 2026-01-09 移除舊的 pinnedPeerCertificateChainSha256,改用 pinnedPeerCertSha256;2026-01-16 又把邏輯改成只要設了釘選,就一律跳過一般的憑證驗證,只做釘選比對。v26.2.6 的 release notes 另外寫明 TLS 移除了 allowInsecure,要求改用 pinnedPeerCertSha256 和 verifyPeerCertByName。

這個設計的前提是:釘選比對本身必須是唯一的安全防線,而且不能出錯。一旦比對邏輯有洞,就沒有第二層驗證接手。

漏洞機制:比對沒有限定在第一張憑證

看 commit 4632984 的 diff,修補前的 verifyChain 會走訪整條鏈,凡是雜湊吻合且 IsCA 為 false 的憑證都回傳 foundLeaf,呼叫端再把它當成「釘選的是葉憑證」直接放行。修補前還有一段「若 certs[0].IsCA 就把整條鏈反轉」的處理,也一併移除。

修補前修補後(4632984)
葉憑證判定鏈中任一位置的非 CA 憑證吻合即算只比對 certs[0]
CA 判定整條鏈(含首張)都會掃先略過 certs[0],其餘吻合且 IsCA 才算
鏈順序處理首張是 CA 時反轉鏈移除
// 修補前(節錄)
for _, cert := range certs {
    if hmac.Equal(GenerateCertHash(cert), pin) {
        if cert.IsCA { return foundCA, cert } else { return foundLeaf, cert }
    }
}
// 修補後
if hmac.Equal(GenerateCertHash(certs[0]), pin) { return foundLeaf, nil }
certs = certs[1:] // skip leaf

回報者的描述是:中間人可以把被釘選的憑證放在鏈中的任意位置,釘選邏輯就會判定通過。因為這條路徑已經跳過一般驗證,攻擊者不需要任何可信 CA 簽發的憑證。這段從 diff 推得的攻擊路徑細節,commit 與 advisory 都未逐步說明。

第二個缺口:CA 釘選時 ServerName 為空

回報者在 2026-07-03 提交的 GHSA-5wf9-h793-w73c 描述另一條路徑:釘選的是知名且非自簽的 CA,同時 r.Config.ServerName 為空時,certs[0].Verify(opts) 不會比對 dNSName 或 iPAddress。攻擊者只要向同一個根 CA 申請一張自己網域或 IP 的葉憑證,就能劫持連線。

Go 的 crypto/tls 要求 ServerName 與 InsecureSkipVerify 至少設一個;使用者設了釘選時 InsecureSkipVerify 恆為 true,所以 ServerName 可以是空的。多數情況下 Xray 會用伺服器位址補上,但 advisory 點名 hysteria 的 dialer 與 grpc 的 dial 兩處呼叫 GetTLSConfig() 時沒帶 tls.WithDestination(dest),其中 gRPC 在位址是 IP 時 ServerName 仍為空。

受影響版本

  • advisory 列出的受影響範圍:>= 1.260113.0, < 1.260327.1-0.20260710210335-64fada32b5b9(Go module github.com/xtls/xray-core)。
  • 首個修正版本:1.260327.1-0.20260710210335-64fada32b5b9。
  • 回報者主張第一個洞(葉憑證位置)從 v26.1.13 起存在,2026-02-06 修補;advisory 本身只涵蓋第二個缺口。

影響範圍

受影響的是設定了 pinnedPeerCertSha256 的客戶端,尤其是用它搭配自簽憑證、或釘選公開 CA 的使用者;GUI 客戶端若內嵌 Xray-core,也要確認其內核版本。沒有設釘選的人,在第一個洞的時期仍有一般憑證驗證可用。

要檢查的具體項目:設定檔裡有沒有 pinnedPeerCertSha256;有的話是否同時設了 serverName(這可以避開第二個缺口,但 advisory 未將此列為正式緩解措施);使用 hysteria 或 gRPC 傳輸時,核心是否已升到修正版本以上。

爭議:揭露方式

回報者的主要指控是維護者沒有公告漏洞,使用者無從得知該升級;Xray-core 方面的回應在本文引用的來源中沒有出現,release notes 對第一個洞只有「重要修復,請及時升級」的籠統說法。這些描述來自回報者的第一人稱文章,本文只採信可由 commit、release 與 advisory 對得上的部分,不對維護者動機下判斷。

原始來源:net4people/bbs #672、Xray-core commit 4632984、GHSA-5wf9-h793-w73c、v26.2.6 release


End of article
0
Would love your thoughts, please comment.x
()
x