Add initial model and support to OutputStream
parent
4257fef91b
commit
d5dfd75e71
|
@ -6,6 +6,7 @@ require 'rex/proto/dcerpc'
|
||||||
require 'rex/proto/drda'
|
require 'rex/proto/drda'
|
||||||
require 'rex/proto/iax2'
|
require 'rex/proto/iax2'
|
||||||
require 'rex/proto/kerberos'
|
require 'rex/proto/kerberos'
|
||||||
|
require 'rex/proto/rmi'
|
||||||
|
|
||||||
module Rex
|
module Rex
|
||||||
module Proto
|
module Proto
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
# JAVA RMI Wire protocol implementation
|
||||||
|
# http://docs.oracle.com/javase/7/docs/platform/rmi/spec/rmi-protocol.html
|
||||||
|
|
||||||
|
require 'rex/proto/rmi/model'
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Rex
|
||||||
|
module Proto
|
||||||
|
module Rmi
|
||||||
|
module Model
|
||||||
|
SIGNATURE = 'JRMI'
|
||||||
|
STREAM_PROTOCOL = 0x4b
|
||||||
|
SINGLE_OP_PROTOCOL = 0x4c
|
||||||
|
MULTIPLEX_PROTOCOL = 0x4d
|
||||||
|
CALL_MESSAGE = 0x50
|
||||||
|
PING_MESSAGE = 0x52
|
||||||
|
DGC_ACK_MESSAGE = 0x54
|
||||||
|
PROTOCOL_ACK = 0x4e
|
||||||
|
PROTOCOL_NOT_SUPPORTED = 0x4f
|
||||||
|
RETURN_DATA = 0x51
|
||||||
|
PING_ACK = 0x53
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'rex/proto/rmi/model/element'
|
||||||
|
require 'rex/proto/rmi/model/output_stream'
|
|
@ -0,0 +1,128 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Rex
|
||||||
|
module Proto
|
||||||
|
module Rmi
|
||||||
|
module Model
|
||||||
|
class Element
|
||||||
|
|
||||||
|
include Rex::Proto::Rmi::Model
|
||||||
|
|
||||||
|
def self.attr_accessor(*vars)
|
||||||
|
@attributes ||= []
|
||||||
|
@attributes.concat vars
|
||||||
|
super(*vars)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Retrieves the element class fields
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
def self.attributes
|
||||||
|
@attributes
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a Rex::Proto::Rmi::Model::Element with data from the IO.
|
||||||
|
#
|
||||||
|
# @param io [IO] the IO to read data from
|
||||||
|
# @return [Rex::Proto::Rmi::Model::Element]
|
||||||
|
def self.decode(io)
|
||||||
|
elem = self.new
|
||||||
|
elem.decode(input)
|
||||||
|
|
||||||
|
elem
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(options = {})
|
||||||
|
self.class.attributes.each do |attr|
|
||||||
|
if options.has_key?(attr)
|
||||||
|
m = (attr.to_s + '=').to_sym
|
||||||
|
self.send(m, options[attr])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Retrieves the element instance fields
|
||||||
|
#
|
||||||
|
# @return [Array]
|
||||||
|
def attributes
|
||||||
|
self.class.attributes
|
||||||
|
end
|
||||||
|
|
||||||
|
# Decodes the Rex::Proto::Rmi::Model::Element from the input.
|
||||||
|
#
|
||||||
|
# @raise [NoMethodError]
|
||||||
|
# @return [Rex::Proto::Rmi::Model::Element]
|
||||||
|
def decode(io)
|
||||||
|
self.class.attributes.each do |attr|
|
||||||
|
dec_method = ("decode_#{attr}").to_sym
|
||||||
|
decoded = self.send(dec_method, io)
|
||||||
|
assign_method = (attr.to_s + '=').to_sym
|
||||||
|
self.send(assign_method, decoded)
|
||||||
|
end
|
||||||
|
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Encodes the Rex::Proto::Rmi::Model::Element into an String.
|
||||||
|
#
|
||||||
|
# @raise [NoMethodError]
|
||||||
|
# @return [String]
|
||||||
|
def encode
|
||||||
|
encoded = ''
|
||||||
|
self.class.attributes.each do |attr|
|
||||||
|
m = ("encode_#{attr}").to_sym
|
||||||
|
encoded << self.send(m) if self.send(attr)
|
||||||
|
end
|
||||||
|
|
||||||
|
encoded
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Reads a byte from an IO
|
||||||
|
#
|
||||||
|
# @param io [IO] the IO to read from
|
||||||
|
# @return [Fixnum]
|
||||||
|
# @raise [RuntimeError] 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
|
||||||
|
|
||||||
|
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
|
||||||
|
def read_short(io)
|
||||||
|
raw = io.read(2)
|
||||||
|
|
||||||
|
unless raw && raw.length == 2
|
||||||
|
raise ::RuntimeError, 'Failed to read short'
|
||||||
|
end
|
||||||
|
|
||||||
|
raw.unpack('n')[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reads an string from an IO
|
||||||
|
#
|
||||||
|
# @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
|
||||||
|
def read_string(io, length)
|
||||||
|
raw = io.read(length)
|
||||||
|
|
||||||
|
unless raw && raw.length == length
|
||||||
|
raise ::RuntimeError, 'Failed to read string'
|
||||||
|
end
|
||||||
|
|
||||||
|
raw
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,86 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
|
module Rex
|
||||||
|
module Proto
|
||||||
|
module Rmi
|
||||||
|
module Model
|
||||||
|
# This class provides a representation of an RMI output stream header
|
||||||
|
class OutputStream < Element
|
||||||
|
|
||||||
|
# @!attribute signature
|
||||||
|
# @return [String] the Java RMI header signature
|
||||||
|
attr_accessor :signature
|
||||||
|
# @!attribute version
|
||||||
|
# @return [Fixnum] the Java RMI version
|
||||||
|
attr_accessor :version
|
||||||
|
# @!attribute protocol
|
||||||
|
# @return [Fixnum] the protocol where the the messages are wrapped within
|
||||||
|
attr_accessor :protocol
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Reads the signature from the IO
|
||||||
|
#
|
||||||
|
# @param io [IO] the IO to read from
|
||||||
|
# @return [String]
|
||||||
|
# @raise [RuntimeError] if fails to decode signature
|
||||||
|
def decode_signature(io)
|
||||||
|
signature = read_string(io, 4)
|
||||||
|
unless signature == SIGNATURE
|
||||||
|
raise ::RuntimeError, 'Failed to decode OutputStream signature'
|
||||||
|
end
|
||||||
|
|
||||||
|
signature
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reads the version from the IO
|
||||||
|
#
|
||||||
|
# @param io [IO] the IO to read from
|
||||||
|
# @return [Fixnum]
|
||||||
|
def decode_version(io)
|
||||||
|
version = read_short(io)
|
||||||
|
|
||||||
|
version
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reads the protocol from the IO
|
||||||
|
#
|
||||||
|
# @param io [IO] the IO to read from
|
||||||
|
# @return [Fixnum]
|
||||||
|
# @raise [RuntimeError] 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 OutputStream protocol'
|
||||||
|
end
|
||||||
|
|
||||||
|
protocol
|
||||||
|
end
|
||||||
|
|
||||||
|
# Encodes the signature field
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def encode_signature
|
||||||
|
signature
|
||||||
|
end
|
||||||
|
|
||||||
|
# Encodes the version field
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def encode_version
|
||||||
|
[version].pack('n')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Encodes the protocol field
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def encode_protocol
|
||||||
|
[protocol].pack('C')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,62 @@
|
||||||
|
# -*- coding:binary -*-
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require 'stringio'
|
||||||
|
require 'rex/proto/rmi'
|
||||||
|
|
||||||
|
describe Rex::Proto::Rmi::Model::OutputStream do
|
||||||
|
|
||||||
|
subject(:output_stream) do
|
||||||
|
described_class.new
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:stream_protocol) do
|
||||||
|
"\x4a\x52\x4d\x49\x00\x02\x4b"
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:stream_protocol_io) { StringIO.new(stream_protocol) }
|
||||||
|
|
||||||
|
describe "#decode" do
|
||||||
|
context "when Stream Protocol" do
|
||||||
|
it "returns the Rex::Proto::Rmi::Model::OutputStream decoded" do
|
||||||
|
expect(output_stream.decode(stream_protocol_io)).to eq(output_stream)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "decodes signature correctly" do
|
||||||
|
output_stream.decode(stream_protocol_io)
|
||||||
|
expect(output_stream.signature).to eq(Rex::Proto::Rmi::Model::SIGNATURE)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "decodes version correctly" do
|
||||||
|
output_stream.decode(stream_protocol_io)
|
||||||
|
expect(output_stream.version).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "decodes protocol correctly" do
|
||||||
|
output_stream.decode(stream_protocol_io)
|
||||||
|
expect(output_stream.protocol).to eq(Rex::Proto::Rmi::Model::STREAM_PROTOCOL)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#encode" do
|
||||||
|
context "when Stream Protocol" do
|
||||||
|
it "encodes the OutputStream correctly" do
|
||||||
|
output_stream.signature = Rex::Proto::Rmi::Model::SIGNATURE
|
||||||
|
output_stream.version = 2
|
||||||
|
output_stream.protocol = Rex::Proto::Rmi::Model::STREAM_PROTOCOL
|
||||||
|
|
||||||
|
expect(output_stream.encode).to eq(stream_protocol)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when version field missed" do
|
||||||
|
it "doesn't encodes the version" do
|
||||||
|
output_stream.signature = Rex::Proto::Rmi::Model::SIGNATURE
|
||||||
|
output_stream.protocol = Rex::Proto::Rmi::Model::STREAM_PROTOCOL
|
||||||
|
|
||||||
|
expect(output_stream.encode).to eq("\x4a\x52\x4d\x49\x4b")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue