GHSA-GCV3-5V9Q-FMHH

Vulnerability from github – Published: 2026-05-29 15:36 – Updated: 2026-05-29 15:36
VLAI
Summary
Froxlor has an authorization bypass in FTP shell assignment via missing server-side `available_shells` enforcement
Details

Summary

Froxlor 2.3.6 lets administrators configure system.available_shells as the approved shell list that customers may assign to FTP users. However, the server-side FTP account handlers do not enforce that whitelist when processing add or edit requests.

As a result, an authenticated customer with shell delegation enabled can submit an arbitrary shell such as /bin/bash even when the panel UI only offers more restricted choices. In deployments that use the default nssextrausers integration, the attacker-controlled shell is then propagated into the system account database, leading to real host shell access.

Details

The customer-facing FTP account page builds the shell selector from system.available_shells, which shows that the product intends the setting to act as the authorization boundary:

// customer_ftp.php:138-149
$shells = [
    '/bin/false' => '/bin/false'
];
$availableshells = explode(',', Settings::Get('system.available_shells'));
if (is_array($availableshells) && !empty($availableshells)) {
    foreach ($availableshells as $shell) {
        $shells[trim($shell)] = trim($shell);
    }
}

The request handler forwards posted form data directly into the FTP API command implementation:

// customer_ftp.php:170-172
if ($action == 'edit' && Request::post('send') == 'send') {
    $result = $log->logAction(USR_ACTION, LOG_INFO, "edited ftp-account #" . $id);
    Commands::get()->apiCall('Ftps.update', Request::postAll());
}

On the server side, Ftps::add() and Ftps::update() only perform generic shell string validation. They do not verify that the submitted shell belongs to system.available_shells:

// lib/Froxlor/Api/Commands/Ftps.php:119-123
if (Settings::Get('system.allow_customer_shell') == '1' && $this->getUserDetail('shell_allowed') == '1') {
    $shell = Validate::validate(trim($shell), 'shell', '', '', [], true);
} else {
    $shell = '/bin/false';
}

The validated shell is stored into ftp_users.shell and later consumed by the root-owned cron task that rebuilds NSS extrausers files:

// lib/Froxlor/Cron/System/Extrausers.php:89-97
$passwd_entries[] = $user['username'] . ':x:' . $uid . ':' . $gid . ':' . $gecos . ':' . $homedir . ':' . $shell;

Because the default installer configuration sets system.nssextrausers=1, and the shipped Debian/Bookworm configuration enables extrausers in nsswitch.conf, the attacker-controlled shell becomes the effective login shell of the generated system user on standard supported deployments.

PoC

An attacker needs a normal customer account and a deployment where customer shell delegation is enabled for that customer.

Relevant runtime prerequisites:

  • system.allow_customer_shell=1
  • the attacking customer has shell_allowed=1
  • the deployment uses system.nssextrausers=1 with the shipped libnss-extrausers integration

Froxlor requires a valid CSRF token for POST requests, so the attacker performs the exploit from an authenticated session.

Complete PoC flow:

  1. Log in as a customer and obtain a valid csrf_token.
  2. Identify one FTP account owned by that customer.
  3. Submit an edit request that sets an arbitrary shell outside the administrator-approved system.available_shells list:
POST /customer_ftp.php?page=accounts&action=edit&id=17 HTTP/1.1
Host: target.example
Content-Type: application/x-www-form-urlencoded
Cookie: <authenticated customer session>

csrf_token=VALID_CSRF_TOKEN&
send=send&
id=17&
username=test1ftp1&
ftp_description=poc&
path=/&
shell=/bin/bash&
login_enabled=1
  1. Wait for Froxlor's master cron to process the queued REBUILD_NSSUSERS task.

Result:

  • the request is accepted even if /bin/bash is not present in system.available_shells
  • ftp_users.shell is updated to /bin/bash
  • /var/lib/extrausers/passwd is regenerated with /bin/bash as the FTP user's login shell
  • the attacker can then authenticate to the host using that FTP user's credentials and obtain an interactive shell

Impact

This issue lets a low-privileged customer bypass an administrator-defined authorization boundary and promote an FTP-only account into a real shell account. On shared-hosting systems managed by Froxlor, that materially changes the trust model and can expose the host to lateral movement, local privilege-escalation follow-on attacks, data theft from colocated services, and persistence on the server.

Because the vulnerable flow is executed through the normal authenticated web interface and a root-owned provisioning task later materializes the chosen shell at the operating-system level, the vulnerability is stronger than a UI-only restriction bypass.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "froxlor/froxlor"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.3.6"
            },
            {
              "fixed": "2.3.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ],
      "versions": [
        "2.3.6"
      ]
    }
  ],
  "aliases": [
    "CVE-2026-41235"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-863"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-29T15:36:26Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\nFroxlor 2.3.6 lets administrators configure `system.available_shells` as the approved shell list that customers may assign to FTP users. However, the server-side FTP account handlers do not enforce that whitelist when processing add or edit requests.\n\nAs a result, an authenticated customer with shell delegation enabled can submit an arbitrary shell such as `/bin/bash` even when the panel UI only offers more restricted choices. In deployments that use the default `nssextrausers` integration, the attacker-controlled shell is then propagated into the system account database, leading to real host shell access.\n\n### Details\nThe customer-facing FTP account page builds the shell selector from `system.available_shells`, which shows that the product intends the setting to act as the authorization boundary:\n\n```php\n// customer_ftp.php:138-149\n$shells = [\n    \u0027/bin/false\u0027 =\u003e \u0027/bin/false\u0027\n];\n$availableshells = explode(\u0027,\u0027, Settings::Get(\u0027system.available_shells\u0027));\nif (is_array($availableshells) \u0026\u0026 !empty($availableshells)) {\n    foreach ($availableshells as $shell) {\n        $shells[trim($shell)] = trim($shell);\n    }\n}\n```\n\nThe request handler forwards posted form data directly into the FTP API command implementation:\n\n```php\n// customer_ftp.php:170-172\nif ($action == \u0027edit\u0027 \u0026\u0026 Request::post(\u0027send\u0027) == \u0027send\u0027) {\n    $result = $log-\u003elogAction(USR_ACTION, LOG_INFO, \"edited ftp-account #\" . $id);\n    Commands::get()-\u003eapiCall(\u0027Ftps.update\u0027, Request::postAll());\n}\n```\n\nOn the server side, `Ftps::add()` and `Ftps::update()` only perform generic shell string validation. They do not verify that the submitted shell belongs to `system.available_shells`:\n\n```php\n// lib/Froxlor/Api/Commands/Ftps.php:119-123\nif (Settings::Get(\u0027system.allow_customer_shell\u0027) == \u00271\u0027 \u0026\u0026 $this-\u003egetUserDetail(\u0027shell_allowed\u0027) == \u00271\u0027) {\n    $shell = Validate::validate(trim($shell), \u0027shell\u0027, \u0027\u0027, \u0027\u0027, [], true);\n} else {\n    $shell = \u0027/bin/false\u0027;\n}\n```\n\nThe validated shell is stored into `ftp_users.shell` and later consumed by the root-owned cron task that rebuilds NSS extrausers files:\n\n```php\n// lib/Froxlor/Cron/System/Extrausers.php:89-97\n$passwd_entries[] = $user[\u0027username\u0027] . \u0027:x:\u0027 . $uid . \u0027:\u0027 . $gid . \u0027:\u0027 . $gecos . \u0027:\u0027 . $homedir . \u0027:\u0027 . $shell;\n```\n\nBecause the default installer configuration sets `system.nssextrausers=1`, and the shipped Debian/Bookworm configuration enables `extrausers` in `nsswitch.conf`, the attacker-controlled shell becomes the effective login shell of the generated system user on standard supported deployments.\n\n### PoC\nAn attacker needs a normal customer account and a deployment where customer shell delegation is enabled for that customer.\n\nRelevant runtime prerequisites:\n\n- `system.allow_customer_shell=1`\n- the attacking customer has `shell_allowed=1`\n- the deployment uses `system.nssextrausers=1` with the shipped `libnss-extrausers` integration\n\nFroxlor requires a valid CSRF token for POST requests, so the attacker performs the exploit from an authenticated session.\n\nComplete PoC flow:\n\n1. Log in as a customer and obtain a valid `csrf_token`.\n2. Identify one FTP account owned by that customer.\n3. Submit an edit request that sets an arbitrary shell outside the administrator-approved `system.available_shells` list:\n\n```http\nPOST /customer_ftp.php?page=accounts\u0026action=edit\u0026id=17 HTTP/1.1\nHost: target.example\nContent-Type: application/x-www-form-urlencoded\nCookie: \u003cauthenticated customer session\u003e\n\ncsrf_token=VALID_CSRF_TOKEN\u0026\nsend=send\u0026\nid=17\u0026\nusername=test1ftp1\u0026\nftp_description=poc\u0026\npath=/\u0026\nshell=/bin/bash\u0026\nlogin_enabled=1\n```\n\n4. Wait for Froxlor\u0027s master cron to process the queued `REBUILD_NSSUSERS` task.\n\nResult:\n\n- the request is accepted even if `/bin/bash` is not present in `system.available_shells`\n- `ftp_users.shell` is updated to `/bin/bash`\n- `/var/lib/extrausers/passwd` is regenerated with `/bin/bash` as the FTP user\u0027s login shell\n- the attacker can then authenticate to the host using that FTP user\u0027s credentials and obtain an interactive shell\n\n\n### Impact\nThis issue lets a low-privileged customer bypass an administrator-defined authorization boundary and promote an FTP-only account into a real shell account. On shared-hosting systems managed by Froxlor, that materially changes the trust model and can expose the host to lateral movement, local privilege-escalation follow-on attacks, data theft from colocated services, and persistence on the server.\n\nBecause the vulnerable flow is executed through the normal authenticated web interface and a root-owned provisioning task later materializes the chosen shell at the operating-system level, the vulnerability is stronger than a UI-only restriction bypass.",
  "id": "GHSA-gcv3-5v9q-fmhh",
  "modified": "2026-05-29T15:36:27Z",
  "published": "2026-05-29T15:36:26Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/froxlor/froxlor/security/advisories/GHSA-gcv3-5v9q-fmhh"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/froxlor/froxlor"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Froxlor has an authorization bypass in FTP shell assignment via missing server-side `available_shells` enforcement"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…