CVE-2026-43875: AVideo Password Hash Leak Vulnerability
MODERATECVE-2026-43875 is a MODERATE (CVSS 6.8) password hash leak vulnerability (CWE-598: Use of HTTP Request With Sensitive Query String) affecting AVideo libraries in the Packagist ecosystem. The issue involves password hash leak in mobilemanager oauth redirect url enables account takeover. Remediation is available in the latest vendor maintenance releases.
CVE-2026-43875 at a Glance
Security Impact
CVSS: 6.8
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N).
How to Fix CVE-2026-43875
To remediate this vulnerability, upgrade all affected AVideo dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade wwbn/avideo to Latest secure release
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for wwbn/avideo
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-43875, inspect your project dependencies for AVideo:
inspect dependencies for wwbn/avideo
Analyze the terminal output to locate any version of AVideo in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than the patched release), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-598: Use of HTTP Request With Sensitive Query String, where the web application uses an http method to process a request, but the request includes sensitive information in the query string. In AVideo, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-43875 Affected AVideo Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
wwbn/avideo
|
Packagist |
≤ 29.0
|
|
References
Frequently Asked Questions
What is CVE-2026-43875?
Which AVideo versions are affected?
What version fixes CVE-2026-43875?
Is wwbn/avideo affected?
What is the CVSS score of CVE-2026-43875?
What CWE is associated with CVE-2026-43875?
How can I check Packagist dependencies for CVE-2026-43875?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-5w8w-26ch-v5cw",
"modified" : "2026-05-13T14:19:29Z",
"published" : "2026-05-05T19:08:45Z",
"aliases" : [ "CVE-2026-43875" ],
"summary" : "AVideo: Password Hash Leak in MobileManager OAuth Redirect URL Enables Account Takeover",
"details" : "## Summary\n\n`plugin/MobileManager/oauth2.php` completes an OAuth login by sending an HTTP 302 `Location: oauth2Success.php?user=<email>&pass=<HASH>` where `<HASH>` is the victim's stored password hash (`md5(hash(\"whirlpool\", sha1(password)))`) read directly from the `users` table. AVideo's own login endpoint (`objects/login.json.php`) accepts an `encodedPass=1` flag that bypasses hashing and performs a direct string comparison between the supplied value and the stored hash. Anyone who captures the redirect URL — via server logs, referrer leakage, or browser history — therefore obtains a credential equivalent to the plaintext password and can fully take over the account, including admin accounts.\n\n## Details\n\n### Sink: hash inlined in a GET redirect\n\n`plugin/MobileManager/oauth2.php:98-102`:\n\n```php\n$pass = rand();\n$users_id = User::createUserIfNotExists($user, $pass, $name, $email, $photoURL);\n$adapter->disconnect();\n$userObject = new User($users_id);\nheader(\"Location: oauth2Success.php?user=\" . $userObject->getUser() . \"&pass=\" . $userObject->getPassword());\n```\n\n`$userObject->getPassword()` returns the raw database column (`objects/user.php:159-162`):\n\n```php\npublic function getPassword()\n{\n return strip_tags($this->password);\n}\n```\n\nThe returned value is the stored password hash for the account (existing or freshly-created). It is transported to the browser as a query-string parameter in the `Location:` header, so it is written to:\n\n* Web-server access logs (`combined` / `main` log formats record the full request line including query string).\n* Upstream proxy / CDN / WAF logs.\n* Any error monitoring / APM that captures request URLs (Sentry, Datadog, New Relic defaults).\n* The victim's browser history (persistent local artifact).\n* The `Referer` header on subsequent navigation from the rendered `oauth2Success.php` page if the page or its assets load any external origin and the browser's `Referrer-Policy` is not strict.\n\n### Hash equals plaintext for login\n\n`objects/login.json.php:182-209`:\n\n```php\nif (!empty($_GET['user'])) {\n $_POST['user'] = $_GET['user'];\n}\nif (!empty($_GET['pass'])) {\n $_POST['pass'] = $_GET['pass'];\n}\nif (!empty($_GET['encodedPass'])) {\n $_POST['encodedPass'] = $_GET['encodedPass'];\n}\n...\n$user = new User(0, $_POST['user'], $_POST['pass']);\n...\n$resp = $user->login(false, @$_POST['encodedPass']);\n```\n\n`objects/user.php:1272-1279` passes `$encodedPass` to `find()`:\n\n```php\nif (strtolower($encodedPass) === 'false') {\n $encodedPass = false;\n}\n...\n$user = $this->find($this->user, $this->password, true, $encodedPass);\n```\n\n`objects/user.php:1785-1794`:\n\n```php\nif ($pass !== false) {\n if (!encryptPasswordVerify($pass, $result['password'], $encodedPass)) {\n ...\n return false;\n }\n}\n```\n\n`objects/functions.php:2312-2331`:\n\n```php\nfunction encryptPasswordVerify(#[\\SensitiveParameter] $password, $hash, $encodedPass = false)\n{\n global $advancedCustom, $global;\n if (!$encodedPass || $encodedPass === 'false') {\n $passwordSalted = encryptPassword($password);\n $passwordUnSalted = encryptPassword($password, true);\n } else {\n $passwordSalted = $password; // <- direct use, no hashing\n $passwordUnSalted = $password;\n }\n $isValid = $passwordSalted === $hash || $passwordUnSalted === $hash;\n ...\n}\n```\n\nWhen `encodedPass` is truthy, the supplied value is compared as-is against the stored hash. The captured redirect parameter `pass=<HASH>` is therefore a valid login credential when replayed with `encodedPass=1`.\n\n### Compounding factors\n\n* The redirect is a raw `Location:` (GET), not a POST — the secret is placed in a URL which is by definition non-confidential transport.\n* No CSRF token, no `state` parameter tied to the session, and no single-use token is used on `/plugin/MobileManager/oauth2.php`.\n* `login.json.php` does not require a CSRF token or captcha on the first attempt (`checkLoginAttempts()` at `objects/user.php:1282` only rate-limits after failures, and the attacker succeeds on the first try).\n* By contrast, the non-plugin flow in `objects/login.json.php:144-145` already sets session state server-side (`$userObject->login(true)`), demonstrating the project already has a safer pattern available.\n\n## PoC\n\nPrerequisites: `MobileManager` plugin enabled and at least one supported login provider (e.g. `LoginGoogle`) configured with valid keys — both are common production settings for this product.\n\n1. Victim initiates the mobile OAuth flow:\n\n ```\n GET /plugin/MobileManager/oauth2.php?type=Google\n ```\n\n2. After the victim authorizes at the provider, the server sends:\n\n ```\n HTTP/1.1 302 Found\n Location: oauth2Success.php?user=victim%40example.com&pass=9d7ab4...stored-hash...\n ```\n\n This request-line — including the password hash — is written to the web server's access log (default `combined` format) and to any upstream proxy/CDN log. It also appears in the victim's browser history.\n\n3. Attacker obtains `<HASH>` from any of those channels.\n\n4. Attacker logs in as the victim without knowing the plaintext password:\n\n ```\n curl -i -c cookies.txt \\\n 'https://target.example.com/objects/login.json.php?user=victim@example.com&pass=<HASH>&encodedPass=1'\n ```\n\n Expected response: `200 OK` with JSON containing `id`, `user`, `PHPSESSID`, `isAdmin`, `email`, and a `Set-Cookie: PHPSESSID=...` that grants full account access. The attacker can now browse, upload, modify the victim's channel, or — if the victim is an admin — access `/mvideos` and all admin endpoints.\n\n## Impact\n\n* Full account takeover of any user who has ever logged in through the MobileManager OAuth endpoint.\n* If the victim is an administrator, the attacker gains administrative control of the AVideo instance (user management, plugin config, site-wide content).\n* The exposed hash works indefinitely: it remains valid for as long as the victim does not change their password, so a one-time log/history/referrer capture yields a persistent credential.\n* Passes silently — from the application's perspective, the attacker is just a legitimate login with `encodedPass=1` (a flag the product itself uses for mobile-app \"remember me\" flows).\n\n## Recommended Fix\n\n1. Never place the password hash (or any credential-equivalent material) in a URL. In `plugin/MobileManager/oauth2.php`, mirror what `objects/login.json.php:143-146` already does for the web flow — establish the session server-side and redirect to a URL with no credentials:\n\n ```php\n $userObject = new User(0, $user, $pass);\n $userObject->login(true); // server-side session\n header(\"Location: oauth2Success.php\");\n ```\n\n2. Additionally, remove or hard-restrict the `encodedPass` branch in `objects/functions.php:2319-2329`. If a \"hash-equivalent\" credential must exist for the mobile app, replace it with a short-lived, single-use, server-issued bearer token bound to the session, rather than the persistent database hash.\n\n3. Add a `state` parameter and CSRF protection on `/plugin/MobileManager/oauth2.php` so the redirect cannot be initiated from a third-party origin.\n\n4. For defense-in-depth, strip query strings containing `pass=` from access-log formats and ensure `oauth2Success.php` sets `Referrer-Policy: no-referrer` while it is being deprecated.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "wwbn/avideo"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"last_affected" : "29.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/WWBN/AVideo/security/advisories/GHSA-5w8w-26ch-v5cw"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-43875"
}, {
"type" : "WEB",
"url" : "https://github.com/WWBN/AVideo/commit/977cd6930a97571a26da4239e25c8096dd4ecbc1"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/WWBN/AVideo"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-598" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-05-05T19:08:45Z",
"nvd_published_at" : "2026-05-11T22:22:11Z"
}
}