GHSA-qm9p-p5pw-jrx2 AVideo: Unauthenticated Disclosure of CloneSite `myKey` via Error Echo in `cloneClient.json.php` Enables Cross-Site DB Dump of the Configured Clone Server
Summary`plugin/CloneSite/cloneClient.json.php` echoes the local CloneSite shared secret (`$objClone->myKey`, a constant `md5($global['systemRootPath'] . $global['salt'])`) into the HTTP response body on every unauthenticated request. The unauthenticated error branch was intended to reject non-admin callers without a valid key, but the rejection message interpolates the expected key before `die()`. When the victim has CloneSite configured with a remote `cloneSiteURL` (standard federation/backup setup), the leaked `myKey` is exactly the credential that authenticates the victim to that remote server's `cloneServer.json.php`, allowing the attacker to impersonate the victim and trigger a full `mysqldump` of the remote's database to the remote's public `videos/clones/` directory.Details1. The leak (`plugin/CloneSite/cloneClient.json.php:51-60`)Under PHP's web SAPI, the script-scope `$argv` global is not populated from the query string (only `$_SERVER['argv']` is populated, and only when `register_argc_argv=On`). Verified on this host (PHP 8.4.16, built-in web server):Because `empty($argv[1])` is true, line 51's `preg_replace` returns `''` and `$argv[1]` becomes `''`. Line 53 therefore enters the outer `if` (empty key). `User::isAdmin()` returns false for unauthenticated callers, so line 57 runs and echoes the contents of `$objClone->myKey` into the response body before `die()`. The response body looks like:The 32-hex prefix is the local `myKey`.2. Where `myKey` comes from (`plugin/CloneSite/CloneSite.php:67`)`myKey` is a static per-installation value generated from `systemRootPath` and `salt`. It never rotates.3. Why the leaked key is dangerous (cross-site chain)`cloneClient.json.php:75` shows `myKey` is the credential the client presents to its configured remote clone server:On the remote side, `plugin/CloneSite/cloneServer.json.php:32-42` calls `Clones::thisURLCanCloneMe($_GET['url'], $_GET['key'])`, which in `plugin/CloneSite/Objects/Clones.php:73-101` does only:For any federation pair the remote admin has approved (`status='a'`), supplying `url=<victim>&key=<leaked myKey>` passes this check. `cloneServer.json.php:86-90` then runs an unconditional `mysqldump` of every table except `CachesInDB`:The dump lands in `{videosDir}/clones/<sqlFile>`, and `videos/` is a public static directory in default AVideo deployments, so the attacker can fetch it with one more unauthenticated request.4. Not fixed by the previous `clones.json.php` hardeningCommit `160e02635`/earlier added `if (!User::isAdmin())` guards to `plugin/CloneSite/clones.json.php` (the table-management endpoint that lists server-side per-client keys, previously advisory-submitted as CWE-306). That fix does not apply to `cloneClient.json.php`, which is a separate file and discloses a structurally different secret (the local `myKey`, not the per-URL server-side keys).PoCPrerequisite: target installation has the `CloneSite` plugin enabled with a configured `cloneSiteURL` (this is the standard use: federated backup / site cloning). No authentication required.**Step 1 — leak the local `myKey` (unauthenticated GET):**Response body:The 32-hex-character prefix is `$objClone->myKey`.**Step 2 — use the leaked `myKey` to make the victim's configured remote dump its own database:**Response (truncated):**Step 3 — download the full database dump from the remote's public `videos/` directory:**This file contains every table except `CachesInDB` — `users` (including password hashes), payment records, API secrets, plugin configuration, etc.ImpactAny unauthenticated attacker can retrieve the CloneSite shared secret (`myKey`) of any AVideo installation that has the plugin enabled. `myKey` is static and never rotates on its own.When that installation is federated with a remote CloneSite server (the standard use of the plugin), the leaked key permits the attacker to impersonate the victim client to the remote. `cloneServer.json.php` on the remote performs no additional authentication, runs an unconditional `mysqldump`, and places the result under the web-accessible `videos/clones/` directory — so a single leaked `myKey` leads to a full database dump (users, password hashes, payment and plugin configuration, API credentials) of the remote partner, downloadable over HTTP.The compromise crosses the federation boundary: leaking the key on site A yields the database of site B. This is scope-changing in practice even if CVSS scope is formally `Unchanged`.The `clones.json.php` hardening (the previously reported CWE-306 fix) does not cover this path; `cloneClient.json.php` is a distinct file that exposes a structurally different credential.Recommended FixDo not echo the expected key in the rejection message, and reject non-CLI / non-admin callers cleanly. Example patch for `plugin/CloneSite/cloneClient.json.php:51-60`:Additional hardening recommended:Replace the static `myKey = md5(systemRootPath . salt)` with a randomly generated, per-installation key stored in the plugin configuration that can be rotated (see similar advice from GHSA-wqcc-qf63-c2x4 / CWE-331 on AVideo secret generation).On the remote side (`cloneServer.json.php`), consider requiring the `sqlFile` path to be unguessable (already is, via `uniqid()`) AND gating the dump behind an IP allowlist or an additional pre-shared rotating token, so that loss of a client's `myKey` does not immediately yield a full database dump.Serve `videos/clones/` with an `.htaccess`/nginx rule that denies direct HTTP access, so that even if a rogue client is authenticated, the dump is not downloadable over the web.
CVSS v3.1 score: 7.5