1. 簡介
Hash-based Message Authentication Codes (HMAC) 被廣泛用於保護兩個共享 secret key 的各方之間的無狀態交換,其安全保證建立在一個隱含假設上:雙方必須簽署並驗證完全相同的位元組序列 [1] 。當簽署的訊息是透過串接數個邏輯上獨立的欄位所建立時,該假設便默默依賴於這些欄位如何被分隔。若系統簽署的是欄位的一種正規化形式,卻驗證另一種正規化形式,則能夠影響欄位邊界的攻擊者可能產生兩個在密文上等價、卻對應到不同應用層意義的訊息,即使底層的 secret key 從未被揭露。此報告分析此類缺陷的真實案例,由 Wordfence 揭露為 CVE-2026-76581,這是 WPMU DEV Dashboard plugin(版本 ≤ 5.0.1)中的 authentication bypass,讓未經驗證的攻擊者能夠在啟用 Hub Single Sign-On (SSO) 的網站上取得管理員 session [1] [2] 。
2. 系統概觀
WPMU DEV Dashboard 將自行託管的安裝連接到供應商的 Hub 服務,並提供以兩個 AJAX actions 實作的 SSO 登入路徑:
wdpsso_step1
與
wdpsso_step2
。由於訪客在 SSO 開始時依定義尚未通過驗證,因此這兩個動作都被註冊為未驗證(
nopriv
)端點,並位於
WPMUDEV_Dashboard_Ajax
類別之中:
- private array $nopriv_actions
- = array(
- 'wdpunauth',
- 'wdpsso_step1',
- 'wdpsso_step2',
- );
程式片段 1 — 兩個 SSO 階段皆可在未先進行 WordPress authentication 的情況下存取,使整個信任決策完全落在每個步驟內部執行的 HMAC 檢查上 [1] 。
此設計在功能上是必要的,但這意味著兩個 endpoints 必須獨立重建並對「簽署了什麼」達成一致。Step 1(
authenticate_sso_access_step1()
)發出一個新的 token、設定 state cookie,並對四個值——token、hashed state、redirect target 與 site domain——進行無分隔符或長度前綴的串接後產生外送 signature:
- $token = uniqid() . '-' . microtime( true );
- WPMUDEV_Dashboard::$settings->set( 'active_token', $token, 'sso' );
- $hashed_pre_sso_state = hash_hmac( 'sha256', $pre_sso_state, $api_key );
- $domain = $this->network_site_url();
- $outgoing_hmac = hash_hmac( 'sha256', $token . $hashed_pre_sso_state . $redirect . $domain, $api_key );
程式片段 2 — Step 1 簽署
token || state || redirect || domain
,並將產生的 HMAC 連同除了 API key 以外的所有明文輸入交還給呼叫者
[1]
。
3. 正規化落差
此漏洞並非 HMAC-SHA256 本身的弱點,也非 API key 保密性的問題——缺陷完全在於 step 1 簽署的內容與 step 2 驗證的內容之間的不一致。
authenticate_sso_access_step2()
僅從三個欄位重建預期的 signature,並默默捨棄 domain:
- $token = $sso_access_data['token'] ?? '';
- $pre_sso_state = $sso_access_data['pre_sso_state'] ?? '';
- $redirect = $sso_access_data['redirect'] ?? '';
- $verifying_hmac = hash_hmac( 'sha256', $token . $pre_sso_state . $redirect, $api_key );
- $is_valid = hash_equals( $incoming_hmac, $verifying_hmac );
程式片段 3 — Step 2 驗證
token || state || redirect
,這是刪去 domain 欄位後的串接,省略了 程式片段 2 中驗證的 domain 欄位
[1]
。
由於無分隔符的字串串接並非一對一(injective),同一個輸出位元組字串可以由欄位值的多種指派方式產生。若攻擊者在 step 1 請求時使用空的 redirect,簽署的訊息會塌縮為
token || state || "" || domain
,這在位元組上與
token || state || domain
完全相同。當攻擊者稍後將 step 1 以明文回傳的 domain 值作為
redirect
參數(而非原本綁定的 redirect 欄位)送出時,該位元組字串正是 step 2 所期望的內容。因此 HMAC 在對其自身輸入的語意不同解釋下成功驗證,攻擊者卻從未得知 API key;step 1 實際上成為 step 2 訊息空間的 signing oracle
[1]
。
into the redirect field A->>S2: Submit token, state, hmac, redirect = domain S2->>S2: verify = HMAC(token || state || redirect, key) Note over S2: verify == hmac (same bytes,
different field meaning) S2->>WP: wp_set_auth_cookie(mapped_userid) WP-->>A: Authenticated session (admin, if Hub-mapped)
圖 1 — 欄位轉移重放:step 1 回傳的 domain 值被重新送出為 step 2 的 redirect 值,因而在重新解釋的訊息上產生相同的 HMAC [1] 。
4. 為何輔助控制無法阻止攻擊
此 plugin 確實實作了 anti-replay 措施——綁定至 hashed 值的 state cookie,以及防止 token reuse 的 monotonic token-timestamp 檢查——但這兩項控制僅保護 token 與 state 欄位,而非歧義的 domain/redirect 邊界。由於 step 1 會向任何訪客發出這些值,並在同一回應中設定對應的 cookie,攻擊者使用完全新鮮、合法發出的材料即可滿足所有檢查;輔助邏輯中沒有任何部分能偵測到 redirect 欄位現在承載的是 domain 的位元組,而非實際的 redirect URL。
5. 修復
供應商的修復透過使兩個階段變為有狀態而非純粹計算式,消除了歧義性:step 1 產生的 HMAC 現在會在伺服器端持久化,而 step 2 會明確拒絕任何等於已儲存 step-1 值的 incoming signature,從而關閉兩個 endpoints 之間的 oracle 關係。
- // Step 1: persist the signature so step 2 can recognize a step-1 artifact.
- $outgoing_hmac = hash_hmac( 'sha256', $token . $hashed_pre_sso_state . $redirect . $domain, $api_key );
- WPMUDEV_Dashboard::$settings->set( 'step1_hmac', $outgoing_hmac, 'sso' );
- // Step 2: reject the request if the incoming signature is literally
- // the value step 1 produced — it cannot be a genuine Hub response.
- $step1_hmac = WPMUDEV_Dashboard::$settings->get( 'step1_hmac', 'sso', '' );
- if ( hash_equals( $step1_hmac, $incoming_hmac ) ) {
- wp_die( 'Invalid SSO authentication response.' );
- }
程式片段 4 — 此修補並未改變串接方案本身;它加入一個 out-of-band 檢查,使 step-1 產生的 HMAC 永遠無法被重放為 step-2 回應 [1] 。
這是務實的緩解措施,而非結構性修復:兩種訊息格式仍是無界的串接,仍可能彼此碰撞或與未來欄位碰撞,但阻止直接重放精確的 step-1 artifact,關閉了使利用成為可能的特定 oracle 關係。
6. 與先前發現的關聯
WPMU DEV Dashboard 先前曾釋出另一個獨立的 authentication-bypass 缺陷,於版本 5.0.1 修補,其中供應商遠端的
WDP-AUTH
Hub handler 在未連線的安裝上接受以已知空 key 計算的偽造 signature
[3]
。該缺陷與此處分析的缺陷僅在影響類別上相同;先前問題是不同程式碼路徑上的 key-management 失敗,而 CVE-2026-76581 之所以在第一個 bug 的修復後仍持續存在,正是因為它涉及已連線的網站、
wdpsso_*
actions,以及正確保管的 secret key。同一 plugin 中兩個獨立程式碼路徑的重複發生,說明由數個未經驗證、鬆散耦合的 request handlers 組成的 authentication 邏輯難以全面推理,因為每個 handler 可能滿足其局部不變量,而 handlers 的組合卻不滿足。
7. 討論
底層弱點可推廣至此 plugin 之外:任何簽署
a || b || c
卻未使用固定長度編碼、長度前綴或 unambiguous 分隔符的 protocol,只要攻擊者能影響一個欄位的長度或內容並觀察或控制另一個欄位,就容易遭受 field-boundary shifting 攻擊。正規緩解措施包括為每個欄位加上長度前綴、使用保證不會出現在欄位內容中的分隔符,或先獨立雜湊每個欄位再組合 digests。同一邏輯操作的兩個驗證 routine 在欄位組成上出現分歧,也顯示出規格與程式碼審查的落差:若簽署與驗證共用單一正規的訊息建構函式,此差異在結構上便不可能發生,而僅會在安全稽核時被觀察到。
8. 結論
CVE-2026-76581 顯示,當簽署的訊息格式在每個建構或檢查它的各方之間未以正規且 unambiguous 的方式定義時,僅有 HMAC 正確性是不夠的。在 Hub SSO 對應至管理員的 WPMU DEV Dashboard 安裝上,
wdpsso_step1
與
wdpsso_step2
之間四欄位對三欄位的不一致,讓未經驗證的攻擊者能夠取得完整的管理員 session,並且在存在如 plugin 或 theme 編輯器這類可寫入程式碼的介面時,進一步提升至 remote code execution。版本 5.0.2 透過將 step 2 綁定至 step 1 產生的特定 artifact,緩解了立即的重放路徑,但更廣泛的教訓——用於密碼學簽署的串接式訊息建構必須正規化一次並處處重用——遠超出此單一 plugin。
References
- Wordfence Argus Finds Critical Authentication Bypass in WPMU DEV Dashboard Plugin
- CVE-2026-76581 Record
- WPMU DEV Dashboard <= 5.0.0 - Authentication Bypass to Arbitrary Plugin Installation (Remote Code Execution) via Forged WDP_AUTH HMAC on ?wpmudev-hub= Endpoint
- FIPS 198-1: The Keyed-Hash Message Authentication Code (HMAC)