GHSA-w59f-67xm-rxx7 Froxlor has Local File Inclusion via path traversal in API `def_language` parameter leads to Remote Code Execution
SummaryThe Froxlor API endpoint `Customers.update` (and `Admins.update`) does not validate the `def_language` parameter against the list of available language files. An authenticated customer can set `def_language` to a path traversal payload (e.g., `../../../../../var/customers/webs/customer1/evil`), which is stored in the database. On subsequent requests, `Language::loadLanguage()` constructs a file path using this value and executes it via `require`, achieving arbitrary PHP code execution as the web server user.Details**Root cause:** The API and web UI have inconsistent validation for the `def_language` parameter.The **web UI** (`customer_index.php:261`, `admin_index.php:265`) correctly validates `def_language` against `Language::getLanguages()`, which scans the `lng/` directory for actual language files:The **API** (`Customers.php:1207`, `Admins.php:600`) only runs `Validate::validate()` with the default regex `/^[^\r\n\t\f\0]*$/D`, which permits path traversal sequences:The tainted value is stored in the `panel_customers` (or `panel_admins`) table. On every subsequent request, it is loaded and used in two paths:**API path** (`ApiCommand.php:218-222`):**Web path** (`init.php:180-185`):The `language` session field is `null` for API requests and empty on fresh web logins, so both paths fall through to the unvalidated `def_language`.**File inclusion** (`Language.php:89-98`):With `$iso = '../../../../../var/customers/webs/customer1/evil'`, the path resolves to `/var/customers/webs/customer1/evil.lng.php`, escaping the `lng/` directory.PoC**Step 1 — Upload malicious language file via FTP:**Froxlor customers have FTP access to their web directory by default (`api_allowed` defaults to `1` in the schema).The file is now at `/var/customers/webs/<loginname>/evil.lng.php`.**Step 2 — Set traversal payload via API:**The traversal path is stored in the database. The `.lng.php` suffix is appended automatically by `Language::loadLanguage()`.**Step 3 — Trigger inclusion on next API call:**`ApiCommand::initLang()` loads `def_language` from the database and passes it to `Language::setLanguage()` → `loadLanguage()` → `require /var/customers/webs/customer1/evil.lng.php`.**Step 4 — Verify execution:**ImpactAn authenticated customer can execute arbitrary PHP code as the web server user. This enables:**Full server compromise:** Read `lib/userdata.inc.php` to obtain database credentials, then access all customer data, admin credentials, and server configuration.**Lateral movement:** Access other customers' databases, email, and files from the shared hosting environment.**Persistent backdoor:** Modify Froxlor source files or cron configurations to maintain access.**Data exfiltration:** Read all hosted databases and email content across the panel.The attack is practical because Froxlor is a hosting panel where customers have FTP access by default, and API access is enabled by default (`api_allowed` = 1). The `.lng.php` suffix constraint is not a meaningful barrier since the attacker controls file creation in their web directory.Recommended FixValidate `def_language` against the actual language file list in the API endpoints, matching the web UI behavior:Apply the same fix in `Admins.php` at line 600.Additionally, add a defensive check in `Language::loadLanguage()` to prevent path traversal:
CVSS v3.1 score: 9.9