Business Logic Errors in froxlor/froxlor
Description
A business logic error in Froxlor prior to 2.0.22 and 2.1.0 allows an attacker to bypass validation, potentially enabling privilege escalation.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A business logic error in Froxlor prior to 2.0.22 and 2.1.0 allows an attacker to bypass validation, potentially enabling privilege escalation.
Vulnerability
Overview CVE-2023-4304 describes a business logic error in the Froxlor server administration panel, affecting versions prior to 2.0.22 and 2.1.0. The flaw is located in the API parameter handling, specifically in the Admins.update() method where the $params argument was not properly type-hinted and validated [1]. As shown in the fixing commit, the constructor was changed from accepting $params = null to enforcing an array type declaration, and helper functions like trimArray and getUlParam received similar type strictness improvements [2]. This lack of input validation could allow an attacker to pass unexpected data types or empty values that bypass the intended administrative checks.
Exploitation
An attacker with access to the Froxlor API (typically requiring authentication as an administrator or a reseller) could craft a specially malformed request to the Admins.update endpoint. By supplying non-array or improperly structured parameters, the flawed business logic may allow the modification of user attributes—such as the admin name—without proper validation [1][2]. This could lead to situations where an empty or malicious admin name is accepted, breaking the integrity of the user management process.
Impact
Successful exploitation could enable privilege escalation or other unauthorized administrative actions. For example, an attacker might update an admin account's name to an empty string, potentially causing denial of service or confusion, or inject a name that leads to cross-site scripting (depending on how the name is rendered) [1]. The core impact is a bypass of expected business rules within the Froxlor administration panel, undermining the security of hosting platform management.
Mitigation
Users should upgrade to Froxlor version 2.0.22 or 2.1.0, which contain the fix that enforces proper type validation for API parameters [2][1]. There are no known workarounds, and applying the update is the recommended course of action. The vulnerability was reported via the Huntr bug bounty program [4] and the Froxlor project has addressed it in the referenced commits [2].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
froxlor/froxlorPackagist | < 2.0.22 | 2.0.22 |
Affected products
2Patches
1ce9a5f97a3edvalidate non-empy admin-name in Admins.update()
3 files changed · +27 −15
lib/Froxlor/Api/ApiParameter.php+14 −14 modified@@ -39,12 +39,12 @@ abstract class ApiParameter /** * - * @param array $params + * @param array|null $params * optional, array of parameters (var=>value) for the command * * @throws Exception */ - public function __construct($params = null) + public function __construct(array $params = null) { if (!is_null($params)) { $params = $this->trimArray($params); @@ -57,7 +57,7 @@ public function __construct($params = null) * * @param array $input * - * @return array + * @return string|array */ private function trimArray($input) { @@ -79,9 +79,9 @@ private function trimArray($input) /** * get specific parameter which also has and unlimited-field * - * @param string $param + * @param string|null $param * parameter to get out of the request-parameter list - * @param string $ul_field + * @param string|null $ul_field * parameter to get out of the request-parameter list * @param bool $optional * default: false @@ -91,7 +91,7 @@ private function trimArray($input) * @return mixed * @throws Exception */ - protected function getUlParam($param = null, $ul_field = null, $optional = false, $default = 0) + protected function getUlParam(string $param = null, string $ul_field = null, bool $optional = false, $default = 0) { $param_value = (int)$this->getParam($param, $optional, $default); $ul_field_value = $this->getBoolParam($ul_field, true, 0); @@ -102,11 +102,11 @@ protected function getUlParam($param = null, $ul_field = null, $optional = false } /** - * get specific parameter from the parameterlist; + * get specific parameter from the parameter list; * check for existence and != empty if needed. * Maybe more in the future * - * @param string $param + * @param string|null $param * parameter to get out of the request-parameter list * @param bool $optional * default: false @@ -116,7 +116,7 @@ protected function getUlParam($param = null, $ul_field = null, $optional = false * @return mixed * @throws Exception */ - protected function getParam($param = null, $optional = false, $default = '') + protected function getParam(string $param = null, bool $optional = false, $default = '') { // does it exist? if (!isset($this->cmd_params[$param])) { @@ -128,7 +128,7 @@ protected function getParam($param = null, $optional = false, $default = '') return $default; } // is it empty? - test really on string, as value 0 is being seen as empty by php - if ($this->cmd_params[$param] === "") { + if (!is_array($this->cmd_params[$param]) && trim($this->cmd_params[$param]) === "") { if ($optional === false) { // get module + function for better error-messages $inmod = $this->getModFunctionString(); @@ -142,7 +142,7 @@ protected function getParam($param = null, $optional = false, $default = '') /** * returns "module::function()" for better error-messages (missing parameter etc.) - * makes debugging a whole lot more comfortable + * makes debugging a lot more comfortable * * @param int $level * depth of backtrace, default 2 @@ -152,7 +152,7 @@ protected function getParam($param = null, $optional = false, $default = '') * * @return string */ - private function getModFunctionString($level = 1, $max_level = 5, $trace = null) + private function getModFunctionString(int $level = 1, int $max_level = 5, $trace = null) { // which class called us $_class = get_called_class(); @@ -174,7 +174,7 @@ private function getModFunctionString($level = 1, $max_level = 5, $trace = null) /** * getParam wrapper for boolean parameter * - * @param string $param + * @param string|null $param * parameter to get out of the request-parameter list * @param bool $optional * default: false @@ -183,7 +183,7 @@ private function getModFunctionString($level = 1, $max_level = 5, $trace = null) * * @return string */ - protected function getBoolParam($param = null, $optional = false, $default = false) + protected function getBoolParam(string $param = null, bool $optional = false, $default = false) { $_default = '0'; if ($default) {
lib/Froxlor/Api/Commands/Admins.php+12 −0 modified@@ -584,6 +584,18 @@ public function update() $theme = Settings::Get('panel.default_theme'); } + if (empty(trim($name))) { + Response::standardError([ + 'stringisempty', + 'admin.name' + ], '', true); + } + if (empty(trim($email))) { + Response::standardError([ + 'stringisempty', + 'admin.email' + ], '', true); + } if (!Validate::validateEmail($email)) { Response::standardError('emailiswrong', $email, true); } else {
lib/Froxlor/Validate/Validate.php+1 −1 modified@@ -260,7 +260,7 @@ public static function validateLocalHostname(string $hostname) } /** - * Returns if an emailaddress is in correct format or not + * Returns if an email-address is in correct format or not * * @param string $email The email address to check *
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.