← Back to Attack Research

CrushFTP CVE-2025-31161: the AWS4-HMAC bypass that picks the user before checking the signature

A logic flaw in CrushFTP's S3-style AWS4-HMAC authorization path lets an unauthenticated request claim any account, including the built-in crushadmin, because the server binds the session to the named user before it verifies the HMAC signature. CVSS 9.8, on CISA's Known Exploited list since April 2025. Here is the mechanism and how to tell if you are exposed.

An authentication check has one job: decide whether the person on the other end is who they claim to be, and refuse to act until that decision is made. CVE-2025-31161 is what happens when a server gets the order of operations wrong. In CrushFTP's S3-style AWS4-HMAC authorization path, the code reads the account name out of the Authorization header, confirms that the account exists, and begins associating the current session with that user, all before it verifies the HMAC signature that is supposed to prove the request was signed with the account's secret. Craft the header the right way and the signature check is effectively skipped. The session is now bound to whatever account you named, up to and including the built-in crushadmin, with no credential ever presented. CVSS 9.8, pre-auth, over the network, and on CISA's Known Exploited Vulnerabilities catalog since 7 April 2025.

CrushFTP is a managed file-transfer server: the kind of internet-facing system organizations put in front of the files they move between partners, customers, and internal teams. That role makes it a trust anchor. It holds transferred data, it holds credentials for the accounts allowed to move that data, and its administrative interface can create users, rewrite configuration, and in several editions run code on the host. A pre-authentication takeover of the admin account on a box like this is not a foothold on the edge of the network. It is a foothold on the system the rest of the environment trusts to broker its file flows. This post explains the mechanism of CVE-2025-31161, why the fix is an ordering fix rather than a new feature, and how to determine your own exposure without firing a single exploit. Verifiable security.

What AWS4-HMAC is doing in a file-transfer server

CrushFTP speaks several protocols on its HTTP component, and one of them is an S3-compatible interface. Amazon's S3 uses a request-signing scheme called Signature Version 4, and clients authenticate by sending an Authorization header that begins with AWS4-HMAC-SHA256. The header names the access key (which maps to a user), declares which headers were signed, and carries a hex signature. The signature is an HMAC computed over a canonical representation of the request using a key derived from the user's secret. The server is meant to look up the named user, derive the same key from the secret it holds for that user, recompute the HMAC over the same canonical request, and compare. If the two signatures match, the request was signed by someone who holds the secret, and only then is the request authorized as that user.

The security of the whole scheme rests on one invariant: the identity asserted in the header is not trusted until the signature is verified. The header is attacker-controlled input. The signature is the proof. An attacker can write any access key they like into the header, so the access key by itself proves nothing. It is the recomputed-HMAC comparison that turns a claimed identity into an authenticated one. Break the ordering so that the identity is acted on before the proof is checked, and the scheme collapses into a pure name-your-account bypass.

The flaw: identity assigned before the signature is checked

According to third-party reverse-engineering of the vulnerable builds by Outpost24, the relevant logic lives in a method named loginCheckHeaderAuth() in ServerSessionHTTP.java. We present these internal names as researcher analysis, not as vendor source, because they come from reversing the shipped code rather than from CrushFTP's own documentation. The shape of the bug, however, is straightforward and matches the observed behavior. When the server parses an AWS4-HMAC authorization header, it extracts the username, checks whether that username exists, and starts binding the current session to that account. That binding is driven by a lookup flag, described in the reversing as lookup_user_pass, which governs whether the code is in the mode of resolving a user and its stored secret. The problem is sequencing: the username-existence resolution and the session association run ahead of the step that verifies the HMAC signature derived from the user's secret.

A crafted header shape lets the username-existence branch succeed while the signature-verification branch is effectively skipped, so the session ends up associated with the named account without the secret ever being tested. The public analyses describe the trigger as a manipulation of the header, and the practical way to think about it is a header that satisfies the code path which looks up and attaches the user, without ever reaching, or without ever failing, the code path that would recompute and compare the HMAC. CrushFTP itself characterized the issue as tied to how the code handled the header when certain conditions were met, which is consistent with a logic and race condition in that authorization routine. We deliberately stop short of publishing a working header. The mechanism is the point; the weaponized string is not.

AWS4-HMAC AUTH: INTENDED vs BROKEN ORDER INTENDED ---------------------------------------------------------------- request + Authorization: AWS4-HMAC-SHA256 ... | v [1] parse header, read username (access key) | v [2] recompute HMAC from that user's SECRET <-- the proof | v [3] signatures match? --- no ---> reject (401) | yes v [4] bind session to user, authorize request BROKEN (CVE-2025-31161) ---------------------------------------------------------------- request + crafted AWS4-HMAC header naming "crushadmin" | v [1] parse header, read username | v [2] username exists? begin binding session to user <-- too early | v [3] HMAC signature check ......... effectively skipped | v [4] session already bound to crushadmin -> authenticated root cause: step 2 acts on the CLAIM before step 3 checks the PROOF

Illustrative control flow reconstructed from public analysis. The defect is ordering: the session is attached to the named account before the signature that is supposed to authorize it is verified.

Why the target is almost always crushadmin

The bypass authenticates you as whatever account the header names, subject to one condition: the username has to exist. That is a low bar. Every CrushFTP install ships with a built-in administrator called crushadmin, and that name is public knowledge, so an attacker does not need to enumerate anything to pick the highest-value target on the system. Other valid usernames work too if they can be guessed or are already known, but there is rarely a reason to aim lower than the built-in admin. Once the session is bound to crushadmin, the attacker holds full administrative control of the file-transfer server.

Administrative control here is not a narrow capability. It means read and write access over the files moving through the server, the ability to change configuration, the ability to create new user accounts, and access to the administrative features that, in several CrushFTP editions, extend to running code on the underlying host. A single unauthenticated request converts into durable, privileged control of a system that sits at a data-flow chokepoint. That combination, no credentials required, full admin achieved, on an internet-facing trust anchor, is why the CVSS base score is 9.8 and why the vector string reads AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H: network reachable, low complexity, no privileges, no user interaction, and high impact across confidentiality, integrity, and availability.

The header is attacker-controlled input. The signature is the proof. This bug acts on the claim before it checks the proof, and a pre-auth admin takeover is the result.

Which versions, and why the DMZ proxy matters

The vulnerability affects CrushFTP 10 before 10.8.4 and CrushFTP 11 before 11.3.1. The fixes are those two builds: upgrading to 10.8.4 on the 10.x line or 11.3.1 on the 11.x line removes the flaw by correcting the authorization path so identity is not committed before the signature is verified. There is also a documented architectural mitigation. Instances deployed behind the CrushFTP DMZ proxy are reported not to be exploitable. The DMZ proxy is a separate front-end component that terminates and relays connections to the backend CrushFTP instance, and its presence changes how requests reach the vulnerable code such that the bypass does not land. It is a mitigation, not a substitute for patching, but for defenders who cannot immediately upgrade every instance, an exposed instance without the DMZ proxy is a materially hotter risk than one behind it. The practical reading: patch to the fixed build, and treat any directly internet-exposed instance not behind the DMZ proxy as the first to fix.

A note on the duplicate CVE

If you research this issue you will find two identifiers, and it is worth being precise about which one to track. The bug was briefly assigned a second id, CVE-2025-2825, by VulnCheck during a disclosure dispute over timing and coordination. That identifier is now REJECTED. The canonical identifier for this vulnerability is CVE-2025-31161, and that is the one that appears on CISA's Known Exploited Vulnerabilities catalog and in the NVD record. If your asset inventory, scanner output, or ticketing system still references CVE-2025-2825, reconcile it to CVE-2025-31161 so you are not tracking one issue as two, or worse, closing one and leaving the other open in your reporting. The underlying defect is a single vulnerability regardless of how many numbers were briefly attached to it.

Detecting exposure and exploitation

You can assess whether you are exposed without sending anything malicious, because exposure is a function of readable facts: is a CrushFTP instance internet-facing, is it running a build older than 10.8.4 or 11.3.1, and is it in front of or behind the DMZ proxy. Those three answers tell you your risk before any request is crafted. Detecting attempted or successful exploitation is a different exercise, and the public incident reporting gives concrete signals to hunt for.

Assess and hunt: CrushFTP CVE-2025-31161

Why ordering bugs are the hard ones to spot

It is tempting to file this under a coding mistake and move on, but the more useful lesson is about where authentication defects hide. This is not a missing check. The signature verification code exists; CrushFTP knows how to validate an AWS4-HMAC request. The defect is that a state change, binding the session to a user, happens on one side of the check when it should happen only on the other. Ordering and state-machine flaws like this survive code review precisely because every individual piece looks correct. The username lookup is correct. The signature comparison is correct. What is wrong is that an authenticated-only action is reachable along a path that has not yet completed the authentication. CWE catalogs this as CWE-305, Authentication Bypass by Primary Weakness, and the record also cites CWE-287, Improper Authentication. Both names point at the same structural issue: the authorization decision was influenced, or committed, before the evidence that should drive it was actually evaluated.

The defensive takeaway generalizes past CrushFTP. Anywhere a server reads an identity from attacker-controlled input and does anything with that identity before verifying the accompanying proof, the same class of bug is possible. Session objects should not be attached to a principal until the credential for that principal has been checked and has passed. The check and the commit must be a single, ordered, fail-closed step, and any early return, race, or alternate branch that reaches the commit without passing the check is a bypass waiting to be found.

How Celvex catches this

Find. Prove. Fix. Verify.

Find

A read-only sweep locates internet-facing CrushFTP instances, fingerprints the build against the 10.8.4 and 11.3.1 fix lines, notes whether the DMZ proxy fronts the service, and cross-references CVE-2025-31161 on the Known Exploited catalog, all without sending a crafted auth header.

Prove

A confirmed exposed, affected build becomes an Ed25519-signed Proof Capsule carrying the host, the version evidence, the DMZ-proxy status, and the matching catalog entry, reproducible offline by you or your auditor.

Fix

The capsule's remediation block names the steps: upgrade to CrushFTP 10.8.4 or 11.3.1, place directly exposed instances behind the DMZ proxy, and reconcile any lingering CVE-2025-2825 references to the canonical id.

Verify

A fresh sweep confirms the instance now reports a fixed build or is no longer exposed. The finding closes and the verified-fix event is recorded for the audit trail.

The reason this vulnerability was so damaging in the wild is not that the fix is hard. It is a one-line-ordering fix, shipped in 10.8.4 and 11.3.1. The reason it hurt is that file-transfer servers are exactly the kind of internet-facing infrastructure that gets stood up, exposed, and then left alone because it is doing its job quietly. The discipline that closes it is unglamorous: know every instance you run, know its version, know whether it is behind the DMZ proxy, and treat a Known-Exploited hit on a trust anchor as an incident with a clock on it rather than a line on a backlog.

Verifiable security. Find it. Prove it. Fix it. Verify the fix held. That is what we ship.

Sources

Is an affected CrushFTP instance exposed on your perimeter?

Free Exposure Check, no signup required. We map your internet-facing file-transfer servers, fingerprint their versions against the CVE-2025-31161 fix lines and the known-exploited catalog, and ship a signed Proof Capsule for the highest-confidence finding.

Run a Free Scan →