Add initial model and support to OutputStream

bug/bundler_fix
jvazquez-r7 2015-01-05 18:52:13 -06:00
parent 4257fef91b
commit d5dfd75e71
6 changed files with 308 additions and 0 deletions

View File

@ -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

7
lib/rex/proto/rmi.rb Normal file
View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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