1. 簡介

報告提供了 CVE-2025-9501 的詳細技術分析,這是 W3 Total Cache (W3TC) WordPress 外掛程式(2.8.13 版本之前)中發現的未經認證的遠端程式碼執行 (RCE) 漏洞。此漏洞源於頁面快取機制中動態內容解析的不安全實作。我們剖析了有問題的程式碼路徑、分析了攻擊的先決條件,並說明了攻擊流程,強調了 W3TC_DYNAMIC_SECURITY 常數和外掛程式的動態函式執行功能所扮演的關鍵角色。本分析純粹著重於命令注入缺陷及其攻擊鏈的技術層面。

百萬網站危機:CVE-2025-9501 如何讓 W3 Total Cache 淪為遠端程式碼執行跳板 | 資訊安全新聞

1. 簡介

W3 Total Cache 外掛程式是 WordPress 最廣泛部署的效能最佳化工具之一,擁有超過一百萬次的活躍安裝。其核心功能涉及快取頁面以減少伺服器負載並改善回應時間。一個關鍵的漏洞,CVE-2025-9501,被確認為未經認證的命令注入缺陷,可能允許 Threat actor 在主機伺服器上執行任意 PHP 程式碼 [1] 。此漏洞在特定的 Configuration 條件下被歸類為 Pre-Auth RCE,因此對其進行分析對於瞭解現代網路應用程式安全風險至關重要。

2. 漏洞分析:不安全的動態內容解析

RCE 漏洞的根源在於外掛程式用於處理快取頁面中動態內容的功能,特別是透過 mfunc (macro function) 指令。此功能允許開發人員嵌入一小段 PHP 程式碼,這些程式碼在提供快取頁面時執行,以確保某些元素保持動態。此缺陷的核心位於 PgCache_ContentGrabber 類別中的 _parse_dynamic_mfunc 函式。

2.1. 不安全的 eval() 呼叫

_parse_dynamic_mfunc 函式直接負責執行嵌入在 mfunc 標籤內的程式碼。該函式的實作顯示直接呼叫了危險的 eval() 函式,該函式將字串作為 PHP 程式碼執行 [1] 。這是發生命令注入的 Sink。

  1. public function _parse_dynamic_mfunc( $matches ) {
  2. $code1 = trim( $matches[1] );
  3. $code2 = trim( $matches[2] );
  4. $code = ( $code1 ? $code1 : $code2 );
  5. if ( $code ) {
  6. $code = trim( $code, ';' ) . ';';
  7. try {
  8. ob_start();
  9. $result = eval( $code ); // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
  10. $output = ob_get_contents();
  11. ob_end_clean();
  12. } catch ( \Exception $ex ) {
  13. $result = false;
  14. }
  15. // ... error handling ...
  16. }
  17. // ...
  18. return $output;
  19. }

如程式碼片段所示,變數 $code (源自動態內容標籤的正規表達式匹配項 ( $matches[1] $matches[2] ))被直接傳遞給 eval() 。這種設計模式,雖然實現動態功能,但如果輸入沒有得到適當的清理或控制,就會造成嚴重的安全漏洞。

2.2. W3TC_DYNAMIC_SECURITY 的作用

_parse_dynamic_mfunc 的執行由 _parse_dynamic 函式觸發,該函式使用 preg_replace_callback 來查找和處理頁面緩衝區中的 mfunc 標籤。關鍵是,用於匹配的正規表達式是使用一個常數 W3TC_DYNAMIC_SECURITY 建構的 [1] 。必須在 WordPress Configuration (例如, wp-config.php )中明確定義此常數,動態內容功能才會啟動。

  1. public function _parse_dynamic( $buffer ) {
  2. // The W3TC_DYNAMIC_SECURITY constant should be a unique string and not an int or boolean.
  3. if ( ! defined( 'W3TC_DYNAMIC_SECURITY' ) || empty( W3TC_DYNAMIC_SECURITY ) || 1 === (int) W3TC_DYNAMIC_SECURITY ) {
  4. return $buffer;
  5. }
  6. $buffer = preg_replace_callback(
  7. '~(.*)~Uis',
  8. array(
  9. $this,
  10. '_parse_dynamic_mfunc',
  11. ),
  12. $buffer
  13. );
  14. // ...
  15. return $buffer;
  16. }

正規表達式模式是 ~(.*)~Uis 。 Threat actor 必須知道 W3TC_DYNAMIC_SECURITY 的值,才能建構出正規表達式將匹配的有效 Payload。這個要求充當了攻擊的部份障礙,將漏洞從一個微不足道的 RCE 轉變為一個依賴於資訊洩露或弱/預設機密值的漏洞。

3. 利用 Path 和先決條件

利用 CVE-2025-9501 需要目標系統滿足三個主要條件 [1]

  1. 知道 W3TC_DYNAMIC_SECURITY : Threat actor 必須知道在 wp-config.php 中定義的機密字串。
  2. 頁面快取啟用: W3TC 頁面快取功能必須處於啟動狀態,因為這會透過 process_cached_page_and_exit 觸發 _parse_dynamic 的執行。
  3. 未經認證的內容送出: Threat actor 必須能夠將 malicious payload mfunc 標籤注入到將被快取的一個頁面中。在 Pre-Auth RCE 的 Context 中,這通常意味著必須為未經認證的使用者啟用留言功能,允許 Threat actor 送出包含 Payload 的留言。

3.1. 攻擊流程圖

下圖說明了導致 RCE 的事件序列,強調了關鍵步驟和依賴關係。

%%{init: {'themeVariables': { 'fontSize': '12px'}}}%% graph TD A[Attacker Submits
Malicious Comment] --> B{Comment Stored
in DB & Cached}; B --> C{User/Attacker Requests
Cached Page}; C --> D["W3TC:
process_cached_page_and_exit"]; D --> E{"W3TC:
_parse_dynamic(buffer)"}; E -- Check
W3TC_DYNAMIC_SECURITY --> F{Regex Match:
mfunc tag}; F --> G["W3TC:
_parse_dynamic_mfunc(matches)"]; G -- $code from matches --> H["PHP:
eval($code)"]; H --> I["Remote
Code
Execution (RCE)"]; subgraph Prerequisites P1[W3TC_DYNAMIC_SECURITY
Known] P2[Page Cache Enabled] P3[Unauthenticated
Comments Enabled] end P1 & P2 & P3 --> A;

圖表註釋: 流程始於 Threat actor 注入 Payload 的能力 (A),這取決於三個先決條件 (P1、P2、P3)。然後,Payload 由 W3TC 的快取機制處理 (D, E),其中已知的安全常數 (P1) 對於正規表達式成功解析 malicious payload mfunc 標籤 (F) 至關重要。這直接導致不安全的 eval() 呼叫 (H) 和隨後的 RCE (I)。

3.2. Payload 建構

一個成功的 Payload 必須符合 mfunc 標籤結構並包含有效的 PHP 程式碼。假設機密常數是 rcesec ,一個典型的 RCE Payload 如下所示 [1]

echo passthru($_GET[1337])

Payload 分析:

  • : 開啟標籤,其中包含必要的 W3TC_DYNAMIC_SECURITY 值 ( rcesec )。
  • echo passthru($_GET[1337]) : 要執行的 PHP 程式碼。 passthru() 用於執行外部程式並顯示原始輸出,而 $_GET[1337] 允許 Threat actor 透過 URL 參數傳遞命令(例如, ?1337=id )。
  • : 關閉標籤,同樣需要機密常數。

此 Payload 一旦經 _parse_dynamic_mfunc 處理,就會執行在 1337 GET 參數中提供的命令被執行,從而實現 RCE。

4. 進階 Payload 執行環境

透過 eval() 執行任意程式碼的機制是一個典型的命令注入向量。在更廣泛的 WordPress 安全環境中,使用此類動態執行方法來執行 Payload 是一個經常出現的主題。例如,在其他 WordPress 後門情境中,經常採用 ROT13 和 base64 編碼等技術來模糊 Payload 的來源和內容,通常將最終程式碼儲存在資料庫的 wp_options 表中以規避基於 檔案 系統的偵測 [2] 。雖然 W3TC 漏洞的 Payload 直接注入到快取頁面內容中,但動態程式碼執行 ( eval() ) 的底層原理仍然是 Threat actor 最終階段的關鍵推動因素。

此漏洞強調了一個基本安全原則:任何允許將使用者可控的輸入作為程式碼執行的功能,即使受到「機密」 金鑰 的限制,也會引入高風險的攻擊面。那麼,整個系統的安全性就會降級為單個常數的機密性。如果常數洩露、被猜到或設定為預設值,Pre-Auth RCE 立即變得可被利用。

5. 結論

W3 Total Cache 中的 CVE-2025-9501 是一個嚴重的命令注入漏洞,在特定但常見的 WordPress Configuration 下允許未經認證的 RCE。技術上的根本原因是在動態內容解析邏輯中不安全地使用了 eval() 。利用依賴於 Threat actor 能夠將特製的 mfunc 標籤注入到快取頁面中,這需要知道 W3TC_DYNAMIC_SECURITY 常數。本分析強調了安全編碼實務的重要性,特別是避免使用像 eval() 這樣的函式來處理受使用者影響的 資料 ,以及網站管理員需要確保所有安全常數都設定為強大、獨一無二的值。