
CVE-2026-49060 を再現するための Docker ベースのラボ。WooCommerce 用 Hippoo Mobile App WordPress プラグインにおける認証されていない権限昇格。脆弱なバージョン 1.9.4 とパッチ適用済みの 1.9.5 を Python PoC で比較します。
このリポジトリには、WordPressプラグイン Hippoo Mobile App for WooCommerce に影響を及ぼす不正確な権限割り当ての脆弱性 CVE-2026-49060 を再現・検証するためのローカル Docker ラボが含まれています。
脆弱な動作は、Hippoo の複製された REST API 名前空間を通じて公開されます:```text /wc-hippoo/v1/ext/
In the vulnerable target, an unauthenticated visitor can access a cloned WordPress REST users route and can update the administrator user's password through an unauthenticated HTTP request. In the patched target, the same request is blocked with `403 Forbidden`.
This lab compares two Hippoo versions:
| Service | Hippoo version | Purpose | URL |
| --------- | -------------: | ---------------------------- | ----------------------- |
| `vuln` | 1.9.4 | 脆弱性のある比較対象 | `http://localhost:8081` |
| `patched` | 1.9.5 | パッチ適用済みの比較対象 | `http://localhost:8082` |
実証される脆弱性チェーンは次のとおりです:```text
Unauthenticated visitor
→ Hippoo cloned REST namespace
→ /wc-hippoo/v1/ext/wp/v2/users/<id>
→ vulnerable permission handling allows access
→ unauthenticated GET exposes user data
→ unauthenticated POST can update the selected user's password
→ patched version blocks the same request with 403 Forbidden
このラボは、Hippoo 1.9.4 と Hippoo 1.9.5 を使用して、脆弱性のあるバージョンとパッチ適用済みバージョンの認可動作を検証します。
このラボは意図的にローカルの Docker サービスのみを対象としています。外部システムを標的とせず、永続化、Web シェル、マルウェア、外部コールバックも含みません。
| 主張 | 証拠 | このラボでの検証方法 |
|---|---|---|
CVE-2026-49060 は、WooCommerce 向け Hippoo Mobile App のバージョン 1.9.4 までに影響します。 | 公開アドバイザリは、Hippoo <= 1.9.4 / 1.9.4 までが影響を受けると特定しています。 | References セクションを確認し、vuln サービスのバージョンを比較します。 |
Hippoo 1.9.5 は、パッチ適用済みの比較対象として使用されます。 | 公開アドバイザリのメタデータは、影響を受ける範囲に対するパッチ適用済みバージョンとして 1.9.5 を特定しています。 | docker compose logs init-vuln init-patched を実行し、初期化されたプラグインのバージョンを確認します。 |
| 脆弱な動作は、Hippoo のクローンされた REST 名前空間を通じて公開されます。 | Hippoo は外部 REST ルートを /wc-hippoo/v1/ext/ の下に再登録します。 | python3 poc/poc.py http://127.0.0.1:8081 http://127.0.0.1:8082 を実行します。 |
このラボでは、Hippoo 1.9.4 はクローンされたユーザールートへの未認証アクセスを許可します。 | ラボの PoC は、http://127.0.0.1:8081/?rest_route=/wc-hippoo/v1/ext/wp/v2/users/1 から 200 OK を受け取ります。 | 8081 に対して読み取り専用の検証コマンドを実行します。 |
このラボでは、Hippoo 1.9.5 は同じ未認証リクエストをブロックします。 | ラボの PoC は、http://127.0.0.1:8082/?rest_route=/wc-hippoo/v1/ext/wp/v2/users/1 から 403 Forbidden を受け取ります。 | 8082 に対して読み取り専用の検証コマンドを実行します。 |
| このローカルラボでは、脆弱なターゲットは未認証の POST を通じて管理者パスワードを更新できます。 | アクティブな PoC は、--update-password を使用したときに脆弱なターゲットから 200 OK を受け取ります。 | python3 poc/poc.py --update-password http://127.0.0.1:8081 を実行します。 |
| パッチ適用済みのターゲットは、未認証のパスワード更新リクエストをブロックします。 | Hippoo 1.9.5 は、同じクローンされたユーザールートに対して Forbidden 応答を返します。 | 両方のターゲットに対してアクティブな検証を実行します。 |
| PoC は HTTP のみです。 | poc/poc.py は HTTP リクエストのみを送信し、Docker、WP-CLI、またはコンテナ API を呼び出しません。 | poc/poc.py を確認します。 |
このラボでは、公開アドバイザリが 1.9.4 までのバージョンを影響を受けると特定しているため、Hippoo 1.9.4 を脆弱な比較対象として使用します。
このラボでは、公開アドバイザリのメタデータが実証された影響範囲に対する修正バージョンとして 1.9.5 を特定しているため、Hippoo 1.9.5 をパッチ適用済みの比較対象として使用します。
公開されている CVE-2026-49060 レコードは、この問題を大まかに不正確な権限割り当て / 権限昇格(Incorrect Privilege Assignment / Privilege Escalation)として説明しています。このラボは、Hippoo 1.9.4 で観測可能な認可動作に焦点を当て、Hippoo 1.9.5 と比較します。
この README の根本原因の要約は、ラボで使用されている脆弱な Hippoo バージョンとパッチ適用済み Hippoo バージョンのソース比較に基づいています。
このラボは、Hippoo のすべてのルートをテストすることを主張するものではありません。クローンされた WordPress ユーザー REST ルートに焦点を当てています:```text /wc-hippoo/v1/ext/wp/v2/users/
The lab does not demonstrate:
* persistence,
* web shell upload,
* arbitrary command execution,
* external callbacks,
* malware behavior,
* attacks against non-lab systems,
* or post-compromise activity beyond the local password update validation.
## Root Cause Summary
The root cause is a permission logic flaw in Hippoo's role and permission handling.
Hippoo exposes cloned WordPress and WooCommerce REST routes under its own namespace:```text
/wc-hippoo/v1/ext/
ルートクローン動作は、クローンされたルートが元のルートの認可要件を維持または強化しなければならないため、セキュリティ上重要です。クローンされたルートが寛容な権限コールバックを受け取った場合、認証と認可を必要とするRESTエンドポイントに未認証ユーザーが到達できる可能性があります。
関連するルートクローン動作は、次のパターンに従います:```php function re_register_external_routes() { $server = rest_get_server(); $endpoints = $server->get_routes();
$new_namespace = $this->hippoo_namespace . '/ext';
foreach ($endpoints as $route => $handlers) {
if (strpos($route, $this->hippoo_namespace) === 0) {
continue;
}
foreach ($handlers as $handler) {
$default_permission_callback = array($this, 'is_user_wordpress_admin');
$permission_callback = apply_filters(
'hippoo_extension_permission_check',
$default_permission_callback,
$route,
$handler
);
register_rest_route(
$new_namespace,
$route,
array(
'methods' => $methods,
'callback' => $handler['callback'],
'args' => $handler['args'],
'permission_callback' => $permission_callback,
)
);
}
}
}
意図されたセキュリティモデルは次のとおりです:```text
Original protected REST route
→ cloned into Hippoo namespace
→ permission callback still denies unauthenticated access
脆弱な動作が発生するのは、Hippoo 1.9.4 が2つの異なる状態に対して同じ戻り値を使用するためです:```text
administrator / unrestricted access
unauthenticated visitor / no user
Hippoo `1.9.4` では、ログイン中のWordPressユーザーがいない場合、権限ヘルパーは `null` を返します:```php
public static function get_user_permissions()
{
$user = wp_get_current_user();
if (empty($user) || !$user->exists()) {
return null;
}
if (in_array('administrator', (array) $user->roles)) {
return null; // Full access
}
$settings = get_option('hippoo_permissions_settings', []);
foreach ((array) $user->roles as $role) {
if (!isset($settings[$role])) {
continue;
}
return $settings[$role];
}
return null; // Full access
}
脆弱なバージョンは null も許可されたものとして扱います:```php
private function has_role_access($section, $key = null)
{
$perms = self::get_user_permissions();
if ($perms === null) {
return true; // admin or unrestricted
}
if (empty($perms['general']['enable_access'])) {
return false;
}
}
This creates the vulnerable data flow:```text
Unauthenticated visitor
→ no WordPress user exists
→ get_user_permissions() returns null
→ has_role_access() treats null as allowed
→ cloned REST route permission can become permissive
→ unauthenticated request reaches sensitive REST endpoints
問題は、単にRESTルートが存在することではありません。問題は、権限判定が認証されていない訪問者を誤って無制限として扱う可能性があることです。
修正版では、これらの状態が分離されています。
Hippoo 1.9.5 では、認証されていない訪問者は null の代わりに false を返します:```php
public static function get_user_permissions()
{
$user = wp_get_current_user();
if (empty($user) || !$user->exists() || !is_user_logged_in()) {
return false;
}
if (in_array('administrator', (array) $user->roles)) {
return null; // Full access
}
$settings = get_option('hippoo_permissions_settings', []);
foreach ((array) $user->roles as $role) {
if (isset($settings[$role])) {
return $settings[$role];
}
}
return false; // No access
}
修正された認可チェックは、その後明示的に `false` を拒否します:```php
private function has_role_access($section, $key = null)
{
$perms = self::get_user_permissions();
if ($perms === null) {
return true; // admin
}
if ($perms === false) {
return false;
}