更新日期:2026 年 7 月 23 日。WordPress 官方於 7 月 17 日發布 7.0.2 安全更新,修補 CVE-2026-60137 與 CVE-2026-63030。這兩項弱點可以串成由 REST API 進入、利用 WP_Query SQL Injection,最終造成未授權遠端程式碼執行(RCE)的攻擊鏈;CISA 已在 7 月 21 日將兩者列入 Known Exploited Vulnerabilities(KEV),代表已有實際利用證據。
社群將這組 WordPress Core 漏洞鏈稱為 wp2shell。本文不提供可直接攻擊公開網站的利用 payload,而是從資料流、根因、受影響版本、日誌獵捕、主機檢查與修補驗證等角度,整理網站管理者與 Blue Team 現在應採取的行動。
60 秒重點摘要
- CVE-2026-60137:
WP_Query的author__not_in參數未被正確清理;當外掛或佈景主題把不可信輸入傳入這個參數時,可能形成 SQL Injection。 - CVE-2026-63030:WordPress REST API batch endpoint 存在路由解讀衝突;與 CVE-2026-60137 串接後,可由未驗證攻擊者達成 SQL Injection,並進一步造成 RCE。
- 已知遭利用:兩項漏洞均已加入 CISA KEV,不應只依 CVSS 或例行維護週期排序。
- 立即修補:7.0.x 升至 7.0.2 以上、6.9.x 升至 6.9.5 以上;6.8.x 雖不受第二段鏈影響,仍應升至 6.8.6 以上修補 SQL Injection。
- 修補不等於事件結束:如果網站曾以受影響版本對外服務,還要檢查 REST API 請求、管理員帳號、核心檔案、外掛/佈景主題檔案與持久化跡象。
漏洞資料與受影響版本
| 項目 | CVE-2026-60137 | CVE-2026-63030 |
|---|---|---|
| 弱點類型 | SQL Injection(CWE-89) | Interpretation Conflict/路由解讀衝突(CWE-436) |
| 主要元件 | WP_Query::author__not_in | REST API batch endpoint |
| WPScan CVSS 3.1 | 5.9 Medium | 9.8 Critical |
| 是否列入 CISA KEV | 是,2026-07-21 加入 | 是,2026-07-21 加入 |
| 攻擊價值 | 特定資料流下可注入 SQL | 與前者串接後可造成未驗證 RCE |
| WordPress 分支 | 受影響版本 | 最低安全版本 | 說明 |
|---|---|---|---|
| 6.8 | 6.8.0~6.8.5 | 6.8.6 | 受 CVE-2026-60137 影響;官方表示不受 CVE-2026-63030 影響 |
| 6.9 | 6.9.0~6.9.4 | 6.9.5 | 同時受兩項漏洞影響,可形成完整漏洞鏈 |
| 7.0 | 7.0.0~7.0.1 | 7.0.2 | 同時受兩項漏洞影響,可形成完整漏洞鏈 |
| 7.1 Beta | 7.1 beta1 | 7.1 beta2 | 測試版本亦有對應修補 |
| 6.7 以下 | 官方公告為不受影響 | 仍建議升級至受支援版本 | 「不受這兩項 CVE 影響」不代表舊分支整體安全 |
CVSS 資料目前存在來源差異:WPScan 對 CVE-2026-60137 評為 5.9,CISA ADP 則提供 9.1;CVE-2026-63030 的 WPScan 評分為 9.8,CISA ADP 為 7.5。實務上不應在這裡被分數差異拖慢:兩者皆已被 CISA 判定為主動利用,而且完整漏洞鏈可能造成未驗證 RCE,修補優先度應視為緊急。
wp2shell 攻擊鏈如何形成?
這不是「單一 SQL Injection 自動等於拿到主機 Shell」,而是兩個信任邊界失效後形成的鏈。理解資料如何穿過各元件,比記住漏洞名稱更重要。
- 外部輸入進入 REST API:WordPress 的 batch endpoint 允許一個 HTTP 請求包裝多個內部 REST 請求,以降低往返次數。
- 外層與內層對路由的理解不一致:CVE-2026-63030 屬於 Interpretation Conflict。安全檢查、路由比對與實際處理元件沒有對同一份請求得出一致結論,讓原本不應抵達的參數組合穿越邊界。
- 不可信值抵達查詢建構器:
WP_Query的author__not_in正常語意是「排除哪些作者 ID」,其預期資料型態應是整數陣列。 - SQL 參數未被正確正規化:CVE-2026-60137 的核心問題,是該參數在特定資料流中未先被安全地轉換、驗證,再放進 SQL 查詢條件。
- SQL Injection 擴大影響:攻擊者可影響資料庫查詢。當它與 batch-route confusion 組合時,WordPress 官方與 CVE 公告指出最終可達遠端程式碼執行。
因此,防禦上不能只搜尋一段固定 exploit 字串。真正需要監控的是「異常 batch API 請求 → 非預期查詢參數 → 資料庫或網站狀態改變」這條行為鏈。
CVE-2026-60137:為什麼 author__not_in 會變成 SQL Injection?
author__not_in 是 WP_Query 的標準參數,用來排除特定作者的文章。正常程式應傳入作者 ID 陣列,例如 [2, 6]。在安全設計中,資料抵達 SQL 層之前至少要經過三步:確認型態是陣列、把每個值轉成受限整數、再由查詢建構流程產生 SQL。
這項漏洞被稱為 facilitated SQL injection,原因是 Core 中的弱點通常需要另一個元件把外部可控資料傳給 WP_Query 才能觸發。在 6.8 分支,網站是否可從外部利用,取決於外掛、佈景主題或自訂 REST endpoint 的資料流;但在 6.9 與 7.0 的受影響版本中,CVE-2026-63030 提供了可與它串接的 Core 路徑,使風險從「需要特定呼叫情境」升高為完整的未驗證攻擊鏈。
這也是為什麼只停用某一個外掛不是可靠修補。漏洞根因位於 WordPress Core,正確處理方式是升級 Core;外掛盤點則屬於降低攻擊面與事件調查的一部分。
CVE-2026-63030:REST API batch-route confusion 原理
WordPress REST API 能把內部請求交由 rest_do_request() 處理,batch endpoint 則把多個子請求包在一次外部請求中。這種設計本身不是漏洞,但它同時存在「外部 batch 路由」、「子請求宣告的路由」以及「實際被 REST Server 配對的 endpoint」等多層解讀。
當不同層對請求路徑、方法或參數來源的解釋不一致,前一層做出的權限與輸入判斷,就不一定能保護後一層真正執行的操作。CVE-2026-63030 正是這類 parser/router differential:攻擊者利用解讀差異,把危險資料送到原本不應抵達的查詢路徑,再與 CVE-2026-60137 串接。
Blue Team 應把它視為「路由與參數的信任邊界失效」,而不是只封鎖某一條已公開的 PoC。固定字串規則可以爭取時間,但不能取代修補與事件調查。
從 WordPress 7.0.1 原始碼追蹤 SQL Injection 資料流
要理解 CVE-2026-60137,必須先把外部 REST 參數和內部 SQL 查詢變數接起來。WordPress 的 Posts REST Controller 會把公開參數 author_exclude 映射成 WP_Query 使用的 author__not_in:
// class-wp-rest-posts-controller.php
$parameter_mappings = array(
'author' => 'author__in',
'author_exclude' => 'author__not_in',
);
if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) {
$args[ $wp_param ] = $request[ $api_param ];
}
正常 REST 流程會先根據 endpoint schema 驗證 author_exclude。它被宣告成「整數陣列」,因此一般請求中的字串應在抵達 Controller callback 前被拒絕。問題在於第二項漏洞能讓「負責驗證的 handler」與「最後執行的 handler」錯位,於是字串值仍可能被上面這段映射送進 WP_Query。
7.0.1 的 vulnerable code:只在輸入是陣列時清理
// WordPress 7.0.1:src/wp-includes/class-wp-query.php
if ( ! empty( $query_vars['author__not_in'] ) ) {
if ( is_array( $query_vars['author__not_in'] ) ) {
$query_vars['author__not_in'] =
array_unique( array_map( 'absint', $query_vars['author__not_in'] ) );
sort( $query_vars['author__not_in'] );
}
$author__not_in = implode( ',', (array) $query_vars['author__not_in'] );
$where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
}
逐行看會發現四個條件剛好連在一起:
is_array()不是單純的型態判斷,而是決定 sanitization 會不會執行的 gate。- 輸入是陣列時,每個值會經過
absint(),只能留下非負整數。 - 輸入若是字串,整段
array_map( 'absint', ... )被略過。 - 後面的
(array)只會把字串包成「含一個字串元素的陣列」,不會把字串轉成整數;implode()後內容仍保持原樣,最後直接串進 SQL。
下面用不具攻擊性的輸入展示 PHP 型態問題:
$input = '2,not-an-id';
// (array) 只改容器,不會清理元素內容。
$wrapped = (array) $input; // array( '2,not-an-id' )
$ids = implode( ',', $wrapped ); // "2,not-an-id"
$where = "post_author NOT IN ($ids)";
// 7.0.1 產生:post_author NOT IN (2,not-an-id)
這個示例只會形成錯誤 SQL,不是 exploit;它證明的重點是:非數字內容確實能穿過舊程式碼進入 SQL grammar。只要把 not-an-id 換成能改變查詢語意的 SQL fragment,就從型態錯誤變成 SQL Injection。根因不是 implode() 本身,而是程式把「只有在某型態下才清理」與「無條件字串拼接 SQL」放在一起。
7.0.2 如何修:先正規化成 ID list,再組 SQL
// WordPress 7.0.2
$author__not_in_id_list =
wp_parse_id_list( $query_vars['author__not_in'] );
if ( count( $author__not_in_id_list ) > 0 ) {
sort( $author__not_in_id_list );
$where .= sprintf(
" AND {$wpdb->posts}.post_author NOT IN (%s) ",
implode( ',', $author__not_in_id_list )
);
$query_vars['author__not_in'] = $author__not_in_id_list;
}
wp_parse_id_list() 會先用 wp_parse_list() 接受陣列、逗號或空白分隔的字串,再對每個元素執行 absint():
function wp_parse_id_list( $input_list ) {
$input_list = wp_parse_list( $input_list );
return array_unique( array_map( 'absint', $input_list ) );
}
修補後,安全性不再依賴呼叫者是否正確傳入陣列。即使輸入是 '2,not-an-id,6',經過正規化後也只會剩下整數集合,例如 [2, 0, 6];排序並串接後只含數字與逗號,SQL fragment 無法保留。
這段修補沒有改用 $wpdb->prepare()。對一般可變字串,prepared statement 仍是首選;但這裡處理的是可變長度 ID list,WordPress 選擇先把每個元素壓縮到「非負整數」這個安全語意域,再組成 IN (...)。只要所有元素真的都經過 absint(),這種組法不會留下 SQL metacharacter。更新後再把清理完成的 list 寫回 $query_vars,則是為了讓 cache key 與實際 SQL 使用同一份 canonical value,避免相同語意因輸入格式不同產生不穩定快取鍵。
從原始碼拆解 CVE-2026-63030 的 index desynchronization
serve_batch_request_v1() 先把每個子請求轉成 WP_REST_Request,再建立三個依位置互相對應的陣列:
$requests[$i]:原始子請求或解析錯誤。$matches[$i]:路由比對後得到的 route 與 handler。$validation[$i]:該 handler 的參數驗證結果。
這種設計的安全前提是三個陣列永遠保持相同索引。7.0.1 在處理無法解析的 path 時破壞了這個不變條件:
// 7.0.1:錯誤存在於 $requests 與 $validation,卻沒有放進 $matches。
foreach ( $requests as $single_request ) {
if ( is_wp_error( $single_request ) ) {
$has_error = true;
$validation[] = $single_request;
continue;
}
$match = $this->match_request_to_handler( $single_request );
$matches[] = $match;
}
假設第一個子請求的 path 解析失敗,陣列會變成:
| 原始索引 | $requests | $matches 實際內容 |
|---|---|---|
| 0 | WP_Error(parse_path_failed) | 沒有建立索引 0 的錯誤占位 |
| 1 | Request A | $matches[0] = Handler A |
| 2 | Request B | $matches[1] = Handler B |
第二階段執行時卻使用原始 request index 取 handler:
foreach ( $requests as $i => $single_request ) {
if ( is_wp_error( $single_request ) ) {
continue;
}
// i 仍是 $requests 的原始索引。
$match = $matches[ $i ];
list( $route, $handler ) = $match;
$result = $this->respond_to_request(
$single_request, $route, $handler, $error
);
}
因此 Request A 位於 $requests[1],卻讀到 $matches[1] 的 Handler B。這不是單純回應順序錯誤,而是安全上下文被拆開:
- HTTP method 來自 Request A。
- query/body parameters 來自 Request A。
- route schema、permission callback 與最終 callback 卻可能來自 Handler B。
- 先前驗證的是某一個 handler 允許的參數,最後執行的卻是另一個 handler。
公開 POC 會使用兩層 batch,而且兩層位移的用途不同。外層把一個帶有 requests body 的 POST /wp/v2/posts request 錯位到 /batch/v1 handler;該 request 先按照 posts request 通過驗證,所以它的 inner requests 沒有經過 batch schema 的 method allow-list。內層再送出 GET /wp/v2/posts/999999?author_exclude=...:這份 request 先以 posts item route 的 schema 驗證,item schema 不會消費 collection-only 的 author_exclude、orderby 與 per_page,接著才因索引位移被 posts collection 的 get_items() handler 執行。Controller 最後把未經 collection schema 驗證的 scalar 映射到 author__not_in,餵入第一項 SQL Injection。
用不包含可利用 payload 的抽象流程表示:
batch[0] = INVALID_PATH
batch[1] = Request_A
batch[2] = Request_B
requests = [ Error, Request_A, Request_B ]
matches = [ Handler_A, Handler_B ] // 少一格,開始左移
dispatch requests[1] with matches[1]
// 實際結果:Request_A 的資料被交給 Handler_B
為何只補一行就能阻止 route confusion?
// 7.0.2
if ( is_wp_error( $single_request ) ) {
$has_error = true;
$matches[] = $single_request; // 新增:維持相同 cardinality
$validation[] = $single_request;
continue;
}
新增的不是一般錯誤紀錄,而是安全上的 positional invariant。只要 $requests 每增加一個元素,$matches 與 $validation 也一定增加一個對應元素,後面的 $matches[$i] 就不會引用下一個請求的 handler。錯誤物件本身也會沿相同索引傳遞,避免錯誤被其他請求「跨格吸收」。
第二層修補:禁止 REST top-level cycle re-entry
WordPress 另外修改 rest_api_loaded() 與 WP_REST_Server::serve_request()。原因是 batch 子請求本來就應該走 dispatch();如果 dispatch 進行中又能啟動一次完整的 serve_request(),就會產生不合法的 top-level REST cycle。要注意的是,Icex0 公開 POC 的兩層 batch 是透過 handler 錯位執行,沒有直接呼叫 rest_api_loaded() 或 serve_request();因此這項 commit 應視為同批更新中的 REST state-machine hardening,而不是宣稱該 POC 必須依靠 reentrancy 才能成功。
// 7.0.2:rest_api_loaded()
if ( isset( $GLOBALS['wp_rest_server'] )
&& $GLOBALS['wp_rest_server'] instanceof WP_REST_Server
&& $GLOBALS['wp_rest_server']->is_dispatching()
) {
return;
}
// 7.0.2:WP_REST_Server::serve_request()
if ( $this->is_dispatching() ) {
return false;
}
dispatch() 會把目前請求放進 $dispatching_requests stack,完成後再 pop;is_dispatching() 則檢查這個 stack 是否非空。修補後,只要 stack 顯示已有 REST request 正在處理:
rest_api_loaded()在執行define()、serve_request()與最後的die()前直接返回。serve_request()本身也有第二道 guard,避免其他呼叫路徑繞過前一層。- 合法內部子請求仍可使用
dispatch(),所以不是粗暴地禁止所有 nested REST 操作。
這是 defense in depth:第一個修補維護陣列索引,第二個修補限制 dispatch state machine 的合法轉移。即使未來 batch parsing 又出現其他錯誤,內部子請求也不能任意升格成新的 top-level REST request。
SQL Injection 為何最後被分類為 RCE?
這裡要區分「SQL parser 直接執行作業系統命令」與「應用程式接管後取得程式碼執行」。Icex0 POC 將 UNION primitive 明確標示為 read-only:它以 UNION SELECT 偽造完整的 wp_posts row,讓 WordPress 在同一次 request 中把該查詢結果轉成 WP_Post 並放入 object cache。orderby=none 用來移除會破壞 UNION 的尾端排序;per_page=500 則讓查詢維持 full-row、non-split 模式。這些假資料列不是直接寫進資料庫,但它們被 WordPress 渲染時仍能觸發應用層副作用。
POC 的 SQLi-to-admin bridge 先渲染包含 的假文章,使 WordPress 建立三筆真實 oembed_cache posts;再經 SQLi 找回它們的 ID。下一個 poisoned batch 以假 WP_Post object graph 將這些真實 ID 重新解讀成 customize_changeset、nav_menu_item 與 request-hook shape,最後讓同一批 request 抵達 POST /wp/v2/users 並建立管理員。攻擊者登入該新帳號後,才使用 WordPress 正常的 plugin upload 功能放入臨時 webshell 並執行命令。因此完整鏈是「未驗證 SQLi/假文章與 object-cache poisoning → 應用層物件與路由混淆 → 建立管理員 → 已驗證外掛上傳 → PHP RCE」,不是 SQLi 直接執行系統命令,也不依賴 MySQL FILE 權限。
防禦與鑑識因此不能只找 webshell:
- 資料庫層:未知管理員、密碼雜湊或 email 變更、session token、application password、active plugins 與 cron option。
- WordPress 層:登入後的外掛安裝、Theme Editor/Plugin Editor 操作、MU Plugin 與排程工作。
- 檔案層:
wp-content新增 PHP、uploads 中的可執行檔、被修改的佈景主題與外掛。 - 主機層:PHP-FPM 子程序、對外連線、系統 cron、SSH key 與其他持久化。
如何快速確認 WordPress 是否受影響
在網站根目錄執行下列唯讀或低風險 WP-CLI 指令。若主機沒有 WP-CLI,也可以從後台「工具 → 網站健康度 → 資訊」查看版本。
# 查看目前 Core 版本
wp core version
# 查看是否有 Core 更新
wp core check-update
# 驗證 Core 檔案是否符合官方 checksum
wp core verify-checksums
判讀方式:
- 7.0.0 或 7.0.1:受兩項漏洞影響,立即升級到 7.0.2 或更新版本。
- 6.9.0~6.9.4:受兩項漏洞影響,立即升級到 6.9.5 或更新版本。
- 6.8.0~6.8.5:受 CVE-2026-60137 影響,立即升級到 6.8.6 或更新版本。
- checksum 失敗:不代表一定遭入侵,但必須釐清是正常客製化、更新不完整,還是核心檔案被修改。
LonelySec 實際檢查:本站在 2026 年 7 月 23 日以 WP-CLI 確認運行 WordPress 7.0.2,並通過官方 Core checksum 驗證。這只能證明檢查當下的 Core 版本與檔案一致;仍不能取代對受影響期間日誌和 wp-content 的調查。
修補與緊急緩解方式
1. 優先升級 WordPress Core
# 先確認維護程序與可用復原點,再執行 Core 更新
wp core update
# 若資料庫需要升級
wp core update-db
# 更新後再次驗證
wp core version
wp core verify-checksums
WordPress 官方因漏洞嚴重度已對受影響版本啟用強制自動更新;但管理者仍應主動驗證版本,不要假設排程、檔案權限、版本控制或代管設定一定允許更新成功。
2. 無法立即升級時縮小攻擊面
- 在反向代理或 WAF 暫時限制對 REST batch endpoint 的外部存取,前提是先確認網站功能不依賴它。
- 監控或阻擋同時出現 batch route 與異常
author__not_in參數的請求。 - 暫停非必要、可由匿名使用者控制查詢條件的外掛、自訂 endpoint 與佈景主題功能。
- 將管理後台和主機管理介面限制在 VPN、零信任存取或可信來源。
- 無法提供可靠緩解且不能修補時,暫時下線受影響服務比持續暴露安全。
WAF 僅是緩解措施。編碼、參數巢狀結構與不同 HTTP 表示法都可能讓固定規則漏報,最終仍必須完成 Core 更新。
日誌偵測與 Threat Hunting
目前公開資訊不足以建立一條保證涵蓋所有利用變形的 IOC 規則,因此以下條件應被視為「需要調查的高訊號」,而不是單獨判定入侵。搜尋時間至少要回溯到修補前,並涵蓋網站曾運行 6.8.0+ 受影響版本的期間。
Web/Nginx/Apache 存取日誌
- 對
/wp-json/batch/v1或等價 REST 路徑的 POST 請求突然增加。 - 匿名來源在 batch 子請求中帶入
author__not_in。 - 同一來源短時間重複測試不同編碼、參數型別、路徑斜線或 HTTP 方法。
- 回應狀態由大量 4xx 轉為 2xx,或回應大小、處理時間明顯偏離基線。
- 緊接著出現登入、外掛安裝、佈景主題編輯、檔案上傳或不尋常 PHP 路徑請求。
# 範例:先找 batch endpoint 與目標查詢參數
# 請依實際日誌路徑、輪替與壓縮方式調整
zgrep -Ei '(/wp-json/batch/v1|author(__|%5[fF]%5[fF])not(__|%5[fF]%5[fF])in)' \
/var/log/nginx/access.log* /var/log/apache2/access.log* 2>/dev/null
若反向代理只記錄路徑而沒有 request body,可能看不到 batch 子請求內容。此時要結合 WAF、CDN、應用程式、資料庫慢查詢與主機稽核資料,不要因 access log 沒命中就判定安全。
WordPress 與主機端檢查
# 核心完整性
wp core verify-checksums
# 盤點管理員帳號與註冊時間
wp user list --role=administrator \
--fields=ID,user_login,user_email,user_registered
# 盤點目前啟用的外掛
wp plugin list --status=active
# 找近期新增或修改的 PHP 檔案;日期應依曝險期間調整
find wp-content -type f -name '*.php' -newermt '2026-07-17' -print
- 檢查未知管理員、應用程式密碼、API Token 與異常密碼重設。
- 檢查
wp-content/uploads是否出現 PHP、PHTML 或其他可執行檔案。 - 檢查
mu-plugins、一般外掛、目前與未啟用佈景主題中的陌生檔案。 - 檢查
wp_options中的active_plugins、siteurl、home、cron 與可疑自動載入資料。 - 檢查系統 cron、systemd timer、SSH authorized keys、Web Server 設定與 PHP-FPM pool 是否遭修改。
- 比對資料庫、檔案、WAF 與認證日誌的時間線,而不是逐項孤立判讀。
如何判斷可能已遭入侵
下列任一跡象都應提高事件等級:
- 確認受影響期間出現指向 batch endpoint 且含異常查詢參數的外部請求。
- Core checksum 不符,且差異無法由正式部署或已知客製化解釋。
- 新增未知管理員、外掛、MU Plugin、排程工作或應用程式密碼。
- uploads、cache 或暫存目錄出現可執行 PHP 檔案。
- 資料庫內容出現陌生 JavaScript、iframe、管理員帳號或選項變更。
- Web 程序產生不尋常的對外連線、子程序、壓縮檔或系統命令紀錄。
若已出現入侵跡象,不要只刪除可疑檔案後恢復服務。應先保存日誌與磁碟/記憶體證據,在乾淨環境重建 Core、外掛與佈景主題,輪替 WordPress salts、管理員密碼、資料庫密碼、SSH 金鑰、API Token 與代管平台憑證,並確認攻擊者沒有留下其他持久化管道。
給 SOC/Blue Team 的偵測邏輯
比起只建立單一字串規則,建議用關聯方式提高準確度:
- 初始存取訊號:外部來源呼叫 WordPress batch REST API,並帶有低頻或不符合本站正常功能的查詢參數。
- 應用層異常:HTTP 5xx、PHP Warning、資料庫錯誤、慢查詢或回應長度偏離基線。
- 權限/狀態變更:新增管理員、變更外掛、寫入佈景主題、修改選項或新增 cron。
- 執行訊號:Web Server/PHP-FPM 身分建立 PHP、啟動子程序或向罕見外部位址連線。
當第 1 項與後續任一訊號在短時間內發生,應提高為高優先調查。若網站業務完全不使用 batch endpoint,對它的匿名外部請求本身就具有較高調查價值。
修補後驗證清單
- WordPress 版本已達 7.0.2、6.9.5、6.8.6 或相應更新版本。
wp core verify-checksums成功。- 網站前台、登入、REST API 與必要外掛功能正常。
- CDN、Page Cache、OPcache 與容器映像沒有繼續提供舊版本檔案。
- 所有節點都已更新;負載平衡後方沒有漏掉的舊主機。
- 已完成受影響期間的 access log、WAF、WordPress 與主機稽核。
- 未發現未知管理員、PHP 檔案、MU Plugin、cron 或憑證。
- 監控已加入 batch endpoint、
author__not_in與後續狀態變更的關聯規則。
常見問題
wp2shell 是單一 CVE 嗎?
不是。它是社群對 CVE-2026-60137 與 CVE-2026-63030 組合攻擊鏈的稱呼:前者提供 WP_Query SQL Injection 弱點,後者提供 REST API batch-route confusion 路徑,組合後可能導致未驗證 RCE。
WordPress 6.8 也會直接遭到未驗證 RCE 嗎?
官方公告指出 6.8 只受第一項漏洞影響,不受第二項漏洞影響。是否能由外部利用取決於網站上的外掛、佈景主題或自訂程式是否把不可信輸入傳入該查詢參數;仍應立即升級至 6.8.6 以上。
網站已自動更新,是否就不用查日誌?
不是。CISA KEV 代表已有實際利用證據。若網站曾用受影響版本對外服務,就應確認更新完成時間,並回溯曝險期間是否有利用與持久化跡象。
停用 REST API 能否完全解決?
限制 batch endpoint 可以暫時縮小完整攻擊鏈的外部入口,但不會修復 Core 中的 SQL Injection 根因,也可能破壞網站功能。正式解法仍是升級 WordPress Core。
可以只靠 WAF 規則嗎?
不建議。WAF 能攔截已知型態並提供調查紀錄,但請求編碼、批次結構與路由差異可能造成繞過。它適合當臨時緩解與偵測層,不應取代修補。
延伸閱讀
官方參考來源
- WordPress 7.0.2 Security Release
- WordPress GHSA:CVE-2026-60137
- WordPress GHSA:CVE-2026-63030
- WordPress 7.0.1 → 7.0.2 完整原始碼差異
- 上游修補 commit:WP_Query ID 正規化
- 上游修補 commit:REST batch 陣列索引對齊
- 上游修補 commit:阻擋 REST request reentrancy
- NVD:CVE-2026-60137
- NVD:CVE-2026-63030
- CISA Known Exploited Vulnerabilities Catalog
- Searchlight Cyber wp2shell 公開曝險檢測工具(外部 checker,非 WordPress 官方服務)
- Icex0 wp2shell POC:route confusion、SQLi 與 SQLi-to-admin chain
- Patchstack 技術分析:WordPress 7.0.2 未授權 SQL Injection
- WordPress Developer Reference:WP_Query
- WordPress REST API Handbook:Requests 與 batch endpoint
更新紀錄:2026-07-23,依 WordPress 官方安全公告、GitHub Security Advisory、NVD、CISA KEV 與本站實際維運檢查,將原 CVE-2026-60137 快訊擴充為完整 wp2shell 漏洞鏈分析。






