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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c24ff901cb886de3f7096625742249f2874803b
4
- data.tar.gz: 5fb0ea51cc770c8cb7c5a6775c7c02f4418f7464
3
+ metadata.gz: 309e4618d12318d87a16f47501dadddd40d1f22e
4
+ data.tar.gz: 4311c39b35821464b197ac1ab674529d13e04559
5
5
  SHA512:
6
- metadata.gz: ebb542338918a8489423ce6055db118c80750ebebb0239e5ed0680ce7b4ffd60f299c9bb6ce11aa9c710ae92be40d2996b7dec6f307f4dff4b9f21a8636a2767
7
- data.tar.gz: c20ba09eb3c27047cfeda64e4aac3c03c7569d43df56f48773e943f0bd321cb5b38f4ec6ed1af8a5cc458b9b62182778bcc234727b712c0f2a95e16107bc2a78
6
+ metadata.gz: 2217561262a7f697e016c314505353f887a221ee3ccfde1f90760589ea5c702f7b80e1eb55aecf1e50feb5ff1e22f013c4489f091200a9a8b73d7534f9d058b6
7
+ data.tar.gz: b40d75c19d279a179c63273acca9300817f36f239223f86cac718ec9434a233d12f6f3c8f08408edbf8da4a3c0022de105a105fd3d680929df13f57aafd70dbe
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
- parked_message = []
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(request_task, @uri)
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.headers.each do |line|
86
- connection.puts line
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
- close_connection(connection)
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 = Thread.new { process_tlv }
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
- @tlv_queue << [ request, payload ]
25
- if @detail_cache[payload] && @detail_cache[payload]['REMOTE_SOCKET'].nil?
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]}"
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
- # process body as tlv
41
- @w.write(request.body)
42
- packet = @parser.recv(@r)
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
- @detail_cache[payload]['MachineID'] = Digest::MD5.hexdigest(packet.get_tlv_value(Metasploit::Aggregator::Tlv::TLV_TYPE_MACHINE_ID).downcase.strip)
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
- TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403
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( @compress )
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
- return [raw.length + 8, self.type].pack("NN") + raw
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 origional, allowing for transparent data compression.
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
- raw_decompressed = Rex::Text.zlib_inflate( raw[8..length-1] )
293
- # update the length to reflect the decompressed data length (+8 for the length and type DWORD's)
294
- length = raw_decompressed.length + 8
295
- # update the raw buffer with the new length, decompressed data and updated type.
296
- raw = [length, self.type].pack("NN") + raw_decompressed
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[8..length-2]
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[8..length-1]
344
+ self.value = raw[HEADER_SIZE..length-1]
320
345
  end
321
346
 
322
- return length;
347
+ length
323
348
  end
324
349
 
325
350
  protected
326
351
 
327
- def htonq( value )
328
- if( [1].pack( 's' ) == [1].pack( 'n' ) )
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( value )
335
- return htonq( value )
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 (type == TLV_TYPE_ANY)
403
- return self.tlvs
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
- return type_tlvs
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 (replace)
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
- return tlv
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 (tlvs != nil)
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 (type_tlvs.length > index)
466
- return type_tlvs[index]
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
- return (tlv != nil) ? tlv.value : nil
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
- return get_tlv(type) != nil
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
- return [raw.length + 8, self.type].pack("NN") + raw
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 = 8
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 (offset < raw.length-1)
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+8].unpack("NN")
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
- return Packet.new(PACKET_TYPE_REQUEST, method)
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
- return Packet.new(response_type, method)
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 (method)
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
- raw = super
637
- xor_key = rand(254) + 1
638
- xor_key |= (rand(254) + 1) << 8
639
- xor_key |= (rand(254) + 1) << 16
640
- xor_key |= (rand(254) + 1) << 24
641
- result = [xor_key].pack('N') + xor_bytes(xor_key, raw)
642
- result
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(bytes)
652
- xor_key = bytes[0,4].unpack('N')[0]
653
- super(xor_bytes(xor_key, bytes[4, bytes.length]))
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 DWORD xor key.
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([xor_key].pack('V').bytes.cycle).each do |b|
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
@@ -1,5 +1,5 @@
1
1
  module Metasploit
2
2
  module Aggregator
3
- VERSION = '0.2.3'
3
+ VERSION = '1.0.0'
4
4
  end
5
5
  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.2.3
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-06 00:00:00.000000000 Z
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
-