{"id":"CVE-2026-46337","aliases":["EUVD-2026-33305","GHSA-w4qq-74h6-58wq"],"status":"published","title":"WWBN AVideo: Unauthenticated Arbitrary Image Read via Path Traversal in `view/img/image404Raw.php`","description":"WWBN AVideo is an open source video platform. In 29.0 and earlier, an unauthenticated remote attacker can read arbitrary image files anywhere on disk that the PHP user can open — including private user-profile photos that the application's normal serving wrappers gate behind ACLs, admin-uploaded thumbnails, encrypted-video poster frames, and image content under sibling-app directories reachable via .. traversal. The endpoint requires no authentication.","severity":{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-11","score":0.00455,"percentile":0.39965,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":false},"cwes":["CWE-22","CWE-284","CWE-862"],"published":"2026-05-19T16:25:27Z","modified":"2026-10-28T02:30:32Z","affected":[{"source":"ghsa","ecosystem":"Packagist","package":"WWBN/AVideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"29.0"}]}]},{"source":"osv","ecosystem":"Packagist","package":"WWBN/AVideo","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"29.0"}]}]}],"products":[{"vendor":"wwbn","product":"avideo"}],"references":[{"url":"https://github.com/WWBN/AVideo/security/advisories/GHSA-w4qq-74h6-58wq","tags":["x_refsource_CONFIRM","exploit","Exploit","Mitigation","Vendor Advisory","advisory","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-46337","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-w4qq-74h6-58wq"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/46xxx/CVE-2026-46337.json","tags":["advisory"]},{"url":"https://github.com/WWBN/AVideo","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-46337","title":"WWBN AVideo: Unauthenticated Arbitrary Image Read via Path Traversal in `view/img/image404Raw.php`","description":"WWBN AVideo is an open source video platform. In 29.0 and earlier, an unauthenticated remote attacker can read arbitrary image files anywhere on disk that the PHP user can open — including private user-profile photos that the application's normal serving wrappers gate behind ACLs, admin-uploaded thumbnails, encrypted-video poster frames, and image content under sibling-app directories reachable via .. traversal. The endpoint requires no authentication.","status":"published","published":"2026-05-29T13:03:01.529Z","modified":"2026-05-29T16:21:29.279Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T13:47:06.353366Z"}],"euvd":[{"id":"EUVD-2026-33305","description":"WWBN AVideo is an open source video platform. In 29.0 and earlier, an unauthenticated remote attacker can read arbitrary image files anywhere on disk that the PHP user can open — including private user-profile photos that the application's normal serving wrappers gate behind ACLs, admin-uploaded thumbnails, encrypted-video poster frames, and image content under sibling-app directories reachable via .. traversal. The endpoint requires no authentication.","status":"published","published":"2026-05-29T13:03:01Z","modified":"2026-05-29T16:21:29Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-02T18:21:00.772434Z"}],"ghsa":[{"id":"GHSA-w4qq-74h6-58wq","title":"AVideo: Unauthenticated Arbitrary Image Read via Path Traversal in `view/img/image404Raw.php`","description":"### Summary\nThe endpoint requires **no authentication**. An unauthenticated remote attacker can read arbitrary image files anywhere on disk that the PHP user can open — including private user-profile photos that the application's normal serving wrappers gate behind ACLs, admin-uploaded thumbnails, encrypted-video poster frames, and image content under sibling-app directories reachable via `..` traversal.\n\n### Details\n`view/img/image404Raw.php` reads the `image` GET parameter and joins it directly into a filesystem path served via `readfile()`.  `view/img/image404Raw.php` (full file, current `master` @ `0dbadbcaaa1b415c7db078a72dc4b26d9fac0485`):\n\n```php\n\u003c?php\n\n// Fetch requested image URL\n$imageURL = !empty($_GET['image']) ? $_GET['image'] : $_SERVER[\"REQUEST_URI\"];\n$rootDir = dirname(__FILE__) . '/../../';\nif ($imageURL == 'favicon.ico') {\n    $imgLocalFile = \"{$rootDir}/videos/{$imageURL}\";\n} else {\n    $imgLocalFile = \"{$rootDir}/{$imageURL}\";   // ← attacker-controlled\n}\n\nif (file_exists($imgLocalFile)) {\n    $imageInfo = getimagesize($imgLocalFile);   // ← format gate\n    if (empty($imageInfo)) {\n        die('not image');\n    }\n    // …extension → Content-Type mapping…\n    header(\"HTTP/1.0 200 OK\");\n    header('Content-Type: ' . $type);\n    header('Content-Length: ' . filesize($imgLocalFile));\n    readfile($imgLocalFile);   // ← exfil bytes\n    exit;\n}\n```\n\nIssues:\n\n1. **No authentication.** The file is reachable via direct GET; no `require` of `globals.php`, no session check, no API-key gate.\n2. **No basename / realpath / prefix containment.** `$_GET['image']`  is concatenated into `$imgLocalFile` with no `..` filtering, no `realpath()` resolution, no allowlist check against the intended `view/img/` directory.\n3. **`getimagesize()` is a magic-bytes check, not a path constraint.**  Any file on disk whose first bytes match a recognized image format (`FFD8FF` JPEG, `89504E47` PNG, `474946` GIF, `52494646…57454250` WebP) passes the gate — including images stored outside any ACL'd area of the application.\n4. **`$_SERVER[\"REQUEST_URI\"]` fallback** when `image` is empty widens the attack surface (path components in the URI itself land in `$imgLocalFile`).\n\n**Re-verified pre-submission** on 2026-05-13 against `view/img/image404Raw.php` blob SHA `c670b0faff4fbea1fd0508f179956975477d4340` — unsafe shape unchanged since first discovery on 2026-05-12.\n\n**Recommended fix** — three layered checks, any one alone is insufficient:\n\n```php\n// view/img/image404Raw.php — proposed fix\n\u003c?php\n\n$imageURL = !empty($_GET['image']) ? $_GET['image'] : '';\nif ($imageURL === '') {\n    http_response_code(400);\n    exit('bad request');\n}\n\n// 1. Reject any path-traversal segment outright.\nif (strpos($imageURL, '..') !== false\n    || strpos($imageURL, \"\\0\") !== false\n    || strpos($imageURL, '://') !== false) {\n    http_response_code(400);\n    exit('bad request');\n}\n\n// 2. Resolve to a real path and verify prefix containment under the\n//    intended image directory.\n$rootDir = realpath(dirname(__FILE__) . '/../../');\n$imgLocalFile = realpath($rootDir . '/' . $imageURL);\nif ($imgLocalFile === false\n    || (strpos($imgLocalFile, $rootDir . '/videos/') !== 0\n        \u0026\u0026 strpos($imgLocalFile, $rootDir . '/view/img/') !== 0)) {\n    http_response_code(404);\n    exit('not found');\n}\n\n// 3. Existing getimagesize() check stays as defense-in-depth.\nif (!is_file($imgLocalFile)) {\n    http_response_code(404);\n    exit('not found');\n}\n$imageInfo = @getimagesize($imgLocalFile);\nif (empty($imageInfo)) {\n    http_response_code(404);\n    exit('not image');\n}\n\n// …rest of the original Content-Type + readfile() flow unchanged…\n```\n\nDrop the `$_SERVER[\"REQUEST_URI\"]` fallback entirely; if no `image`\nparameter is provided, return 400.\n\n### PoC\n\nDiscovery probe — any HTTP client, no authentication, no cookies:\n\n```http\nGET /view/img/image404Raw.php?image=../videos/userPhoto/photo1.jpg HTTP/1.1\nHost: avideo.example.com\n```\n\nIf `videos/userPhoto/photo1.jpg` exists on the server, the response is the raw image bytes (HTTP 200, `Content-Type: image/jpeg`). The application's normal user-photo serving wrapper (which can gate by session / channel ownership) is bypassed entirely.\n\nCross-directory probe — read images outside the AVideo install root:\n\n```http\nGET /view/img/image404Raw.php?image=../../../var/www/other-app/uploads/users/admin.jpg HTTP/1.1\nHost: avideo.example.com\n```\n\nIf the PHP user has read access to a sibling app's image directory, those files are exfiltrable too.\n\nEnumeration — iterate over predictable numeric IDs:\n\n```\nGET /view/img/image404Raw.php?image=../videos/userPhoto/photo1.jpg\nGET /view/img/image404Raw.php?image=../videos/userPhoto/photo2.jpg\nGET /view/img/image404Raw.php?image=../videos/userPhoto/photo3.jpg\n...\n```\n\n…to harvest all profile images regardless of the application's intended privacy controls.\n\n### Impact\n\n**Path traversal → arbitrary image read (CWE-22 + CWE-284).** Affects any AVideo deployment running master through commit `0dbadbca` and likely every release on the supported branches. The attacker:\n\n1. **Bypasses the application's image-content ACLs.** Profile photos under `videos/userPhoto/` and admin-uploaded private thumbnails  that AVideo's normal image-serving wrappers gate by session / channel ownership become readable to any anonymous internet user.\n2. **Reads images stored outside the AVideo install root.** On shared-hosting / multi-tenant deployments, `..` traversal lets the  attacker page into sibling-app upload directories — anywhere the PHP user has read access on disk and the target file's first bytes form a valid image header.\n3. **Enables enumeration at scale.** Numeric ID schemes (`photo1.jpg`, `photo2.jpg`, …) and predictable filenames let an attacker harvest every private image on a deployment without detection (each request looks like a single 200-image-OK to the web log).\n\nBecause the read primitive is restricted to image-magic-bytes files, there is no source-code or credential exfiltration via this primitive alone — but the **privacy / GDPR exposure** is substantial on any deployment that hosts user-uploaded photos. CVSS 5.3 (Medium) reflects the limited but real confidentiality impact; many operators will rate this higher because the leaked content is user-private by intent.\n\nThis is **not** a silent-fix disclosure — the bug is still present on current `master` at submission time; the maintainer is being\nnotified of a previously-unknown issue.","status":"published","published":"2026-05-19T16:25:27Z","modified":"2026-06-09T10:28:44Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"},{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"}],"cwes":["CWE-22","CWE-284","CWE-862"],"updated_at":"2026-11-02T14:40:22.540562Z"}],"nvd":[{"id":"CVE-2026-46337","description":"WWBN AVideo is an open source video platform. In 29.0 and earlier, an unauthenticated remote attacker can read arbitrary image files anywhere on disk that the PHP user can open — including private user-profile photos that the application's normal serving wrappers gate behind ACLs, admin-uploaded thumbnails, encrypted-video poster frames, and image content under sibling-app directories reachable via .. traversal. The endpoint requires no authentication.","status":"published","published":"2026-05-29T14:16:31.52Z","modified":"2026-07-21T12:10:00.09Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"},{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium","assessor":"primary"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T14:12:21.244426Z"}],"osv":[{"id":"CVE-2026-46337","title":"WWBN AVideo: Unauthenticated Arbitrary Image Read via Path Traversal in `view/img/image404Raw.php`","description":"WWBN AVideo is an open source video platform. In 29.0 and earlier, an unauthenticated remote attacker can read arbitrary image files anywhere on disk that the PHP user can open — including private user-profile photos that the application's normal serving wrappers gate behind ACLs, admin-uploaded thumbnails, encrypted-video poster frames, and image content under sibling-app directories reachable via .. traversal. The endpoint requires no authentication.","status":"published","published":"2026-05-29T13:03:01.529Z","modified":"2026-10-28T02:30:32.325883856Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-22"],"updated_at":"2026-11-02T15:45:05.870292Z"},{"id":"GHSA-w4qq-74h6-58wq","title":"AVideo: Unauthenticated Arbitrary Image Read via Path Traversal in `view/img/image404Raw.php`","description":"### Summary\nThe endpoint requires **no authentication**. An unauthenticated remote attacker can read arbitrary image files anywhere on disk that the PHP user can open — including private user-profile photos that the application's normal serving wrappers gate behind ACLs, admin-uploaded thumbnails, encrypted-video poster frames, and image content under sibling-app directories reachable via `..` traversal.\n\n### Details\n`view/img/image404Raw.php` reads the `image` GET parameter and joins it directly into a filesystem path served via `readfile()`.  `view/img/image404Raw.php` (full file, current `master` @ `0dbadbcaaa1b415c7db078a72dc4b26d9fac0485`):\n\n```php\n\u003c?php\n\n// Fetch requested image URL\n$imageURL = !empty($_GET['image']) ? $_GET['image'] : $_SERVER[\"REQUEST_URI\"];\n$rootDir = dirname(__FILE__) . '/../../';\nif ($imageURL == 'favicon.ico') {\n    $imgLocalFile = \"{$rootDir}/videos/{$imageURL}\";\n} else {\n    $imgLocalFile = \"{$rootDir}/{$imageURL}\";   // ← attacker-controlled\n}\n\nif (file_exists($imgLocalFile)) {\n    $imageInfo = getimagesize($imgLocalFile);   // ← format gate\n    if (empty($imageInfo)) {\n        die('not image');\n    }\n    // …extension → Content-Type mapping…\n    header(\"HTTP/1.0 200 OK\");\n    header('Content-Type: ' . $type);\n    header('Content-Length: ' . filesize($imgLocalFile));\n    readfile($imgLocalFile);   // ← exfil bytes\n    exit;\n}\n```\n\nIssues:\n\n1. **No authentication.** The file is reachable via direct GET; no `require` of `globals.php`, no session check, no API-key gate.\n2. **No basename / realpath / prefix containment.** `$_GET['image']`  is concatenated into `$imgLocalFile` with no `..` filtering, no `realpath()` resolution, no allowlist check against the intended `view/img/` directory.\n3. **`getimagesize()` is a magic-bytes check, not a path constraint.**  Any file on disk whose first bytes match a recognized image format (`FFD8FF` JPEG, `89504E47` PNG, `474946` GIF, `52494646…57454250` WebP) passes the gate — including images stored outside any ACL'd area of the application.\n4. **`$_SERVER[\"REQUEST_URI\"]` fallback** when `image` is empty widens the attack surface (path components in the URI itself land in `$imgLocalFile`).\n\n**Re-verified pre-submission** on 2026-05-13 against `view/img/image404Raw.php` blob SHA `c670b0faff4fbea1fd0508f179956975477d4340` — unsafe shape unchanged since first discovery on 2026-05-12.\n\n**Recommended fix** — three layered checks, any one alone is insufficient:\n\n```php\n// view/img/image404Raw.php — proposed fix\n\u003c?php\n\n$imageURL = !empty($_GET['image']) ? $_GET['image'] : '';\nif ($imageURL === '') {\n    http_response_code(400);\n    exit('bad request');\n}\n\n// 1. Reject any path-traversal segment outright.\nif (strpos($imageURL, '..') !== false\n    || strpos($imageURL, \"\\0\") !== false\n    || strpos($imageURL, '://') !== false) {\n    http_response_code(400);\n    exit('bad request');\n}\n\n// 2. Resolve to a real path and verify prefix containment under the\n//    intended image directory.\n$rootDir = realpath(dirname(__FILE__) . '/../../');\n$imgLocalFile = realpath($rootDir . '/' . $imageURL);\nif ($imgLocalFile === false\n    || (strpos($imgLocalFile, $rootDir . '/videos/') !== 0\n        \u0026\u0026 strpos($imgLocalFile, $rootDir . '/view/img/') !== 0)) {\n    http_response_code(404);\n    exit('not found');\n}\n\n// 3. Existing getimagesize() check stays as defense-in-depth.\nif (!is_file($imgLocalFile)) {\n    http_response_code(404);\n    exit('not found');\n}\n$imageInfo = @getimagesize($imgLocalFile);\nif (empty($imageInfo)) {\n    http_response_code(404);\n    exit('not image');\n}\n\n// …rest of the original Content-Type + readfile() flow unchanged…\n```\n\nDrop the `$_SERVER[\"REQUEST_URI\"]` fallback entirely; if no `image`\nparameter is provided, return 400.\n\n### PoC\n\nDiscovery probe — any HTTP client, no authentication, no cookies:\n\n```http\nGET /view/img/image404Raw.php?image=../videos/userPhoto/photo1.jpg HTTP/1.1\nHost: avideo.example.com\n```\n\nIf `videos/userPhoto/photo1.jpg` exists on the server, the response is the raw image bytes (HTTP 200, `Content-Type: image/jpeg`). The application's normal user-photo serving wrapper (which can gate by session / channel ownership) is bypassed entirely.\n\nCross-directory probe — read images outside the AVideo install root:\n\n```http\nGET /view/img/image404Raw.php?image=../../../var/www/other-app/uploads/users/admin.jpg HTTP/1.1\nHost: avideo.example.com\n```\n\nIf the PHP user has read access to a sibling app's image directory, those files are exfiltrable too.\n\nEnumeration — iterate over predictable numeric IDs:\n\n```\nGET /view/img/image404Raw.php?image=../videos/userPhoto/photo1.jpg\nGET /view/img/image404Raw.php?image=../videos/userPhoto/photo2.jpg\nGET /view/img/image404Raw.php?image=../videos/userPhoto/photo3.jpg\n...\n```\n\n…to harvest all profile images regardless of the application's intended privacy controls.\n\n### Impact\n\n**Path traversal → arbitrary image read (CWE-22 + CWE-284).** Affects any AVideo deployment running master through commit `0dbadbca` and likely every release on the supported branches. The attacker:\n\n1. **Bypasses the application's image-content ACLs.** Profile photos under `videos/userPhoto/` and admin-uploaded private thumbnails  that AVideo's normal image-serving wrappers gate by session / channel ownership become readable to any anonymous internet user.\n2. **Reads images stored outside the AVideo install root.** On shared-hosting / multi-tenant deployments, `..` traversal lets the  attacker page into sibling-app upload directories — anywhere the PHP user has read access on disk and the target file's first bytes form a valid image header.\n3. **Enables enumeration at scale.** Numeric ID schemes (`photo1.jpg`, `photo2.jpg`, …) and predictable filenames let an attacker harvest every private image on a deployment without detection (each request looks like a single 200-image-OK to the web log).\n\nBecause the read primitive is restricted to image-magic-bytes files, there is no source-code or credential exfiltration via this primitive alone — but the **privacy / GDPR exposure** is substantial on any deployment that hosts user-uploaded photos. CVSS 5.3 (Medium) reflects the limited but real confidentiality impact; many operators will rate this higher because the leaked content is user-private by intent.\n\nThis is **not** a silent-fix disclosure — the bug is still present on current `master` at submission time; the maintainer is being\nnotified of a previously-unknown issue.","status":"published","published":"2026-05-19T16:25:27Z","modified":"2026-09-10T03:51:07.218801902Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N","level":"medium"},{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-22","CWE-284","CWE-862"],"updated_at":"2026-11-02T15:46:58.04805Z"}]}}
