
WPForms <= 2.0.2.1 における未認証の反射型XSS — 属性名の位置における CVE-2026-88996 の Smart Tag エスケープのバイパス
影響対象: WPForms – AI Form Builder for WordPress(無料/ライト版)、バージョン 2.0.2.1 — 開示時点での最新版(CVE-2026-88996 の修正を含むバージョンでもある)。Pro エディションは Smart Tag レンダラーを共有している。
分類: 未認証の反射型クロスサイトスクリプティング(CWE-79)、JavaScript の実行を確認済み。CVE-2026-88996 / GHSA-8j5g-9vmv-9mmw の不完全な修正によるもの。
発見日: 2026-09-27 ·
Patchstack への報告日: 2026-09-27 ·
却下: 2026-09-28(「この問題は、サイト管理者が HTML 属性名があるべき場所に裸の Smart Tag を記述した場合にのみ発生する……デフォルトの設定でも、誰かが現実的に記述するようなパターンでもない」— 技術的な妥当性については争われていない)·
公開日: 2026-09-28。
CVE-2026-93485 ファミリーの反射型 XSS に対する 2.0.2.1 の修正は、マークアップに置換される Smart Tag の値をエスケープする(SmartTags::escape_for_attribute() が引用符と空白をエンコードする)。これは、置換された値が引用符で囲まれた属性値の内部に収まる場合には常に有効である。フォーム作成者が確認メッセージ内の属性名の位置に裸の Smart Tag を配置した場合 — 例えば:
<input {field_id="3"}>
引用符も空白も含まない送信済みフィールド値、たとえば
onmouseover=alert(1) はそのまま挿入され、新たな有効なイベントハンドラ
属性となります。エスケープ処理は = をエンコードせず、属性名には引用符も
空白も不要です。その後、匿名の送信者に返される確認応答には次が含まれます:
<input onmouseover=alert(1)>
admin-ajax.php(action=wpforms_submit、wp_ajax_nopriv_*)への匿名送信は、ログアウトユーザー向けに設計上nonce不要であるため、これは完全に未認証の反射型XSSです。任意のJavaScriptが、onmouseover=eval(String.fromCharCode(...)) を介して単一トークンの制約内で実行されます。
同じフォーム上で、親CVEのペイロード形状 — 引用符で囲まれた値の内側から引用符を破るもの、例えば x" onfocus=alert(1) autofocus y=" — は、すべての位置で正しくエスケープされています(" /   エンコードが確認されています)。2.0.2.1のエスケープは、属性名の配置を除くすべての箇所で機能します。これは同じ置換シンクに対する不完全な修正であり、別個の機能上の欠陥ではありません。
# 1. WordPress + WPForms lite 2.0.2.1, create a form with a single-line text field (id 3)
# 2. Set the form's Confirmation message to:
# A: <input value="{field_id="3"}"> B: <input {field_id="3"}>
# 3. Run the PoC (stdlib-only Python 3):
python3 poc.py --url http://target.example/form-page/
期待される出力:
[+] field 3 reflects into the confirmation message
[payload (bypass position)] occurrences: LIVE=1 ESCAPED/INERT=2
LIVE -> ...<input onmouseover=alert(poc93485)>...
[control] parent-CVE payload escaped everywhere: YES (fix works at value positions)
=== VERDICT: VULNERABLE — payload reaches attribute-NAME position unescaped ===
poc.py はフォーム、フィールド、および反射フィールドを自動検出し、匿名で送信し、
すべての出現箇所を LIVE(属性名へのインジェクション)と ESCAPED(引用符で囲まれた値、
親 CVE の形状)に分類します。終了コード 0 = 脆弱。
フォームの管理者が作成した確認メッセージには、属性名の位置にあるマークアップ内に、 入力を反射するベアの Smart Tag が含まれている必要があります。Patchstack の研究者はこれを 非現実的な作成パターンと評価しましたが、それでもこれは文書化された機能上の位置であり、 エスケープはまさにマークアップコンテキストでの置換を安全にするために存在しており、作成者は エラーを受け取らず、静かなインジェクションのみが発生します。
マークアップコンテキストのエスケープで = をエンコードするか、確認マークアップを
コンテキストに応じて解析し、Smart Tag の置換をテキスト位置および引用符付き属性値の位置に
限定して許可してください。
poc.py — 自己完結型の PoC(サーバー側の手順なし、依存関係なし)poc.sh — curl ベースの最小バリアント認可されたテストおよび教育目的のみを対象としています。報告者は、調整済みの提出の試みが 拒否された後にこれを公開しました。技術的な発見については異議は唱えられていません。
Ashish Kunwar (@D0rkerdevil)