
CVE-2026-56011を再現・検証するためのDockerベースのラボ。WordPress用MapPress Mapsの認証なしXSS脆弱性で、脆弱なターゲットとパッチ適用済みターゲットを比較できます。
このリポジトリには、WordPress用MapPress Mapsに影響を与える未認証のクロスサイトスクリプティング(XSS)脆弱性であるCVE-2026-56011を再現・検証するためのローカルDockerラボが含まれています。
MapPress Maps for WordPressは、WordPressのページや投稿内に地図をレンダリングするために使用されるWordPressプラグインです。この脆弱な動作は、mappress=embedリクエストパラメータを介して到達可能なiframeマップレンダリングパスに影響を与えます。
このラボでは、2つのMapPressバージョンを比較します。
| サービス | MapPressバージョン | 目的 | URL |
|---|---|---|---|
| vuln | 2.97.3 | 脆弱な比較対象 | http://localhost:8081 |
| patched | 2.97.4 | パッチ適用済みの比較対象 | http://localhost:8082 |
このローカルラボで実証される検証パスは次のとおりです。```text Unauthenticated browser request → GET /?mappress=embed → request supplies a crafted name value → vulnerable target renders name into an unquoted id attribute → injected onclick handler becomes a standalone HTML attribute → clicking the rendered MapPress component triggers alert(1) → patched target keeps the payload inside a quoted and escaped id attribute → clicking the rendered component does not trigger alert(1)
脆弱なターゲットはこの手動ブラウザURLを使用します:```text
http://localhost:8081/?mappress=embed&name=cve56011%20onclick%3Dalert%281%29&width=400px&height=300px&zoom=5¢er=0%2C0
想定される脆弱性の結果:```text Click on the rendered MapPress component → alert(1) pops up
パッチ適用済みのターゲットは、MapPress 2.97.4 に対して同じペイロードを使用します:```text
http://localhost:8082/?mappress=embed&name=cve56011%20onclick%3Dalert%281%29&width=400px&height=300px&zoom=5¢er=0%2C0
期待されるパッチ適用後の結果:```text Click on the rendered MapPress component → no alert appears
このラボは意図的に手動ブラウザ検証のみを使用します。PoC スクリプト、ブラウザ自動化、認証情報の窃取、外部コールバック、マルウェア、永続化、侵害後の活動、または外部システムへの攻撃は含まれません。
## 検証済みの事実
| 主張 | 証拠 | このラボでの検証方法 |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
| CVE-2026-56011 は WordPress 用 MapPress Maps に影響します。 | 公開アドバイザリは、影響を受ける WordPress プラグインを WordPress 用 MapPress Maps と特定しています。 | 参考文献セクションを確認し、両方の Docker ターゲットにインストールされているプラグインを検査します。 |
| このラボの脆弱な比較バージョンは MapPress 2.97.3 です。 | `vuln` サービスは `MAPPRESS_VERSION: 2.97.3` を使用してプラグインをビルドします。 | `docker-compose.yml` と `vuln/Dockerfile` を検査します。 |
| このラボのパッチ適用済み比較バージョンは MapPress 2.97.4 です。 | `patched` サービスは `MAPPRESS_VERSION: 2.97.4` を使用してプラグインをビルドします。 | `docker-compose.yml` と `patched/Dockerfile` を検査します。 |
| MapPress 2.97.4 で関連する iframe エスケープ修正が導入されました。 | 2.97.4 の公式プラグイン変更ログには `Added: escape in iframe` と記載されています。 | 公式の WordPress プラグイン変更ログを確認し、脆弱なソースとパッチ適用済みソースを比較します。 |
| 脆弱なソースは、マップ名を引用符なしで Web コンポーネントの `id` にレンダリングします。 | 2.97.3 では、`mappress_map.php` は `<mappress-map id={$name} ...>` をレンダリングします。 | 2.97.3 のソースと 2.97.4 のソースを比較します。 |
| パッチ適用済みソースは、`id` の値を引用符で囲み、エスケープします。 | 2.97.4 では、`mappress_map.php` は `esc_attr($name)` を使用して `id="` をレンダリングします。 | 2.97.3 と 2.97.4 のパッチ差分を比較します。 |
| iframe パスは認証なしで到達可能です。 | MapPress は `$_GET['mappress']` が存在するときに `template_redirect` を登録します。 | ログインせずにブラウザから `/?mappress=embed...` をリクエストします。 |
| iframe パスはリクエストからマップ属性を読み取ります。 | `template_redirect()` は `$_GET` をマップ引数にマッピングし、iframe レンダラーを呼び出します。 | `mappress.php` を検査し、手動 URL を再現します。 |
| 脆弱なターゲットは、`name` を介した属性インジェクションを許可します。 | 細工された name 値は、引用符なしの `id` 属性から抜け出して `onclick=alert(1)` になる可能性があります。 | 脆弱な手動 URL を開き、レンダリングされた MapPress コンポーネントをクリックします。 |
| パッチ適用済みターゲットは、テストされた属性インジェクション動作をブロックします。 | パッチ適用済みの出力は、完全なペイロードを引用符で囲まれた `id` 属性内に保持します。 | パッチ適用済みの手動 URL を開き、レンダリングされた MapPress コンポーネントをクリックします。 |
## 前提と不明点
このラボでは、公開アドバイザリが 2.97.3 までのバージョンを影響を受けるものとして特定しており、ソース差分がそのバージョンにおける脆弱な引用符なし属性レンダリングを示しているため、脆弱な比較ターゲットとして MapPress 2.97.3 を使用しています。
このラボでは、公開アドバイザリが 2.97.4 を修正版として特定しており、公式の変更ログが iframe パスにエスケープが追加されたと述べているため、パッチ適用済み比較ターゲットとして MapPress 2.97.4 を使用しています。
テスト対象の脆弱な動作は、認証なしの iframe レンダリングパスです:```text
GET /?mappress=embed&name=<crafted-value>
このラボでは、無害なアラートペイロードの手動ブラウザ実行に焦点を当てています:```text name=cve56011 onclick=alert(1)
このラボは、保存型(stored)配信チェーンを証明しようとするものではありません。一部の公開アドバイザリでは、この脆弱性を保存型XSSとして分類しています。このリポジトリは、ソースコード上で確認されたiframeレンダリングシンクと、未認証の`mappress=embed`ルートを通じてローカルで再現可能な脆弱版とパッチ適用版の動作の差異に焦点を当てています。
このラボでは、以下は実証しません:
* 保存型ペイロードの永続化
* WordPressアカウントの侵害
* 管理者セッションの窃取
* nonceの窃取
* 外部コールバック
* ブラインドXSSの収集
* 認証情報の窃取
* データベースのダンプ
* マルウェア
* 永続化
* またはラボ外のシステムへの攻撃
手動ブラウザ検証により、セキュリティ上重要なレンダリングの差異が証明されます:```text
MapPress 2.97.3:
crafted name value becomes executable onclick attribute
MapPress 2.97.4:
crafted name value remains inside the quoted id attribute
CVE-2026-56011 の根本原因は、MapPress が iframe マップ出力パス内で Web コンポーネントをレンダリングする際に、マップ name 値の出力エンコーディングが不正確であることです。
脆弱なコードパスは、リクエストからマップレンダリング属性を受け入れ、最終的にカスタム HTML 要素をレンダリングします:```html <mappress-map ...>
MapPress 2.97.3 では、マップ名は引用符なしで、属性コンテキストのエスケープなしで `id` 属性に直接挿入されます:```php
return "<div></div>\r\n<mappress-map id={$name} {$atts}>\r\n$pois\r\n</mappress-map>\r\n";
これは、値がHTML属性コンテキストで使用されるため安全ではありません。攻撃者がnameを制御できる場合、スペースを含む値が意図したid値を終了させ、新しい属性を導入する可能性があります。
脆弱な動作は次のように要約できます:```text Attacker sends unauthenticated iframe request → name = cve56011 onclick=alert(1) → MapPress sanitizes the value as text → sanitized text is still unsafe for an unquoted HTML attribute → renderer outputs id=cve56011 onclick=alert(1) → onclick becomes a standalone event handler attribute → user clicks the rendered component → JavaScript executes
重要な点は、一般的なテキストのサニタイズは、正しい出力エスケープと同じではないということです。
脆弱なコードはマップ名に対して `sanitize_text_field()` を使用していますが、その値が引用符なしのHTML属性に対して安全になるわけではありません。HTML属性では、スペースは属性を区切るため意味を持ち続けます。
したがって、セキュリティ上の問題は次のとおりです。```text
User-controlled input
+ unquoted HTML attribute context
+ missing esc_attr()
= attribute injection and XSS
パッチ適用版では、id 値を引用符で囲んでエスケープするようにレンダリングが変更されます:```php
return "
パッチ適用後の動作は、次のように要約できます:```text
Attacker sends the same crafted name value
→ MapPress renders id="cve56011 onclick=alert(1)"
→ onclick remains text inside the id value
→ no standalone event handler attribute is created
→ clicking the component does not execute alert(1)
セキュリティの教訓は:```text Sanitize on input if needed, but always escape on output for the exact output context. For HTML attributes in WordPress, use esc_attr() and quote attribute values.
## ソースコード分析
ソースレベルでの問題は、MapPress 2.97.3 と MapPress 2.97.4 を比較することで確認された。
主要なレンダリングシンクは以下の場所にある:```text
mappress_map.php
脆弱なバージョンは、name 値を引用符なしの id 属性としてレンダリングします:```php
$name = (isset($vars['name']) ? $vars['name'] : 'noname');
return "
パッチ適用版は属性を引用符で囲み、値をエスケープします:```php
$name = (isset($vars['name']) ? $vars['name'] : 'noname');
return "<div></div>\r\n<mappress-map id=\"" . esc_attr($name) . "\" {$atts}>\r\n$pois\r\n</mappress-map>\r\n";
リクエストに mappress クエリパラメータが含まれる場合、iframe ルートが登録されます:```php
if (isset($_GET['mappress']))
add_action('template_redirect', array(CLASS, 'template_redirect'));
iframeリクエストハンドラは、クエリパラメータをマップ引数にマッピングします:```php
$args = array_map(function($arg) {
if ($arg == 'true')
return true;
if ($arg == 'false')
return false;
return $arg;
}, $_GET);
ハンドラはその後、マップオブジェクトを作成またはロードし、リクエスト引数で更新します:```php $map = new Mappress_Map(); $map->update($args); $map->layout = 'left'; echo self::get_iframe($map); die();
The iframe helper renders the map content:
iframe ヘルパーはマップのコンテンツをレンダリングします:```php
$content = $map->display(null, true);
The display() パスは、指定された空でない name 値を保持します:```php
if (empty($this->name)) {
$this->name = (defined('DOING_AJAX') && DOING_AJAX) ? "mapp" . uniqid() : "mapp$div";
$div++;
}
これは、リクエストで提供された `name` 値が脆弱なレンダリングシンクに到達できることを意味します。