Skip to content

Commit 00590f9

Browse files
author
HD Moore
committed
Adds Java serialization support, lands rapid7#4327
2 parents 6ea5ed1 + 11acba3 commit 00590f9

35 files changed

+3662
-0
lines changed

lib/rex/java.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# -*- coding: binary -*-
2+
3+
require 'rex/java/serialization'

lib/rex/java/serialization.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# -*- coding: binary -*-
2+
3+
module Rex
4+
module Java
5+
# Include constants defining terminal and constant
6+
# values expected in a stream.
7+
module Serialization
8+
STREAM_MAGIC = 0xaced
9+
STREAM_VERSION = 5
10+
TC_NULL = 0x70
11+
TC_REFERENCE = 0x71
12+
TC_CLASSDESC = 0x72
13+
TC_OBJECT = 0x73
14+
TC_STRING = 0x74
15+
TC_ARRAY = 0x75
16+
TC_CLASS = 0x76
17+
TC_BLOCKDATA = 0x77
18+
TC_ENDBLOCKDATA = 0x78
19+
TC_RESET = 0x79
20+
TC_BLOCKDATALONG = 0x7A
21+
TC_EXCEPTION = 0x7B
22+
TC_LONGSTRING = 0x7C
23+
TC_PROXYCLASSDESC = 0x7D
24+
TC_ENUM = 0x7E
25+
BASE_WIRE_HANDLE = 0x7E0000
26+
27+
SC_WRITE_METHOD = 0x01 # if SC_SERIALIZABLE
28+
SC_BLOCK_DATA = 0x08 # if SC_EXTERNALIZABLE
29+
SC_SERIALIZABLE = 0x02
30+
SC_EXTERNALIZABLE = 0x04
31+
SC_ENUM = 0x10
32+
33+
PRIMITIVE_TYPE_CODES = {
34+
'B' => 'byte',
35+
'C' => 'char',
36+
'D' => 'double',
37+
'F' => 'float',
38+
'I' => 'int',
39+
'J' => 'long',
40+
'S' => 'short',
41+
'Z' => 'boolean'
42+
}
43+
44+
OBJECT_TYPE_CODES = {
45+
'[' => 'array',
46+
'L' => 'object'
47+
}
48+
49+
TYPE_CODES = PRIMITIVE_TYPE_CODES.merge(OBJECT_TYPE_CODES)
50+
end
51+
end
52+
end
53+
54+
require 'rex/java/serialization/model'

lib/rex/java/serialization/model.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# -*- coding: binary -*-
2+
3+
require 'rex/java/serialization/model/element'
4+
require 'rex/java/serialization/model/null_reference'
5+
require 'rex/java/serialization/model/reference'
6+
require 'rex/java/serialization/model/reset'
7+
require 'rex/java/serialization/model/utf'
8+
require 'rex/java/serialization/model/long_utf'
9+
require 'rex/java/serialization/model/block_data'
10+
require 'rex/java/serialization/model/block_data_long'
11+
require 'rex/java/serialization/model/end_block_data'
12+
require 'rex/java/serialization/model/contents'
13+
require 'rex/java/serialization/model/new_enum'
14+
require 'rex/java/serialization/model/field'
15+
require 'rex/java/serialization/model/new_array'
16+
require 'rex/java/serialization/model/annotation'
17+
require 'rex/java/serialization/model/class_desc'
18+
require 'rex/java/serialization/model/new_class_desc'
19+
require 'rex/java/serialization/model/new_object'
20+
require 'rex/java/serialization/model/stream'
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# -*- coding: binary -*-
2+
3+
module Rex
4+
module Java
5+
module Serialization
6+
module Model
7+
# This class provides an annotation representation. It's used for both class
8+
# annotations (classAnnotation) and object annotations (objectAnnotation).
9+
class Annotation < Element
10+
11+
include Rex::Java::Serialization::Model::Contents
12+
13+
# @!attribute contents
14+
# @return [Array] The annotation contents
15+
attr_accessor :contents
16+
17+
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
18+
def initialize(stream = nil)
19+
super(stream)
20+
self.contents = []
21+
end
22+
23+
# Deserializes a Rex::Java::Serialization::Model::Annotation
24+
#
25+
# @param io [IO] the io to read from
26+
# @return [self] if deserialization succeeds
27+
# @raise [RuntimeError] if deserialization doesn't succeed
28+
def decode(io)
29+
loop do
30+
content = decode_content(io, stream)
31+
self.contents << content
32+
return self if content.class == EndBlockData
33+
end
34+
35+
self
36+
end
37+
38+
# Serializes the Rex::Java::Serialization::Model::Annotation
39+
#
40+
# @return [String] if serialization suceeds
41+
# @raise [RuntimeError] if serialization doesn't succeed
42+
def encode
43+
raise ::RuntimeError, 'Failed to serialize Annotation with empty contents' if contents.empty?
44+
45+
encoded = ''
46+
47+
contents.each do |content|
48+
encoded << encode_content(content)
49+
end
50+
51+
encoded
52+
end
53+
54+
# Creates a print-friendly string representation
55+
#
56+
# @return [String]
57+
def to_s
58+
str = '[ '
59+
contents_data = contents.collect {|content| "#{print_content(content)}"}
60+
str << contents_data.join(', ')
61+
str << ' ]'
62+
str
63+
end
64+
65+
end
66+
end
67+
end
68+
end
69+
end
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# -*- coding: binary -*-
2+
3+
module Rex
4+
module Java
5+
module Serialization
6+
module Model
7+
# This class provides a block data representation
8+
class BlockData < Element
9+
10+
# @!attribute length
11+
# @return [Integer] the length of the block
12+
attr_accessor :length
13+
# @!attribute contents
14+
# @return [String] the contents of the block
15+
attr_accessor :contents
16+
17+
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
18+
# @param contents [String] the contents of the block
19+
def initialize(stream = nil, contents = '')
20+
super(stream)
21+
self.contents = contents
22+
self.length = contents.length
23+
end
24+
25+
# Deserializes a Rex::Java::Serialization::Model::BlockData
26+
#
27+
# @param io [IO] the io to read from
28+
# @return [self] if deserialization succeeds
29+
# @raise [RuntimeError] if deserialization doesn't succeed
30+
def decode(io)
31+
raw_length = io.read(1)
32+
raise RuntimeError, 'Failed to unserialize BlockData' if raw_length.nil?
33+
self.length = raw_length.unpack('C')[0]
34+
35+
if length == 0
36+
self.contents = ''
37+
else
38+
self.contents = io.read(length)
39+
if contents.nil? || contents.length != length
40+
raise RuntimeError, 'Failed to unserialize BlockData'
41+
end
42+
end
43+
44+
self
45+
end
46+
47+
# Creates a print-friendly string representation
48+
#
49+
# @return [String]
50+
def to_s
51+
contents_hex = []
52+
contents.each_byte {|byte| contents_hex << "0x#{byte.to_s(16)}" }
53+
54+
"[ #{contents_hex.join(', ')} ]"
55+
end
56+
57+
# Serializes the Rex::Java::Serialization::Model::BlockData
58+
#
59+
# @return [String]
60+
def encode
61+
encoded = [length].pack('C')
62+
encoded << contents
63+
64+
encoded
65+
end
66+
end
67+
end
68+
end
69+
end
70+
end
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# -*- coding: binary -*-
2+
3+
module Rex
4+
module Java
5+
module Serialization
6+
module Model
7+
# This class provides a block data (long) representation
8+
class BlockDataLong < Element
9+
10+
# @!attribute length
11+
# @return [Integer] the length of the block
12+
attr_accessor :length
13+
# @!attribute contents
14+
# @return [String] the contents of the block
15+
attr_accessor :contents
16+
17+
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
18+
# @param contents [String] the contents of the block
19+
def initialize(stream = nil, contents = '')
20+
super(stream)
21+
self.contents = contents
22+
self.length = contents.length
23+
end
24+
25+
# Deserializes a Rex::Java::Serialization::Model::BlockDataLong
26+
#
27+
# @param io [IO] the io to read from
28+
# @return [self] if deserialization succeeds
29+
# @raise [RuntimeError] if deserialization doesn't succeed
30+
def decode(io)
31+
raw_length = io.read(4)
32+
if raw_length.nil? || raw_length.length != 4
33+
raise ::RuntimeError, 'Failed to unserialize BlockDataLong'
34+
end
35+
self.length = raw_length.unpack('N')[0]
36+
37+
if length == 0
38+
self.contents = ''
39+
else
40+
self.contents = io.read(length)
41+
if contents.nil? || contents.length != length
42+
raise ::RuntimeError, 'Failed to unserialize BlockData'
43+
end
44+
end
45+
46+
self
47+
end
48+
49+
# Serializes the Rex::Java::Serialization::Model::BlockDataLong
50+
#
51+
# @return [String]
52+
def encode
53+
encoded = [length].pack('N')
54+
encoded << contents
55+
56+
encoded
57+
end
58+
59+
# Creates a print-friendly string representation
60+
#
61+
# @return [String]
62+
def to_s
63+
contents_hex = []
64+
contents.each_byte {|byte| contents_hex << "0x#{byte.to_s(16)}" }
65+
66+
"[ #{contents_hex.join(', ')} ]"
67+
end
68+
end
69+
end
70+
end
71+
end
72+
end
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# -*- coding: binary -*-
2+
3+
module Rex
4+
module Java
5+
module Serialization
6+
module Model
7+
# This class provides a Java classDesc representation
8+
class ClassDesc < Element
9+
10+
include Rex::Java::Serialization::Model::Contents
11+
12+
attr_accessor :description
13+
14+
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
15+
def initialize(stream = nil)
16+
super(stream)
17+
self.description = nil
18+
end
19+
20+
# Deserializes a Rex::Java::Serialization::Model::ClassDesc
21+
#
22+
# @param io [IO] the io to read from
23+
# @return [self] if deserialization succeeds
24+
# @raise [RuntimeError] if deserialization doesn't succeed
25+
def decode(io)
26+
content = decode_content(io, stream)
27+
allowed_contents = [NullReference, NewClassDesc, Reference]
28+
29+
unless allowed_contents.include?(content.class)
30+
raise ::RuntimeError, 'ClassDesc unserialize failed'
31+
end
32+
33+
self.description = content
34+
self
35+
end
36+
37+
# Serializes the Rex::Java::Serialization::Model::ClassDesc
38+
#
39+
# @return [String] if serialization succeeds
40+
# @raise [RuntimeError] if serialization doesn't succeed
41+
def encode
42+
encoded = ''
43+
allowed_contents = [NullReference, NewClassDesc, Reference]
44+
45+
unless allowed_contents.include?(description.class)
46+
raise ::RuntimeError, 'Failed to serialize ClassDesc'
47+
end
48+
49+
encoded << encode_content(description)
50+
51+
encoded
52+
end
53+
54+
# Creates a print-friendly string representation
55+
#
56+
# @return [String]
57+
def to_s
58+
print_content(description)
59+
end
60+
end
61+
end
62+
end
63+
end
64+
end

0 commit comments

Comments
 (0)