metasploit-aggregator 0.2.3 → 1.0.0
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 +1 -3
- data/lib/metasploit/aggregator/http/request.rb +32 -9
- data/lib/metasploit/aggregator/http/responder.rb +27 -7
- data/lib/metasploit/aggregator/session_detail_service.rb +72 -16
- data/lib/metasploit/aggregator/tlv/packet.rb +215 -51
- data/lib/metasploit/aggregator/version.rb +1 -1
- metadata +2 -3
- metadata.gz.sig +0 -0
- data/lib/metasploit/aggregator/tlv/packet_parser.rb +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 309e4618d12318d87a16f47501dadddd40d1f22e
|
4
|
+
data.tar.gz: 4311c39b35821464b197ac1ab674529d13e04559
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2217561262a7f697e016c314505353f887a221ee3ccfde1f90760589ea5c702f7b80e1eb55aecf1e50feb5ff1e22f013c4489f091200a9a8b73d7534f9d058b6
|
7
|
+
data.tar.gz: b40d75c19d279a179c63273acca9300817f36f239223f86cac718ec9434a233d12f6f3c8f08408edbf8da4a3c0022de105a105fd3d680929df13f57aafd70dbe
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
@@ -1,3 +1 @@
|
|
1
|
-
"..S~s?϶6��֧fOL�JG|p�E��H��~3��c�+Ѳ��=榃D������m���;�
|
2
|
-
q00؏�ϐ��TbUҔ�X�A�}��R���ء�
|
3
|
-
���{��&�e�����>��F�m!�Poఠw&&�V68H��(�.���[Ȱ�=K�M:�֔6ݴf�����TDC�HܶBlM
|
4
1
|
ډ��n�7÷p�T�c@�� w4�T��W��v��'��D��3Y
|
2
|
+
v�1�~�ܸ�[��]��,.�����?�v�7?��n�[�UY����v�6Ht��F��:��L�y��Nal�g�E�?�g[z9����<�.$t�?-ٶ�D�c"3E�)���>������2nI����%�T��}�K�E�L/I�x�?���O(�dpU&m�vK��v]�c=d���zO�"���Kr[��Ȧ-٭��ִx��jy�ܧGIF"��V^�(��mb̌�������K��fIɎ��B7��
|
@@ -25,16 +25,39 @@ module Metasploit
|
|
25
25
|
|
26
26
|
# provide a default response in Request form
|
27
27
|
def self.parked()
|
28
|
-
|
29
|
-
parked_message << 'HTTP/1.1 200 OK'
|
30
|
-
parked_message << 'Content-Type: application/octet-stream'
|
31
|
-
parked_message << 'Connection: close'
|
32
|
-
parked_message << 'Server: Apache'
|
33
|
-
parked_message << 'Content-Length: 0'
|
34
|
-
parked_message << ' '
|
35
|
-
parked_message << ' '
|
36
|
-
self.new(parked_message, '', nil)
|
28
|
+
generate_response(nil)
|
37
29
|
end
|
30
|
+
|
31
|
+
def self.generate_response(http_request)
|
32
|
+
socket = nil
|
33
|
+
body = ''
|
34
|
+
unless http_request.nil? || http_request.body.nil?
|
35
|
+
body = http_request.body
|
36
|
+
end
|
37
|
+
message_headers = []
|
38
|
+
message_headers << 'HTTP/1.1 200 OK' + "\n"
|
39
|
+
message_headers << 'Content-Type: application/octet-stream' + "\n"
|
40
|
+
message_headers << 'Connection: close' + "\n"
|
41
|
+
message_headers << 'Server: Apache' + "\n"
|
42
|
+
message_headers << 'Content-Length: ' + body.length.to_s + "\n"
|
43
|
+
message_headers << '' + "\n"
|
44
|
+
message_headers << '' + "\n"
|
45
|
+
self.new(message_headers, body, socket)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.forge_request(uri, body, socket = nil)
|
49
|
+
message_headers = []
|
50
|
+
message_headers << "POST #{uri}/ HTTP/1.1" + "\n"
|
51
|
+
message_headers << 'Accept-Encoding: identity' + "\n"
|
52
|
+
message_headers << 'Content-Length: ' + body.length.to_s + "\n"
|
53
|
+
message_headers << 'Host: 127.0.0.1:2447' + "\n" # this value is defaulted to reflect the aggregator
|
54
|
+
message_headers << 'Content-Type: application/octet-stream' + "\n"
|
55
|
+
message_headers << 'Connection: close' + "\n"
|
56
|
+
message_headers << 'User-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko'+ "\n"
|
57
|
+
message_headers << '' + "\n"
|
58
|
+
self.new(message_headers, body, socket)
|
59
|
+
end
|
60
|
+
|
38
61
|
end
|
39
62
|
end
|
40
63
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "metasploit/aggregator/session_detail_service"
|
2
2
|
require "metasploit/aggregator/http/request"
|
3
|
+
require "metasploit/aggregator/logger"
|
3
4
|
|
4
5
|
module Metasploit
|
5
6
|
module Aggregator
|
@@ -50,7 +51,17 @@ module Metasploit
|
|
50
51
|
# now get the response once available and send back using this connection
|
51
52
|
begin
|
52
53
|
request_obj = recv.pop
|
53
|
-
@session_service.add_request(
|
54
|
+
@session_service.add_request(request_obj, @uri)
|
55
|
+
tlv_response = @session_service.eval_tlv_enc(request_obj)
|
56
|
+
unless tlv_response.nil?
|
57
|
+
# build a new request with a the new tlv
|
58
|
+
suppression = Metasploit::Aggregator::Http::Request.forge_request(@uri, tlv_response.to_r, connection)
|
59
|
+
send << suppression
|
60
|
+
log "Suppressing cryptTLV on session #{@uri}"
|
61
|
+
send << request_task
|
62
|
+
request_obj = recv.pop
|
63
|
+
@session_service.add_request(suppression, @uri)
|
64
|
+
end
|
54
65
|
@pending_request = nil
|
55
66
|
request_obj.headers.each do |line|
|
56
67
|
connection.write line
|
@@ -59,9 +70,10 @@ module Metasploit
|
|
59
70
|
connection.write request_obj.body
|
60
71
|
end
|
61
72
|
connection.flush
|
73
|
+
@session_service.add_request(request_obj, @uri)
|
62
74
|
log 'message delivered from console'
|
63
|
-
rescue
|
64
|
-
log
|
75
|
+
rescue Exception
|
76
|
+
log "error processing console response for #{@uri}"
|
65
77
|
end
|
66
78
|
close_connection(connection)
|
67
79
|
rescue Exception => e
|
@@ -82,10 +94,18 @@ module Metasploit
|
|
82
94
|
def send_parked_response(connection)
|
83
95
|
address = connection.peeraddr[3]
|
84
96
|
log "sending parked response to #{address}"
|
85
|
-
Metasploit::Aggregator::Http::Request.parked
|
86
|
-
|
97
|
+
send_response(Metasploit::Aggregator::Http::Request.parked, connection)
|
98
|
+
end
|
99
|
+
|
100
|
+
def send_response(request_obj, connection)
|
101
|
+
@pending_request = nil
|
102
|
+
request_obj.headers.each do |line|
|
103
|
+
connection.write line
|
104
|
+
end
|
105
|
+
unless request_obj.body.nil?
|
106
|
+
connection.write request_obj.body
|
87
107
|
end
|
88
|
-
|
108
|
+
connection.flush
|
89
109
|
end
|
90
110
|
|
91
111
|
def self.get_data(connection, guaranteed_length)
|
@@ -114,7 +134,7 @@ module Metasploit
|
|
114
134
|
body += connection.read(content_length - body.length)
|
115
135
|
end
|
116
136
|
end
|
117
|
-
Request.new request_lines, body, connection
|
137
|
+
Metasploit::Aggregator::Http::Request.new request_lines, body, connection
|
118
138
|
end
|
119
139
|
|
120
140
|
def get_connection(host, port)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'digest/md5'
|
2
2
|
require 'singleton'
|
3
|
+
require 'metasploit/aggregator/logger'
|
3
4
|
require 'metasploit/aggregator/tlv/packet'
|
4
|
-
require 'metasploit/aggregator/tlv/packet_parser'
|
5
5
|
require 'metasploit/aggregator/tlv/uuid'
|
6
6
|
|
7
7
|
module Metasploit
|
@@ -12,19 +12,22 @@ module Metasploit
|
|
12
12
|
def initialize
|
13
13
|
@mutex = Mutex.new
|
14
14
|
@tlv_queue = Queue.new
|
15
|
-
@thread =
|
16
|
-
# for now since all data is http no cipher is required on the parser
|
17
|
-
@parser = Metasploit::Aggregator::Tlv::PacketParser.new
|
18
|
-
@r, @w = IO.pipe
|
15
|
+
@thread = create_processor
|
19
16
|
@payloads_count = 0;
|
20
17
|
@detail_cache = {}
|
21
18
|
end
|
22
19
|
|
23
20
|
def add_request(request, payload)
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
@mutex.synchronize do
|
22
|
+
@tlv_queue << [ request, payload ]
|
23
|
+
end
|
24
|
+
begin
|
25
|
+
if @detail_cache[payload] && @detail_cache[payload]['REMOTE_SOCKET'].nil? && request.socket
|
26
|
+
@detail_cache[payload]['REMOTE_SOCKET'] = "#{request.socket.peeraddr[3]}:#{request.socket.peeraddr[1]}"
|
27
|
+
@detail_cache[payload]['LOCAL_SOCKET'] = "#{request.socket.addr[3]}:#{request.socket.addr[1]}"
|
28
|
+
end
|
29
|
+
rescue Exception
|
30
|
+
Logger.log "error retrieving socket details"
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
@@ -32,15 +35,40 @@ module Metasploit
|
|
32
35
|
@detail_cache[payload]
|
33
36
|
end
|
34
37
|
|
38
|
+
def eval_tlv_enc(request)
|
39
|
+
# this is really expensive as we have to process every
|
40
|
+
# piece of information presented from the console to eval for enc requests
|
41
|
+
response = nil
|
42
|
+
begin
|
43
|
+
if request.body && request.body.length > 0
|
44
|
+
packet = Metasploit::Aggregator::Tlv::Packet.new(0)
|
45
|
+
packet.add_raw(request.body)
|
46
|
+
packet.from_r
|
47
|
+
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_METHOD)
|
48
|
+
packet_val = packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_METHOD)
|
49
|
+
if packet_val == "core_negotiate_tlv_encryption"
|
50
|
+
response = Metasploit::Aggregator::Tlv::Packet.create_response(packet)
|
51
|
+
response.add_tlv(Metasploit::Aggregator::Tlv::TLV_TYPE_RESULT, 0)
|
52
|
+
response
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
rescue Exception
|
57
|
+
# any exception return nil
|
58
|
+
Logger.log "error evaluating tlv packet"
|
59
|
+
response = nil
|
60
|
+
end
|
61
|
+
response
|
62
|
+
end
|
63
|
+
|
35
64
|
def process_tlv
|
36
65
|
while true
|
37
66
|
begin
|
38
67
|
request, payload = @tlv_queue.pop
|
39
68
|
if request.body && request.body.length > 0
|
40
|
-
|
41
|
-
|
42
|
-
packet
|
43
|
-
next unless packet
|
69
|
+
packet = Metasploit::Aggregator::Tlv::Packet.new(0)
|
70
|
+
packet.add_raw(request.body)
|
71
|
+
packet.from_r
|
44
72
|
unless @detail_cache[payload]
|
45
73
|
@detail_cache[payload] = { 'ID' => (@payloads_count += 1) }
|
46
74
|
end
|
@@ -49,7 +77,12 @@ module Metasploit
|
|
49
77
|
@detail_cache[payload]['UUID'] = Metasploit::Aggregator::Tlv::UUID.new(args)
|
50
78
|
end
|
51
79
|
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_MACHINE_ID)
|
52
|
-
|
80
|
+
machine_id = packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_MACHINE_ID)
|
81
|
+
@detail_cache[payload]['MachineID'] = Digest::MD5.hexdigest(machine_id.downcase.strip)
|
82
|
+
_user, computer_name = machine_id.split(":")
|
83
|
+
unless computer_name.nil?
|
84
|
+
@detail_cache[payload]['HOSTNAME'] = computer_name
|
85
|
+
end
|
53
86
|
end
|
54
87
|
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_USER_NAME)
|
55
88
|
@detail_cache[payload]['USER'] = packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_USER_NAME)
|
@@ -60,12 +93,35 @@ module Metasploit
|
|
60
93
|
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_OS_NAME)
|
61
94
|
@detail_cache[payload]['OS'] = packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_OS_NAME)
|
62
95
|
end
|
96
|
+
|
97
|
+
# remove sessions that get shutdown
|
98
|
+
if packet.has_tlv?(Metasploit::Aggregator::Tlv::TLV_TYPE_METHOD)
|
99
|
+
packet_val = packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_METHOD)
|
100
|
+
if packet_val == "core_shutdown"
|
101
|
+
@detail_cache.delete(payload)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
rescue Exception
|
106
|
+
Logger.log "error processing tlv for session details"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def create_processor
|
112
|
+
processor = Thread.new do
|
113
|
+
while true # always restart the processor
|
114
|
+
begin
|
115
|
+
process_tlv
|
116
|
+
rescue Exception
|
117
|
+
Logger.log "tlv processing thread error -- restarting"
|
63
118
|
end
|
64
|
-
rescue
|
65
|
-
Logger.log $!
|
66
119
|
end
|
67
120
|
end
|
121
|
+
processor
|
68
122
|
end
|
123
|
+
|
124
|
+
private :create_processor
|
69
125
|
end
|
70
126
|
end
|
71
127
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- coding: binary -*-
|
2
|
+
require 'openssl'
|
3
|
+
require 'rex/text'
|
2
4
|
|
3
5
|
module Metasploit
|
4
6
|
module Aggregator
|
@@ -80,7 +82,7 @@ module Metasploit
|
|
80
82
|
TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 400
|
81
83
|
TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401
|
82
84
|
TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402
|
83
|
-
|
85
|
+
TLV_TYPE_MIGRATE_PAYLOAD_LEN = TLV_META_TYPE_UINT | 403
|
84
86
|
TLV_TYPE_MIGRATE_PAYLOAD = TLV_META_TYPE_STRING | 404
|
85
87
|
TLV_TYPE_MIGRATE_ARCH = TLV_META_TYPE_UINT | 405
|
86
88
|
TLV_TYPE_MIGRATE_BASE_ADDR = TLV_META_TYPE_UINT | 407
|
@@ -105,9 +107,21 @@ module Metasploit
|
|
105
107
|
|
106
108
|
TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460
|
107
109
|
TLV_TYPE_UUID = TLV_META_TYPE_RAW | 461
|
110
|
+
TLV_TYPE_SESSION_GUID = TLV_META_TYPE_RAW | 462
|
111
|
+
|
112
|
+
TLV_TYPE_RSA_PUB_KEY = TLV_META_TYPE_STRING | 550
|
113
|
+
TLV_TYPE_SYM_KEY_TYPE = TLV_META_TYPE_UINT | 551
|
114
|
+
TLV_TYPE_SYM_KEY = TLV_META_TYPE_RAW | 552
|
115
|
+
TLV_TYPE_ENC_SYM_KEY = TLV_META_TYPE_RAW | 553
|
116
|
+
|
117
|
+
#
|
118
|
+
# Pivots
|
119
|
+
#
|
120
|
+
TLV_TYPE_PIVOT_ID = TLV_META_TYPE_RAW | 650
|
121
|
+
TLV_TYPE_PIVOT_STAGE_DATA = TLV_META_TYPE_RAW | 651
|
122
|
+
TLV_TYPE_PIVOT_STAGE_DATA_SIZE = TLV_META_TYPE_UINT | 652
|
123
|
+
TLV_TYPE_PIVOT_NAMED_PIPE_NAME = TLV_META_TYPE_STRING | 653
|
108
124
|
|
109
|
-
TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500
|
110
|
-
TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501
|
111
125
|
|
112
126
|
#
|
113
127
|
# Core flags
|
@@ -129,6 +143,12 @@ module Metasploit
|
|
129
143
|
TLV_TYPE_LOGGED_ON_USER_COUNT = TLV_META_TYPE_UINT | 1047
|
130
144
|
TLV_TYPE_LOCAL_DATETIME = TLV_META_TYPE_STRING | 1048
|
131
145
|
|
146
|
+
#
|
147
|
+
# Sane defaults
|
148
|
+
#
|
149
|
+
GUID_SIZE = 16
|
150
|
+
NULL_GUID = "\x00" * GUID_SIZE
|
151
|
+
|
132
152
|
###
|
133
153
|
#
|
134
154
|
# Base TLV (Type-Length-Value) class
|
@@ -137,6 +157,8 @@ module Metasploit
|
|
137
157
|
class Tlv
|
138
158
|
attr_accessor :type, :value, :compress
|
139
159
|
|
160
|
+
HEADER_SIZE = 8
|
161
|
+
|
140
162
|
##
|
141
163
|
#
|
142
164
|
# Constructor
|
@@ -254,7 +276,7 @@ module Metasploit
|
|
254
276
|
end
|
255
277
|
|
256
278
|
# check if the tlv is to be compressed...
|
257
|
-
if
|
279
|
+
if @compress
|
258
280
|
raw_uncompressed = raw
|
259
281
|
# compress the raw data
|
260
282
|
raw_compressed = Rex::Text.zlib_deflate( raw_uncompressed )
|
@@ -270,7 +292,7 @@ module Metasploit
|
|
270
292
|
end
|
271
293
|
end
|
272
294
|
|
273
|
-
|
295
|
+
[raw.length + HEADER_SIZE, self.type].pack("NN") + raw
|
274
296
|
end
|
275
297
|
|
276
298
|
#
|
@@ -286,19 +308,22 @@ module Metasploit
|
|
286
308
|
# set this TLV as using compression
|
287
309
|
@compress = true
|
288
310
|
# remove the TLV_META_TYPE_COMPRESSED flag from the tlv type to restore the
|
289
|
-
# tlv type to its
|
311
|
+
# tlv type to its original, allowing for transparent data compression.
|
290
312
|
self.type = self.type ^ TLV_META_TYPE_COMPRESSED
|
291
313
|
# decompress the compressed data (skipping the length and type DWORD's)
|
292
|
-
|
293
|
-
#
|
294
|
-
|
295
|
-
#
|
296
|
-
|
314
|
+
|
315
|
+
# disable raw_decompression for now - not needed by aggregator at this time
|
316
|
+
# raw_decompressed = Rex::Text.zlib_inflate( raw[HEADER_SIZE..length-1] )
|
317
|
+
#
|
318
|
+
# # update the length to reflect the decompressed data length (+HEADER_SIZE for the length and type DWORD's)
|
319
|
+
# length = raw_decompressed.length + HEADER_SIZE
|
320
|
+
# # update the raw buffer with the new length, decompressed data and updated type.
|
321
|
+
# raw = [length, self.type].pack("NN") + raw_decompressed
|
297
322
|
end
|
298
323
|
|
299
324
|
if (self.type & TLV_META_TYPE_STRING == TLV_META_TYPE_STRING)
|
300
325
|
if (raw.length > 0)
|
301
|
-
self.value = raw[
|
326
|
+
self.value = raw[HEADER_SIZE..length-2]
|
302
327
|
else
|
303
328
|
self.value = nil
|
304
329
|
end
|
@@ -316,23 +341,24 @@ module Metasploit
|
|
316
341
|
self.value = false
|
317
342
|
end
|
318
343
|
else
|
319
|
-
self.value = raw[
|
344
|
+
self.value = raw[HEADER_SIZE..length-1]
|
320
345
|
end
|
321
346
|
|
322
|
-
|
347
|
+
length
|
323
348
|
end
|
324
349
|
|
325
350
|
protected
|
326
351
|
|
327
|
-
def htonq(
|
328
|
-
if
|
352
|
+
def htonq(value)
|
353
|
+
if [1].pack( 's' ) == [1].pack('n')
|
329
354
|
return value
|
355
|
+
else
|
356
|
+
[value].pack('Q<').reverse.unpack('Q<').first
|
330
357
|
end
|
331
|
-
return [ value ].pack( 'Q<' ).reverse.unpack( 'Q<' ).first
|
332
358
|
end
|
333
359
|
|
334
|
-
def ntohq(
|
335
|
-
|
360
|
+
def ntohq(value)
|
361
|
+
htonq(value)
|
336
362
|
end
|
337
363
|
|
338
364
|
end
|
@@ -358,7 +384,7 @@ module Metasploit
|
|
358
384
|
def initialize(type)
|
359
385
|
super(type)
|
360
386
|
|
361
|
-
self.tlvs = [
|
387
|
+
self.tlvs = []
|
362
388
|
end
|
363
389
|
|
364
390
|
##
|
@@ -399,8 +425,8 @@ module Metasploit
|
|
399
425
|
# Returns an array of TLVs for the given type.
|
400
426
|
#
|
401
427
|
def get_tlvs(type)
|
402
|
-
if
|
403
|
-
|
428
|
+
if type == TLV_TYPE_ANY
|
429
|
+
self.tlvs
|
404
430
|
else
|
405
431
|
type_tlvs = []
|
406
432
|
|
@@ -410,7 +436,7 @@ module Metasploit
|
|
410
436
|
end
|
411
437
|
}
|
412
438
|
|
413
|
-
|
439
|
+
type_tlvs
|
414
440
|
end
|
415
441
|
end
|
416
442
|
|
@@ -426,7 +452,7 @@ module Metasploit
|
|
426
452
|
def add_tlv(type, value = nil, replace = false, compress=false)
|
427
453
|
|
428
454
|
# If we should replace any TLVs with the same type...remove them first
|
429
|
-
if
|
455
|
+
if replace
|
430
456
|
each(type) { |tlv|
|
431
457
|
if (tlv.type == type)
|
432
458
|
self.tlvs.delete(tlv)
|
@@ -442,14 +468,14 @@ module Metasploit
|
|
442
468
|
|
443
469
|
self.tlvs << tlv
|
444
470
|
|
445
|
-
|
471
|
+
tlv
|
446
472
|
end
|
447
473
|
|
448
474
|
#
|
449
475
|
# Adds zero or more TLVs to the packet.
|
450
476
|
#
|
451
477
|
def add_tlvs(tlvs)
|
452
|
-
if
|
478
|
+
if tlvs
|
453
479
|
tlvs.each { |tlv|
|
454
480
|
add_tlv(tlv['type'], tlv['value'])
|
455
481
|
}
|
@@ -462,11 +488,12 @@ module Metasploit
|
|
462
488
|
def get_tlv(type, index = 0)
|
463
489
|
type_tlvs = get_tlvs(type)
|
464
490
|
|
465
|
-
if
|
466
|
-
|
491
|
+
if type_tlvs.length > index
|
492
|
+
type_tlvs[index]
|
493
|
+
else
|
494
|
+
nil
|
467
495
|
end
|
468
496
|
|
469
|
-
return nil
|
470
497
|
end
|
471
498
|
|
472
499
|
#
|
@@ -475,7 +502,7 @@ module Metasploit
|
|
475
502
|
def get_tlv_value(type, index = 0)
|
476
503
|
tlv = get_tlv(type, index)
|
477
504
|
|
478
|
-
|
505
|
+
(tlv != nil) ? tlv.value : nil
|
479
506
|
end
|
480
507
|
|
481
508
|
#
|
@@ -489,7 +516,7 @@ module Metasploit
|
|
489
516
|
# Checks to see if the container has a TLV of a given type.
|
490
517
|
#
|
491
518
|
def has_tlv?(type)
|
492
|
-
|
519
|
+
get_tlv(type) != nil
|
493
520
|
end
|
494
521
|
|
495
522
|
#
|
@@ -516,7 +543,7 @@ module Metasploit
|
|
516
543
|
raw << tlv.to_r
|
517
544
|
}
|
518
545
|
|
519
|
-
|
546
|
+
[raw.length + HEADER_SIZE, self.type].pack("NN") + raw
|
520
547
|
end
|
521
548
|
|
522
549
|
#
|
@@ -524,19 +551,19 @@ module Metasploit
|
|
524
551
|
# TLVs.
|
525
552
|
#
|
526
553
|
def from_r(raw)
|
527
|
-
offset =
|
554
|
+
offset = HEADER_SIZE
|
528
555
|
|
529
556
|
# Reset the TLVs array
|
530
557
|
self.tlvs = []
|
531
558
|
self.type = raw.unpack("NN")[1]
|
532
559
|
|
533
560
|
# Enumerate all of the TLVs
|
534
|
-
while
|
561
|
+
while offset < raw.length-1
|
535
562
|
|
536
563
|
tlv = nil
|
537
564
|
|
538
565
|
# Get the length and type
|
539
|
-
length, type = raw[offset..offset+
|
566
|
+
length, type = raw[offset..offset+HEADER_SIZE].unpack("NN")
|
540
567
|
|
541
568
|
if (type & TLV_META_TYPE_GROUP == TLV_META_TYPE_GROUP)
|
542
569
|
tlv = GroupTlv.new(type)
|
@@ -563,6 +590,49 @@ module Metasploit
|
|
563
590
|
###
|
564
591
|
class Packet < GroupTlv
|
565
592
|
attr_accessor :created_at
|
593
|
+
attr_accessor :raw
|
594
|
+
attr_accessor :session_guid
|
595
|
+
attr_accessor :encrypt_flags
|
596
|
+
attr_accessor :length
|
597
|
+
|
598
|
+
##
|
599
|
+
#
|
600
|
+
# The Packet container itself has a custom header that is slightly different to the
|
601
|
+
# typical TLV packets. The header contains the following:
|
602
|
+
#
|
603
|
+
# XOR KEY - 4 bytes
|
604
|
+
# Session GUID - 16 bytes
|
605
|
+
# Encrypt flags - 4 bytes
|
606
|
+
# Packet length - 4 bytes
|
607
|
+
# Packet type - 4 bytes
|
608
|
+
# Packet data - X bytes
|
609
|
+
#
|
610
|
+
# If the encrypt flags are zero, then the Packet data is just straight TLV values as
|
611
|
+
# per the normal TLV packet structure.
|
612
|
+
#
|
613
|
+
# If the encrypt flags are non-zer, then the Packet data is encrypted based on the scheme.
|
614
|
+
#
|
615
|
+
# Flag == 1 (AES256)
|
616
|
+
# IV - 16 bytes
|
617
|
+
# Encrypted data - X bytes
|
618
|
+
#
|
619
|
+
# The key that is required to decrypt the data is stored alongside the session data,
|
620
|
+
# and hence when the packet is initially parsed, only the header is accessed. The
|
621
|
+
# packet itself will need to be decrypted on the fly at the point that it is required
|
622
|
+
# and at that point the decryption key needs to be provided.
|
623
|
+
#
|
624
|
+
###
|
625
|
+
|
626
|
+
XOR_KEY_SIZE = 4
|
627
|
+
ENCRYPTED_FLAGS_SIZE = 4
|
628
|
+
PACKET_LENGTH_SIZE = 4
|
629
|
+
PACKET_TYPE_SIZE = 4
|
630
|
+
PACKET_HEADER_SIZE = XOR_KEY_SIZE + GUID_SIZE + ENCRYPTED_FLAGS_SIZE + PACKET_LENGTH_SIZE + PACKET_TYPE_SIZE
|
631
|
+
|
632
|
+
AES_IV_SIZE = 16
|
633
|
+
|
634
|
+
ENC_FLAG_NONE = 0x0
|
635
|
+
ENC_FLAG_AES256 = 0x1
|
566
636
|
|
567
637
|
##
|
568
638
|
#
|
@@ -574,7 +644,7 @@ module Metasploit
|
|
574
644
|
# Creates a request with the supplied method.
|
575
645
|
#
|
576
646
|
def Packet.create_request(method = nil)
|
577
|
-
|
647
|
+
Packet.new(PACKET_TYPE_REQUEST, method)
|
578
648
|
end
|
579
649
|
|
580
650
|
#
|
@@ -583,6 +653,7 @@ module Metasploit
|
|
583
653
|
def Packet.create_response(request = nil)
|
584
654
|
response_type = PACKET_TYPE_RESPONSE
|
585
655
|
method = nil
|
656
|
+
id = nil
|
586
657
|
|
587
658
|
if (request)
|
588
659
|
if (request.type?(PACKET_TYPE_PLAIN_REQUEST))
|
@@ -590,9 +661,19 @@ module Metasploit
|
|
590
661
|
end
|
591
662
|
|
592
663
|
method = request.method
|
664
|
+
|
665
|
+
if request.has_tlv?(TLV_TYPE_REQUEST_ID)
|
666
|
+
id = request.get_tlv_value(TLV_TYPE_REQUEST_ID)
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
packet = Packet.new(response_type, method)
|
671
|
+
|
672
|
+
if id
|
673
|
+
packet.add_tlv(TLV_TYPE_REQUEST_ID, id)
|
593
674
|
end
|
594
675
|
|
595
|
-
|
676
|
+
packet
|
596
677
|
end
|
597
678
|
|
598
679
|
##
|
@@ -609,11 +690,12 @@ module Metasploit
|
|
609
690
|
def initialize(type = nil, method = nil)
|
610
691
|
super(type)
|
611
692
|
|
612
|
-
if
|
693
|
+
if method
|
613
694
|
self.method = method
|
614
695
|
end
|
615
696
|
|
616
697
|
self.created_at = ::Time.now
|
698
|
+
self.raw = ''
|
617
699
|
|
618
700
|
# If it's a request, generate a random request identifier
|
619
701
|
if ((type == PACKET_TYPE_REQUEST) ||
|
@@ -626,20 +708,99 @@ module Metasploit
|
|
626
708
|
end
|
627
709
|
end
|
628
710
|
|
711
|
+
def add_raw(bytes)
|
712
|
+
self.raw << bytes
|
713
|
+
end
|
714
|
+
|
715
|
+
def raw_bytes_required
|
716
|
+
# if we have the xor bytes and length ...
|
717
|
+
if self.raw.length >= PACKET_HEADER_SIZE
|
718
|
+
# return a value based on the length of the data indicated by
|
719
|
+
# the header
|
720
|
+
xor_key = self.raw.unpack('a4')[0]
|
721
|
+
decoded_bytes = xor_bytes(xor_key, raw[0, PACKET_HEADER_SIZE])
|
722
|
+
_, _, _, length, _ = decoded_bytes.unpack('a4a16NNN')
|
723
|
+
length + PACKET_HEADER_SIZE - HEADER_SIZE - self.raw.length
|
724
|
+
else
|
725
|
+
# Otherwise ask for the remaining bytes for the metadata to get the packet length
|
726
|
+
# So we can do the rest of the calculation next time
|
727
|
+
PACKET_HEADER_SIZE - self.raw.length
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
def aes_encrypt(key, data)
|
732
|
+
# Create the required cipher instance
|
733
|
+
aes = OpenSSL::Cipher.new('AES-256-CBC')
|
734
|
+
# Generate a truly random IV
|
735
|
+
iv = aes.random_iv
|
736
|
+
|
737
|
+
# set up the encryption
|
738
|
+
aes.encrypt
|
739
|
+
aes.key = key
|
740
|
+
aes.iv = iv
|
741
|
+
|
742
|
+
# encrypt and return the IV along with the result
|
743
|
+
return iv, aes.update(data) + aes.final
|
744
|
+
end
|
745
|
+
|
746
|
+
def aes_decrypt(key, iv, data)
|
747
|
+
# Create the required cipher instance
|
748
|
+
aes = OpenSSL::Cipher.new('AES-256-CBC')
|
749
|
+
# Generate a truly random IV
|
750
|
+
|
751
|
+
# set up the encryption
|
752
|
+
aes.decrypt
|
753
|
+
aes.key = key
|
754
|
+
aes.iv = iv
|
755
|
+
|
756
|
+
# decrypt!
|
757
|
+
aes.update(data) + aes.final
|
758
|
+
end
|
759
|
+
|
629
760
|
#
|
630
761
|
# Override the function that creates the raw byte stream for
|
631
762
|
# sending so that it generates an XOR key, uses it to scramble
|
632
763
|
# the serialized TLV content, and then returns the key plus the
|
633
764
|
# scrambled data as the payload.
|
634
765
|
#
|
635
|
-
def to_r
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
766
|
+
def to_r(session_guid = nil, key = nil)
|
767
|
+
xor_key = (rand(254) + 1).chr + (rand(254) + 1).chr + (rand(254) + 1).chr + (rand(254) + 1).chr
|
768
|
+
|
769
|
+
raw = (session_guid || NULL_GUID).dup
|
770
|
+
tlv_data = GroupTlv.instance_method(:to_r).bind(self).call
|
771
|
+
|
772
|
+
if key && key[:key] && key[:type] == ENC_FLAG_AES256
|
773
|
+
# encrypt the data, but not include the length and type
|
774
|
+
iv, ciphertext = aes_encrypt(key[:key], tlv_data[HEADER_SIZE..-1])
|
775
|
+
# now manually add the length/type/iv/ciphertext
|
776
|
+
raw << [ENC_FLAG_AES256, iv.length + ciphertext.length + HEADER_SIZE, self.type, iv, ciphertext].pack('NNNA*A*')
|
777
|
+
else
|
778
|
+
raw << [ENC_FLAG_NONE, tlv_data].pack('NA*')
|
779
|
+
end
|
780
|
+
|
781
|
+
# return the xor'd result with the key
|
782
|
+
xor_key + xor_bytes(xor_key, raw)
|
783
|
+
end
|
784
|
+
|
785
|
+
#
|
786
|
+
# Decrypt the packet based on the content of the encryption flags.
|
787
|
+
#
|
788
|
+
def decrypt_packet(key, encrypt_flags, data)
|
789
|
+
# TODO: throw an error if the expected encryption isn't the same as the given
|
790
|
+
# as this could be an indication of hijacking or side-channel packet addition
|
791
|
+
# as highlighted by Justin Steven on github.
|
792
|
+
if key && key[:key] && key[:type] && encrypt_flags == ENC_FLAG_AES256 && encrypt_flags == key[:type]
|
793
|
+
iv = data[0, AES_IV_SIZE]
|
794
|
+
aes_decrypt(key[:key], iv, data[iv.length..-1])
|
795
|
+
else
|
796
|
+
data
|
797
|
+
end
|
798
|
+
end
|
799
|
+
|
800
|
+
def parse_header!
|
801
|
+
xor_key = self.raw.unpack('a4')[0]
|
802
|
+
data = xor_bytes(xor_key, self.raw[0..PACKET_HEADER_SIZE])
|
803
|
+
_, self.session_guid, self.encrypt_flags, self.length, self.type = data.unpack('a4a16NNN')
|
643
804
|
end
|
644
805
|
|
645
806
|
#
|
@@ -648,17 +809,20 @@ module Metasploit
|
|
648
809
|
# passing it on to the default functionality that can parse
|
649
810
|
# the TLV values.
|
650
811
|
#
|
651
|
-
def from_r(
|
652
|
-
|
653
|
-
|
812
|
+
def from_r(key=nil)
|
813
|
+
self.parse_header!
|
814
|
+
xor_key = self.raw.unpack('a4')[0]
|
815
|
+
data = xor_bytes(xor_key, self.raw[PACKET_HEADER_SIZE..-1])
|
816
|
+
raw = decrypt_packet(key, self.encrypt_flags, data)
|
817
|
+
super([self.length, self.type, raw].pack('NNA*'))
|
654
818
|
end
|
655
819
|
|
656
820
|
#
|
657
|
-
# Xor a set of bytes with a given
|
821
|
+
# Xor a set of bytes with a given XOR key.
|
658
822
|
#
|
659
823
|
def xor_bytes(xor_key, bytes)
|
660
824
|
result = ''
|
661
|
-
bytes.bytes.zip(
|
825
|
+
bytes.bytes.zip(xor_key.bytes.cycle).each do |b|
|
662
826
|
result << (b[0].ord ^ b[1].ord).chr
|
663
827
|
end
|
664
828
|
result
|
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.
|
4
|
+
version: 1.0.0
|
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-10-
|
91
|
+
date: 2017-10-11 00:00:00.000000000 Z
|
92
92
|
dependencies:
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
94
|
name: bundler
|
@@ -223,7 +223,6 @@ files:
|
|
223
223
|
- lib/metasploit/aggregator/router.rb
|
224
224
|
- lib/metasploit/aggregator/session_detail_service.rb
|
225
225
|
- lib/metasploit/aggregator/tlv/packet.rb
|
226
|
-
- lib/metasploit/aggregator/tlv/packet_parser.rb
|
227
226
|
- lib/metasploit/aggregator/tlv/uuid.rb
|
228
227
|
- lib/metasploit/aggregator/version.rb
|
229
228
|
- metasploit-aggregator.gemspec
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,103 +0,0 @@
|
|
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
|
-
|