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/iax2'
|
||||
require 'rex/proto/kerberos'
|
||||
require 'rex/proto/rmi'
|
||||
|
||||
module Rex
|
||||
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