CVE-2025-54309: CrushFTP AS2 Auth Bypass to Unauthenticated RCE
CVE-2025-54309: CrushFTP AS2 Auth Bypass to Unauthenticated RCE
Educational material only. The techniques below are documented for authorized penetration testing, security research and defensive understanding. Do not run any of this against systems you are not explicitly authorized to test.
TL;DR
| Field | Value |
|---|---|
| CVE | CVE-2025-54309 |
| Product | CrushFTP (managed file transfer server) |
| Affected | CrushFTP 10 < 10.8.5, CrushFTP 11 < 11.3.4_23 |
| Precondition | DMZ proxy feature not in use |
| Class | Authentication bypass (AS2 validation logic flaw) → RCE |
| CVSS v3.1 | 9.8 Critical — AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Status | Exploited in the wild as a zero-day, July 2025 (CISA KEV) |
CVE-2025-54309 is not a memory-corruption bug or an injection in the classic
sense — it is a logic flaw in how CrushFTP validates AS2 messages over
HTTP(S). A request whose AS2-To header is empty or malformed slips
through an early-return path in the authentication routine and is treated as
trusted. From that single primitive an attacker builds a full chain to
remote command execution.
1. Root cause: a premature return in loginCheckHeaderAuth()
The defect lives in CrushFTP’s HTTP session-management code, in
ServerSessionHTTP.java, in the method loginCheckHeaderAuth() (around
line 2285 in the affected builds).
CrushFTP supports the AS2 protocol (Applicability Statement 2 — a standard for secure B2B document exchange over HTTP). AS2 messages carry routing headers, notably:
AS2-From— the sending partner identifierAS2-To— the receiving partner identifier
CrushFTP encodes partner/auth context inside the AS2-To value using a
-_- delimiter (a partner token and an associated marker joined by -_-).
The authentication routine parses AS2-To, splits on -_-, and uses the
result to decide how the request authenticates.
The flaw: when the AS2-To header is empty or does not contain the
-_- delimiter, the parsing logic does not fail closed. Combined with the
default configuration where blank_passwords is disabled, the method
returns early — before any authentication is actually enforced. The
request continues down the stack as if a valid authentication decision had
been made.
Conceptually, the broken control flow looks like this:
// ServerSessionHTTP.java — loginCheckHeaderAuth() (simplified illustration)
String as2To = request.getHeader("AS2-To");
// Intended: parse "<partner>-_-<marker>" and authenticate accordingly.
String[] parts = as2To.split("-_-");
if (/* as2To empty OR no "-_-" delimiter */ && !blankPasswordsAllowed) {
// BUG: bails out of the auth routine here instead of rejecting.
// No credential check is performed; caller proceeds "authenticated".
return;
}
// ... real authentication / AS2 MIC verification would have happened here ...
Two failures compound:
- Fail-open parsing. A missing/short-circuited
AS2-Toshould be a hard rejection. Instead it short-circuits out of the auth code. - No cryptographic enforcement. The AS2 Message Integrity Check (MIC) and the multipart message body are never properly validated on this path, so the attacker controls the request content without integrity binding.
Net effect: a remote, unauthenticated client can issue requests that the server treats as coming from an authenticated administrative context.
Why the DMZ-proxy precondition matters. When CrushFTP’s DMZ proxy feature is deployed, the front-end proxy normalizes/handles the AS2 path differently and the vulnerable code path is not reached the same way. The bug is exploitable specifically when the DMZ proxy is not in use — which is why the vendor’s interim mitigation is “enable the DMZ proxy.”
2. The bypass primitive
The abused surface is the admin function endpoint:
POST /WebInterface/function/ (also reachable as /?/WebInterface/function/)
Host: target
AS2-To: ← empty / missing the -_- delimiter
...
Because the auth routine returned early, calls to /WebInterface/function/
are processed with administrative privilege without any session, token or
password. /WebInterface/function/ is CrushFTP’s control plane — it exposes
user management, VFS configuration, settings and more. Owning it
unauthenticated is equivalent to owning the server.
3. From auth bypass to RCE — the kill chain
The publicly documented exploitation chain stitches the bypass into full command execution:
Stage 1 — Create an attacker-controlled admin account
Using the bypassed /WebInterface/function/ endpoint, the attacker invokes
the user-management functions to create a brand-new account:
[+] Attack successful — account 'admin' created with password 'password'
[+] Login successful: admin:password
Stage 2 — Inherit crushadmin privileges
The new account is cloned from / made to inherit the permissions of the
built-in crushadmin super-administrator:
[+] Attack successful — account 'admin' inheritance from 'crushadmin'
The attacker now has legitimate, persistent credentials with full admin rights — no longer dependent on the bypass.
Stage 3 — Remap the Virtual File System to /
CrushFTP accounts have a Virtual File System (VFS) that maps virtual paths
to real directories. The attacker reconfigures the new account’s VFS root to
the operating-system root (/). Every file the CrushFTP process can
read or write is now reachable — e.g. arbitrary download of /etc/passwd,
and arbitrary write into application directories.
Stage 4 — Drop a malicious Java plugin
With write access to the OS filesystem, the attacker plants a Java plugin JAR into CrushFTP’s plugin directory:
/app/plugins/CrushCommandPlugin.jar
The plugin implements a run() method that takes a command parameter — a
purpose-built code-execution backdoor loaded by the trusted server process.
Stage 5 — Remote command execution
After the plugin is loaded (on restart / plugin reload), commands are executed through it:
[+] Running command on CrushFTP server: uname -a
<commandResult><response>Linux c9fb6d607542 ... Exit code: 0</response></commandResult>
Commands run as the CrushFTP service account (in the analyzed container,
the unprivileged java user, uid=65532). The plugin invocation pattern
observed is:
cmd=syscmd&args=<command>
That is the complete path: unauthenticated HTTP request → admin account → VFS = OS root → malicious plugin → arbitrary command execution.
4. Severity (CVSS 9.8)
- No authentication required — the entry point needs no credentials; the only precondition is that the DMZ proxy is not in use.
- Network exploitable — any internet-reachable CrushFTP instance is a target; reconnaissance is a single HTTP request.
- Full system impact — confidentiality (read any file), integrity
(create admins, alter config, write files) and availability (service
control) are all compromised, hence the
C:H/I:H/A:Hvector.
5. Lab setup and a read-only PoC
A public proof-of-concept exists for authorized testing:
git clone https://github.com/issamjr/CVE-2025-54309-EXPLOIT.git && cd CVE-2025-54309-EXPLOIT
pip install -r requirements.txt
Reconnaissance / version + vulnerability check (non-destructive):
python3 exploit.py 10.10.10.10 --recon
Default AS2 auth-bypass → command execution:
python3 exploit.py 10.10.10.10 -c "uname -a"
Alternate command-injection payload path:
python3 exploit.py 10.10.10.10 -p cmd_inject -c "whoami"
File-write primitive demonstration:
python3 exploit.py 10.10.10.10 -p file_upload \
--upload-file "/tmp/pwned.txt" --upload-data "System Compromised"
In a real authorized engagement, prefer the
--recon/version check to confirm vulnerability, and treat the destructive stages (account creation, VFS remap, plugin drop) as proof-of-impact only with explicit written scope — they leave persistent backdoors that must be cleaned up.
6. Detection — indicators of compromise
Hunt CrushFTP / web logs and the host for:
- HTTP(S) requests to
/WebInterface/function/or/?/WebInterface/function/carrying an empty or delimiter-lessAS2-Toheader. - AS2 requests over HTTPS to a server where the DMZ proxy is disabled.
- Unexpected new user accounts, especially any cloned from or inheriting
crushadmin. - Account VFS definitions newly mapped to
/or other OS roots. - New/unknown JAR files in the plugin directory, e.g.
/app/plugins/CrushCommandPlugin.jar, and plugin loads after a restart. - Process execution chains spawned from the CrushFTP/Java service account
(
cmd=syscmd&args=...patterns).
CrushFTP keeps a copy of the previous configuration; defenders can diff the current user/VFS config against the prior known-good copy to spot injected accounts.
7. Remediation
Immediately
- Patch. Upgrade to CrushFTP 11.3.4_23+ or 10.8.5+. These
builds fix the
loginCheckHeaderAuth()fail-open logic. - Interim mitigation if you cannot patch now: enable the DMZ proxy feature, which moves AS2 handling off the vulnerable path.
- Restrict the management/web interface so it is not exposed directly to the internet (reverse proxy / firewall / IP allowlisting).
Incident response (assume breach if internet-exposed and unpatched in July 2025)
- Audit all user accounts; delete any unrecognized admin/
crushadminclones and rotatecrushadminand all admin credentials. - Review every account’s VFS for mappings to
/or unexpected roots. - Inspect the plugin directory; remove unknown JARs and review plugin load history.
- Hunt the host for the IOCs in §6 and for post-exploitation activity by the service account.
Key takeaway
The dangerous class of bug here is fail-open authentication parsing:
when an auth routine cannot make a decision (missing/malformed AS2-To), it
must reject, not return out of the check. CVE-2025-54309 is a reminder
that protocol-handling code paths (AS2, in this case) are part of the
authentication attack surface, and that a single early-return can collapse
into full remote code execution when downstream features (user management,
VFS, plugins) are powerful and trust the caller implicitly.
References
- Exploit PoC: https://github.com/issamjr/CVE-2025-54309-EXPLOIT
- Root-cause analysis (ZeroPath): https://zeropath.com/blog/crushftp-cve-2025-54309-as2-validation-flaw
- Bypass-to-RCE chain (Foregenix OrionX): https://orionx.foregenix.com/blog/cve-2025-54309-rce-crushftp
- SentinelOne vulnerability database: https://www.sentinelone.com/vulnerability-database/cve-2025-54309/