metasploit-aggregator 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/bin/{msfaggregator → metasploit-aggregator} +11 -0
- data/lib/metasploit/aggregator.rb +15 -4
- data/lib/metasploit/aggregator/connection_manager.rb +18 -0
- data/lib/metasploit/aggregator/forwarder.rb +10 -4
- data/lib/metasploit/aggregator/http/responder.rb +4 -0
- data/lib/metasploit/aggregator/http_forwarder.rb +0 -3
- data/lib/metasploit/aggregator/router.rb +3 -3
- data/lib/metasploit/aggregator/session_detail_service.rb +71 -0
- data/lib/metasploit/aggregator/tlv/packet.rb +740 -0
- data/lib/metasploit/aggregator/tlv/packet_parser.rb +103 -0
- data/lib/metasploit/aggregator/tlv/uuid.rb +275 -0
- data/lib/metasploit/aggregator/version.rb +1 -1
- data/metasploit-aggregator.gemspec +2 -1
- metadata +22 -4
- metadata.gz.sig +0 -0
@@ -0,0 +1,103 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'metasploit/aggregator/tlv/packet'
|
3
|
+
|
4
|
+
module Metasploit
|
5
|
+
module Aggregator
|
6
|
+
module Tlv
|
7
|
+
###
|
8
|
+
#
|
9
|
+
# This class is responsible for reading in and decrypting meterpreter
|
10
|
+
# packets that arrive on a socket
|
11
|
+
#
|
12
|
+
###
|
13
|
+
class PacketParser
|
14
|
+
|
15
|
+
# 4 byte xor
|
16
|
+
# 4 byte length
|
17
|
+
# 4 byte type
|
18
|
+
HEADER_SIZE = 12
|
19
|
+
|
20
|
+
#
|
21
|
+
# Initializes the packet parser context with an optional cipher.
|
22
|
+
#
|
23
|
+
def initialize(cipher = nil)
|
24
|
+
self.cipher = cipher
|
25
|
+
|
26
|
+
reset
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Resets the parser state so that a new packet can begin being parsed.
|
31
|
+
#
|
32
|
+
def reset
|
33
|
+
self.raw = ''
|
34
|
+
self.hdr_length_left = HEADER_SIZE
|
35
|
+
self.payload_length_left = 0
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Reads data from the wire and parse as much of the packet as possible.
|
40
|
+
#
|
41
|
+
def recv(sock)
|
42
|
+
# Create a typeless packet
|
43
|
+
packet = Packet.new(0)
|
44
|
+
|
45
|
+
if (self.hdr_length_left > 0)
|
46
|
+
buf = sock.read(self.hdr_length_left)
|
47
|
+
|
48
|
+
if (buf)
|
49
|
+
self.raw << buf
|
50
|
+
|
51
|
+
self.hdr_length_left -= buf.length
|
52
|
+
else
|
53
|
+
raise EOFError
|
54
|
+
end
|
55
|
+
|
56
|
+
# If we've finished reading the header, set the
|
57
|
+
# payload length left to the number of bytes
|
58
|
+
# specified in the length
|
59
|
+
if (self.hdr_length_left == 0)
|
60
|
+
xor_key = raw[0, 4].unpack('N')[0]
|
61
|
+
length_bytes = packet.xor_bytes(xor_key, raw[4, 4])
|
62
|
+
# header size doesn't include the xor key, which is always tacked on the front
|
63
|
+
self.payload_length_left = length_bytes.unpack("N")[0] - (HEADER_SIZE - 4)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
if (self.payload_length_left > 0)
|
67
|
+
buf = sock.read(self.payload_length_left)
|
68
|
+
|
69
|
+
if (buf)
|
70
|
+
self.raw << buf
|
71
|
+
|
72
|
+
self.payload_length_left -= buf.length
|
73
|
+
else
|
74
|
+
raise EOFError
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# If we've finished reading the entire packet
|
79
|
+
if ((self.hdr_length_left == 0) &&
|
80
|
+
(self.payload_length_left == 0))
|
81
|
+
|
82
|
+
# TODO: cipher decryption
|
83
|
+
if (cipher)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Deserialize the packet from the raw buffer
|
87
|
+
packet.from_r(self.raw)
|
88
|
+
|
89
|
+
# Reset our state
|
90
|
+
reset
|
91
|
+
|
92
|
+
return packet
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
protected
|
97
|
+
attr_accessor :cipher, :raw, :hdr_length_left, :payload_length_left # :nodoc:
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,275 @@
|
|
1
|
+
# -*- coding => binary -*-
|
2
|
+
require 'rex/arch'
|
3
|
+
#
|
4
|
+
# This class provides methods for calculating, extracting, and parsing
|
5
|
+
# unique ID values used by payloads.
|
6
|
+
#
|
7
|
+
module Metasploit
|
8
|
+
module Aggregator
|
9
|
+
module Tlv
|
10
|
+
class UUID
|
11
|
+
|
12
|
+
include Rex::Arch
|
13
|
+
#
|
14
|
+
# Constants
|
15
|
+
#
|
16
|
+
|
17
|
+
Architectures = {
|
18
|
+
0 => nil,
|
19
|
+
1 => ARCH_X86,
|
20
|
+
2 => ARCH_X64, # removed ARCH_X86_64, now consistent across the board
|
21
|
+
3 => ARCH_X64,
|
22
|
+
4 => ARCH_MIPS,
|
23
|
+
5 => ARCH_MIPSLE,
|
24
|
+
6 => ARCH_MIPSBE,
|
25
|
+
7 => ARCH_PPC,
|
26
|
+
8 => ARCH_PPC64,
|
27
|
+
9 => ARCH_CBEA,
|
28
|
+
10 => ARCH_CBEA64,
|
29
|
+
11 => ARCH_SPARC,
|
30
|
+
12 => ARCH_ARMLE,
|
31
|
+
13 => ARCH_ARMBE,
|
32
|
+
14 => ARCH_CMD,
|
33
|
+
15 => ARCH_PHP,
|
34
|
+
16 => ARCH_TTY,
|
35
|
+
17 => ARCH_JAVA,
|
36
|
+
18 => ARCH_RUBY,
|
37
|
+
19 => ARCH_DALVIK,
|
38
|
+
20 => ARCH_PYTHON,
|
39
|
+
21 => ARCH_NODEJS,
|
40
|
+
22 => ARCH_FIREFOX,
|
41
|
+
23 => ARCH_ZARCH,
|
42
|
+
24 => ARCH_AARCH64,
|
43
|
+
25 => ARCH_MIPS64,
|
44
|
+
26 => ARCH_PPC64LE
|
45
|
+
}
|
46
|
+
|
47
|
+
Platforms = {
|
48
|
+
0 => nil,
|
49
|
+
1 => 'windows',
|
50
|
+
2 => 'netware',
|
51
|
+
3 => 'android',
|
52
|
+
4 => 'java',
|
53
|
+
5 => 'ruby',
|
54
|
+
6 => 'linux',
|
55
|
+
7 => 'cisco',
|
56
|
+
8 => 'solaris',
|
57
|
+
9 => 'osx',
|
58
|
+
10 => 'bsd',
|
59
|
+
11 => 'openbsd',
|
60
|
+
12 => 'bsdi',
|
61
|
+
13 => 'netbsd',
|
62
|
+
14 => 'freebsd',
|
63
|
+
15 => 'aix',
|
64
|
+
16 => 'hpux',
|
65
|
+
17 => 'irix',
|
66
|
+
18 => 'unix',
|
67
|
+
19 => 'php',
|
68
|
+
20 => 'js',
|
69
|
+
21 => 'python',
|
70
|
+
22 => 'nodejs',
|
71
|
+
23 => 'firefox'
|
72
|
+
}
|
73
|
+
|
74
|
+
# The raw length of the UUID structure
|
75
|
+
RawLength = 16
|
76
|
+
|
77
|
+
# The base64url-encoded length of the UUID structure
|
78
|
+
UriLength = 22
|
79
|
+
|
80
|
+
# Validity constraints for UUID timestamps in UTC
|
81
|
+
TimestampMaxFuture = Time.now.utc.to_i + (30*24*3600) # Up to 30 days in the future
|
82
|
+
TimestampMaxPast = 1420070400 # Since 2015-01-01 00:00:00 UTC
|
83
|
+
|
84
|
+
#
|
85
|
+
# Class Methods
|
86
|
+
#
|
87
|
+
|
88
|
+
#
|
89
|
+
# Parse a raw 16-byte payload UUID and return the payload ID, platform, architecture, and timestamp
|
90
|
+
#
|
91
|
+
# @param raw [String] The raw 16-byte payload UUID to parse
|
92
|
+
# @return [Hash] A hash containing the Payload ID, platform, architecture, and timestamp
|
93
|
+
#
|
94
|
+
def self.parse_raw(raw)
|
95
|
+
if raw.to_s.length < 16
|
96
|
+
raise ArgumentError, "Raw UUID must be at least 16 bytes"
|
97
|
+
end
|
98
|
+
|
99
|
+
puid, plat_xor, arch_xor, plat_id, arch_id, tstamp = raw.unpack('a8C4N')
|
100
|
+
plat = find_platform_name(plat_xor ^ plat_id)
|
101
|
+
arch = find_architecture_name(arch_xor ^ arch_id)
|
102
|
+
time_xor = [plat_xor, arch_xor, plat_xor, arch_xor].pack('C4').unpack('N').first
|
103
|
+
time = time_xor ^ tstamp
|
104
|
+
{ puid: puid, platform: plat, arch: arch, timestamp: time, xor1: plat_xor, xor2: arch_xor }
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# Filter out UUIDs with obviously invalid fields and return either
|
109
|
+
# a validated UUID or a UUID with the arch, platform, and timestamp
|
110
|
+
# fields strippped out.
|
111
|
+
#
|
112
|
+
# @param uuid [Hash] The UUID in hash format
|
113
|
+
# @return [Hash] The filtered UUID in hash format
|
114
|
+
#
|
115
|
+
def self.filter_invalid(uuid)
|
116
|
+
# Verify the UUID fields and return just the Payload ID unless the
|
117
|
+
# timestamp is within our constraints and the UUID has either a
|
118
|
+
# valid architecture or platform
|
119
|
+
if uuid[:timestamp] > TimestampMaxFuture ||
|
120
|
+
uuid[:timestamp] < TimestampMaxPast ||
|
121
|
+
(uuid[:arch].nil? && uuid[:platform].nil?)
|
122
|
+
return { puid: uuid[:puid] }
|
123
|
+
end
|
124
|
+
uuid
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
# Look up the numeric platform ID given a string or PlatformList as input
|
129
|
+
#
|
130
|
+
# @param platform [String] The name of the platform to lookup
|
131
|
+
# @return [Fixnum] The integer value of this platform
|
132
|
+
#
|
133
|
+
def self.find_platform_id(platform)
|
134
|
+
name = name.first if name.kind_of? ::Array
|
135
|
+
( Platforms.keys.select{ |k|
|
136
|
+
Platforms[k] == name
|
137
|
+
}.first || Platforms[0] ).to_i
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Look up the numeric architecture ID given a string as input
|
142
|
+
#
|
143
|
+
# @param name [String] The name of the architecture to lookup
|
144
|
+
# @return [Fixnum] The integer value of this architecture
|
145
|
+
#
|
146
|
+
def self.find_architecture_id(name)
|
147
|
+
name = name.first if name.kind_of? ::Array
|
148
|
+
( Architectures.keys.select{ |k|
|
149
|
+
Architectures[k] == name
|
150
|
+
}.first || Architectures[0] ).to_i
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.find_platform_name(num)
|
154
|
+
Platforms[num]
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.find_architecture_name(num)
|
158
|
+
Architectures[num]
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Instance methods
|
163
|
+
#
|
164
|
+
|
165
|
+
def initialize(opts=nil)
|
166
|
+
opts = load_new if opts.nil?
|
167
|
+
opts = load_raw(opts[:raw]) if opts[:raw]
|
168
|
+
|
169
|
+
self.puid = opts[:puid]
|
170
|
+
self.timestamp = opts[:timestamp]
|
171
|
+
self.arch = opts[:arch]
|
172
|
+
self.platform = opts[:platform]
|
173
|
+
self.xor1 = opts[:xor1]
|
174
|
+
self.xor2 = opts[:xor2]
|
175
|
+
|
176
|
+
# Generate some sensible defaults
|
177
|
+
self.puid ||= SecureRandom.random_bytes(8)
|
178
|
+
self.xor1 ||= rand(256)
|
179
|
+
self.xor2 ||= rand(256)
|
180
|
+
self.timestamp ||= Time.now.utc.to_i
|
181
|
+
end
|
182
|
+
|
183
|
+
#
|
184
|
+
# Initializes a UUID object given a raw 16+ byte blob
|
185
|
+
#
|
186
|
+
# @param raw [String] The string containing at least 16 bytes of encoded data
|
187
|
+
# @return [Hash] The attributes encoded into this UUID
|
188
|
+
#
|
189
|
+
def load_raw(raw)
|
190
|
+
self.class.filter_invalid(self.class.parse_raw(raw))
|
191
|
+
end
|
192
|
+
|
193
|
+
def load_new
|
194
|
+
self.class.parse_raw(self.class.generate_raw())
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# Provides a string representation of a UUID
|
199
|
+
#
|
200
|
+
# @return [String] The human-readable version of the UUID data
|
201
|
+
#
|
202
|
+
def to_s
|
203
|
+
arch_id = self.class.find_architecture_id(self.arch).to_s
|
204
|
+
plat_id = self.class.find_platform_id(self.platform).to_s
|
205
|
+
[
|
206
|
+
self.puid_hex,
|
207
|
+
[ self.arch || "noarch", arch_id ].join("="),
|
208
|
+
[ self.platform || "noplatform", plat_id ].join("="),
|
209
|
+
Time.at(self.timestamp.to_i).utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
210
|
+
].join("/")
|
211
|
+
end
|
212
|
+
|
213
|
+
#
|
214
|
+
# Return a string that represents the Meterpreter arch/platform
|
215
|
+
#
|
216
|
+
def session_type
|
217
|
+
# mini-patch for x86 so that it renders x64 instead. This is
|
218
|
+
# mostly to keep various external modules happy.
|
219
|
+
arch = self.arch
|
220
|
+
if arch == ARCH_X86_64
|
221
|
+
arch = ARCH_X64
|
222
|
+
end
|
223
|
+
"#{arch}/#{self.platform}"
|
224
|
+
end
|
225
|
+
|
226
|
+
#
|
227
|
+
# Provides a hash representation of a UUID
|
228
|
+
#
|
229
|
+
# @return [Hash] The hash representation of the UUID suitable for creating a new one
|
230
|
+
#
|
231
|
+
def to_h
|
232
|
+
{
|
233
|
+
puid: self.puid,
|
234
|
+
arch: self.arch, platform: self.platform,
|
235
|
+
timestamp: self.timestamp,
|
236
|
+
xor1: self.xor1, xor2: self.xor2
|
237
|
+
}
|
238
|
+
end
|
239
|
+
|
240
|
+
#
|
241
|
+
# Provides a raw byte representation of a UUID
|
242
|
+
#
|
243
|
+
# @return [String] The 16-byte raw encoded version of the UUID
|
244
|
+
#
|
245
|
+
def to_raw
|
246
|
+
self.class.generate_raw(self.to_h)
|
247
|
+
end
|
248
|
+
|
249
|
+
#
|
250
|
+
# Provides a hex representation of the Payload UID of the UUID
|
251
|
+
#
|
252
|
+
# @return [String] The 16-byte hex string representing the Payload UID
|
253
|
+
#
|
254
|
+
def puid_hex
|
255
|
+
self.puid.unpack('H*').first
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
# Clears the two random XOR keys used for obfuscation
|
260
|
+
#
|
261
|
+
def xor_reset
|
262
|
+
self.xor1 = self.xor2 = nil
|
263
|
+
self
|
264
|
+
end
|
265
|
+
|
266
|
+
attr_accessor :arch
|
267
|
+
attr_accessor :platform
|
268
|
+
attr_accessor :timestamp
|
269
|
+
attr_accessor :puid
|
270
|
+
attr_accessor :xor1
|
271
|
+
attr_accessor :xor2
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
f.match(%r{^(test|spec|features)/})
|
18
18
|
end
|
19
19
|
spec.bindir = "bin"
|
20
|
-
spec.executables << '
|
20
|
+
spec.executables << 'metasploit-aggregator'
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.13"
|
@@ -25,4 +25,5 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.0"
|
26
26
|
spec.add_runtime_dependency 'msgpack'
|
27
27
|
spec.add_runtime_dependency 'msgpack-rpc'
|
28
|
+
spec.add_runtime_dependency 'rex-arch'
|
28
29
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metasploit-aggregator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Metasploit Hackers
|
@@ -88,7 +88,7 @@ cert_chain:
|
|
88
88
|
G+Hmcg1v810agasPdoydE0RTVZgEOOMoQ07qu7JFXVWZ9ZQpHT7qJATWL/b2csFG
|
89
89
|
8mVuTXnyJOKRJA==
|
90
90
|
-----END CERTIFICATE-----
|
91
|
-
date: 2017-
|
91
|
+
date: 2017-02-02 00:00:00.000000000 Z
|
92
92
|
dependencies:
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
94
|
name: bundler
|
@@ -160,11 +160,25 @@ dependencies:
|
|
160
160
|
- - ">="
|
161
161
|
- !ruby/object:Gem::Version
|
162
162
|
version: '0'
|
163
|
+
- !ruby/object:Gem::Dependency
|
164
|
+
name: rex-arch
|
165
|
+
requirement: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
type: :runtime
|
171
|
+
prerelease: false
|
172
|
+
version_requirements: !ruby/object:Gem::Requirement
|
173
|
+
requirements:
|
174
|
+
- - ">="
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: '0'
|
163
177
|
description: metasploit-aggregator
|
164
178
|
email:
|
165
179
|
- metasploit-hackers@lists.sourceforge.net
|
166
180
|
executables:
|
167
|
-
-
|
181
|
+
- metasploit-aggregator
|
168
182
|
extensions: []
|
169
183
|
extra_rdoc_files: []
|
170
184
|
files:
|
@@ -177,7 +191,7 @@ files:
|
|
177
191
|
- LICENSE
|
178
192
|
- README.md
|
179
193
|
- Rakefile
|
180
|
-
- bin/
|
194
|
+
- bin/metasploit-aggregator
|
181
195
|
- lib/metasploit/aggregator.rb
|
182
196
|
- lib/metasploit/aggregator/cable.rb
|
183
197
|
- lib/metasploit/aggregator/connection_manager.rb
|
@@ -192,6 +206,10 @@ files:
|
|
192
206
|
- lib/metasploit/aggregator/https_forwarder.rb
|
193
207
|
- lib/metasploit/aggregator/logger.rb
|
194
208
|
- lib/metasploit/aggregator/router.rb
|
209
|
+
- lib/metasploit/aggregator/session_detail_service.rb
|
210
|
+
- lib/metasploit/aggregator/tlv/packet.rb
|
211
|
+
- lib/metasploit/aggregator/tlv/packet_parser.rb
|
212
|
+
- lib/metasploit/aggregator/tlv/uuid.rb
|
195
213
|
- lib/metasploit/aggregator/version.rb
|
196
214
|
- metasploit-aggregator.gemspec
|
197
215
|
homepage: https://www.msf.com
|