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 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
-