metasploit-aggregator 0.2.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|