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;
}
}
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 viafile_get_contents(). No authentication, origin validation, or URL allowlisting is performed.Affected Component
File:
plugin/Live/standAloneFiles/saveDVR.json.php, lines 5-28Root Cause
$streamerURLis set directly from$_REQUEST['webSiteRootURL']with no validation.file_get_contents()call. There is no check forhttp/httpsscheme only, no private IP blocking, and no domain allowlist.Exploitation
Part 1: Basic SSRF (Internal Network Access)
The server fetches:
While the appended path may cause a 404 on the metadata service, the attacker can also use this for:
webSiteRootURL=http://192.168.1.X:PORT/— differentiate open/closed ports by response time and error messages.webSiteRootURL=http://internal-service/— reach services behind the firewall.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:That returns:
{"error": false, "response": {"key": "attacker_controlled_value"}}The attacker sends:
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:$DVRFile = "{$hls_path}{$key}";— used inexec()at line 80 (thoughescapeshellarg()is applied to the path components)$DVRFileTarget = "{$tmpDVRDir}" . DIRECTORY_SEPARATOR . "{$key}.m3u8";— used withoutescapeshellarg()in:exec("echo \"{$endLine}\" >> {$DVRFileTarget}");exec("ffmpeg -i {$DVRFileTarget} -c copy -bsf:a aac_adtstoasc {$filename} -y");exec("rm -R {$tmpDVRDir}");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::) is allowed by the regex and has special meaning in some shell contexts and FFmpeg input specifiers.Impact
$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.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:If the parameter must remain for backward compatibility, validate it:
Apply
escapeshellarg()to all variables used inexec()calls, including$DVRFileTargetat lines 119, 157, and$tmpDVRDirat line 167.