metasploit-aggregator 0.1.2 → 0.1.3

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: df43d9c519841eb237a29d07f02423decce8ae9c
4
- data.tar.gz: 3a9f144ffceefc2caf76a0b99aaacfd8d933cab5
3
+ metadata.gz: f132369edfc38617414a3e370e08b300cfe0960d
4
+ data.tar.gz: e78d5af7c79f7578403a2aff1129b0e9b9ad36c2
5
5
  SHA512:
6
- metadata.gz: 77a8d3d7de9523f6680346211b722b901eaac594ab51124c3f84053dfbf4dda8391524651d0bbbaeb1ab6b7592be3a4cd7b2a1aff227196b04c80932093e7f53
7
- data.tar.gz: da9034ab7c33353c9eb890ff67885f025957f754be923264dbb0a5ad14545a818003c41131ea0a7bc77f81242ca9e0ae12ab279b68664a2a3cf1947c0cec9567
6
+ metadata.gz: a8155484bd84179dfcadd27ed391c56fb77d911f8d727845119e33b5f1d2d3b66ebcf652c4a15075d64c7da41238c99db164ed54ff3ffdd0e6f00c953eda1923
7
+ data.tar.gz: be4b963516ce7645ec42e970f70f2c8203c058e392c73ec891b326e5cfe61bfe53b84384f02856b80112e8af27ca3ca03bd107b593e62191f9b5a7f053b12e39
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -7,35 +7,27 @@ require 'metasploit/aggregator/logger'
7
7
 
8
8
  admin_host = '127.0.0.1'
9
9
  admin_port = 2447
10
- listener = '127.0.0.1'
11
- remote_console = '127.0.0.1'
12
10
  # cert_file = './cert.pem'
13
11
  # cert_string = File.new(cert_file).read
14
12
  cert_string = nil
15
13
 
16
- # server = Metasploit::Aggregator::Server.new('127.0.0.1', 1337)
17
- server = Metasploit::Aggregator::MsgPackServer.new(admin_host, admin_port)
18
- server.start
14
+ server = Metasploit::Aggregator::GrpcServer.new(admin_host, admin_port)
19
15
  Logger.log "Starting administration service on #{admin_host}:#{admin_port}"
20
16
 
21
17
  loop do
22
18
  command = $stdin.gets
23
19
  if command.chomp == 'exit'
24
20
  exit
25
- elsif command.chomp == 'clear'
26
- forwarder.requests = []
27
- forwarder.responses = []
28
21
  elsif command.chomp == 'pause'
29
22
  Logger.log "paused"
30
- elsif command.chomp == 'start'
31
- server.start
32
23
  elsif command.chomp == 'stop'
33
24
  server.stop
34
25
  elsif command.chomp == 'park'
35
26
  client.release_session($stdin.gets.chomp)
36
27
  elsif command.chomp == 'details'
37
28
  client = Metasploit::Aggregator::ServerProxy.new(admin_host, admin_port)
38
- client.sessions.each_pair do |payload, console|
29
+ sessions = client.sessions
30
+ sessions.each_pair do |payload, console|
39
31
  details = client.session_details(payload)
40
32
  $stdout.puts payload
41
33
  details.each_pair do |key, attr|
@@ -1,10 +1,11 @@
1
1
  require 'socket'
2
2
  require 'openssl'
3
3
  require 'thread'
4
- require 'msgpack'
5
- require 'msgpack/rpc'
6
4
  require 'securerandom'
7
5
 
6
+ require 'metasploit/aggregator/error'
7
+ require 'metasploit/aggregator/messages_pb'
8
+ require 'metasploit/aggregator/aggregator_services_pb'
8
9
  require 'metasploit/aggregator/version'
9
10
  require 'metasploit/aggregator/cable'
10
11
  require 'metasploit/aggregator/connection_manager'
@@ -21,6 +22,11 @@ module Metasploit
21
22
  # index for impl
22
23
  end
23
24
 
25
+ # return the current service version found
26
+ def version
27
+ Metasploit::Aggregator::VERSION
28
+ end
29
+
24
30
  # returns map of sessions available from the service
25
31
  def sessions
26
32
  # index for impl
@@ -80,87 +86,91 @@ module Metasploit
80
86
 
81
87
  class ServerProxy < Service
82
88
  attr_reader :uuid
89
+ @exit_lock = Mutex.new
83
90
  @host = @port = @socket = nil
84
- @response_queue = []
91
+ @no_params = nil
92
+ @response_queue = nil
93
+ @listening_thread = nil
94
+ @cleanup_list = nil
85
95
 
86
96
  def initialize(host, port)
87
97
  @host = host
88
98
  @port = port
89
- @client = MessagePack::RPC::Client.new(@host, @port)
99
+ @client = Metasploit::Aggregator::Pb::Stub.new("#{@host}:#{@port}", :this_channel_is_insecure)
100
+ # TODO: add arg{ :channel_override => Core::Channel } to control connection
90
101
  @uuid = SecureRandom.uuid
102
+ @no_params = Metasploit::Aggregator::Message::No_params.new
103
+ # server_version = pb_to_array(@client.version(@no_params).value)[0]
104
+ # raise CompatibilityError("server version mis-match found #{server_version}") unless server_version == version
91
105
  end
92
106
 
93
107
  def available?
94
- @client.call(:available?)
95
- rescue MessagePack::RPC::ConnectionTimeoutError => e
96
- false
108
+ @client.available(@no_params).answer
97
109
  end
98
110
 
99
111
  def sessions
100
- @client.call(:sessions)
101
- rescue MessagePack::RPC::TimeoutError => e
102
- Logger.log(e.to_s)
112
+ pb_to_map(@client.sessions(@no_params).map)
103
113
  end
104
114
 
105
115
  def cables
106
- @client.call(:cables)
107
- rescue MessagePack::RPC::TimeoutError => e
108
- Logger.log(e.to_s)
116
+ pb_to_array(@client.cables(@no_params).value)
109
117
  end
110
118
 
111
119
 
112
120
  def obtain_session(payload, uuid)
113
- @client.call(:obtain_session, payload, uuid)
114
- rescue MessagePack::RPC::TimeoutError => e
115
- Logger.log(e.to_s)
121
+ args = Metasploit::Aggregator::Message::String_array.new( value: [payload, uuid] )
122
+ @client.obtain_session(args).answer
116
123
  end
117
124
 
118
125
  def release_session(payload)
119
- @client.call(:release_session, payload)
120
- rescue MessagePack::RPC::TimeoutError => e
121
- Logger.log(e.to_s)
126
+ args = Metasploit::Aggregator::Message::String_array.new( value: [payload] )
127
+ @client.release_session(args).answer
122
128
  end
123
129
 
124
130
  def session_details(payload)
125
- @client.call(:session_details, payload)
126
- rescue MessagePack::RPC::TimeoutError => e
127
- Logger.log(e.to_s)
131
+ args = Metasploit::Aggregator::Message::String_array.new( value: [payload] )
132
+ pb_to_map(@client.session_details(args).map)
128
133
  end
129
134
 
130
135
  def add_cable(type, host, port, certificate = nil)
131
- @client.call(:add_cable, type, host, port, certificate)
132
- rescue MessagePack::RPC::TimeoutError => e
133
- Logger.log(e.to_s)
136
+ args = nil
137
+ if certificate.nil?
138
+ args = Metasploit::Aggregator::Message::Cable_def.new( type: type, host: host, port: port.to_i )
139
+ else
140
+ args = Metasploit::Aggregator::Message::Cable_def.new( type: type, host: host, port: port.to_i, pem: certificate )
141
+ end
142
+ @client.add_cable(args).answer
134
143
  end
135
144
 
136
145
  def remove_cable(host, port)
137
- @client.call(:remove_cable, host, port)
138
- rescue MessagePack::RPC::TimeoutError => e
139
- Logger.log(e.to_s)
146
+ args = Metasploit::Aggregator::Message::String_array.new( value: [host, port] )
147
+ @client.remove_cable(args).answer
140
148
  end
141
149
 
142
150
  def register_default(uuid, payload_list)
143
- @client.call(:register_default, uuid, payload_list)
144
- rescue MessagePack::RPC::TimeoutError => e
145
- Logger.log(e.to_s)
151
+ uuid = "" if uuid.nil?
152
+ payloads = []
153
+ payloads = payload + payload_list unless payload_list.nil?
154
+ args = Metasploit::Aggregator::Message::Register.new( uuid: uuid, payloads: payloads )
155
+ @client.register_default(args).answer
146
156
  end
147
157
 
148
158
  def default
149
- @client.call(:default)
150
- rescue MessagePack::RPC::TimeoutError => e
151
- Logger.log(e.to_s)
159
+ pb_to_array(@client.default(@no_params).value)[0]
152
160
  end
153
161
 
154
162
  def available_addresses
155
- @client.call(:available_addresses)
156
- rescue MessagePack::RPC::TimeoutError => e
157
- Logger.log(e.to_s)
163
+ pb_to_array(@client.available_addresses(@no_params).value)
158
164
  end
159
165
 
160
- def stop
161
- @client.close
162
- @client = nil
166
+ def stop(force = false)
167
+ # end the response queue
168
+ ServerProxy.unregister_for_cleanup(self) unless force
169
+ @response_queue.push(self) unless @response_queue.nil?
170
+
163
171
  @listening_thread.join if @listening_thread
172
+ @listening_thread = nil
173
+ @client = nil
164
174
  end
165
175
 
166
176
  def register_response_channel(requester)
@@ -168,44 +178,268 @@ module Metasploit
168
178
  raise ArgumentError("response channel class invalid")
169
179
  end
170
180
  @response_io = requester
171
- start_responding
181
+ process
172
182
  end
173
183
 
174
- def start_responding
184
+ protected
185
+
186
+ def self.register_for_cleanup(connection)
187
+ @exit_lock.synchronize do
188
+ unless @cleanup_list
189
+ @cleanup_list = ::Set.new
190
+ at_exit { ServerProxy.run_cleanup }
191
+ end
192
+ @cleanup_list.add connection
193
+ end
194
+ end
195
+
196
+ def self.unregister_for_cleanup(connection)
197
+ @exit_lock.synchronize do
198
+ @cleanup_list.delete connection if @cleanup_list
199
+ end
200
+ end
201
+
202
+ def self.run_cleanup
203
+ @exit_lock.synchronize do
204
+ if @cleanup_list
205
+ @cleanup_list.each do |connection|
206
+ connection.stop(true)
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ private
213
+
214
+ def pb_to_map(map)
215
+ result = {}
216
+ map.each do |key , value|
217
+ result[key] = value
218
+ end
219
+ result
220
+ end
221
+
222
+ def pb_to_array(array)
223
+ result = []
224
+ array.each do |value|
225
+ result << value
226
+ end
227
+ result
228
+ end
229
+
230
+ def process
231
+ @response_queue = EnumeratorQueue.new(self)
232
+ requests = @client.process(@response_queue.each_item)
233
+
234
+ # add initial key response with only local uuid
235
+ initial_response = Metasploit::Aggregator::Message::Response.new( uuid: @uuid )
236
+ @response_queue.push(initial_response)
237
+
175
238
  @listening_thread = Thread.new do
176
- @listener_client = MessagePack::RPC::Client.new(@host, @port) unless @listener_client
177
- while @client
178
- begin
179
- sleep 0.1 # polling for now need
180
- result, result_obj, session_id, response_obj = nil
181
- result = @listener_client.call(:request, @uuid)
182
- next unless result # just continue to poll if no request is found
183
- result_obj = Metasploit::Aggregator::Http::Request.from_msgpack(result)
184
- session_id = Metasploit::Aggregator::Http::Request.parse_uri(result_obj)
185
- response_obj = @response_io.process_request(result_obj)
186
- @listener_client.call(:respond, session_id, response_obj.to_msgpack)
187
- rescue MessagePack::RPC::TimeoutError
188
- next
189
- rescue
190
- Logger.log $!
239
+ requests.each do |pb_request|
240
+ request = Metasploit::Aggregator::Http::Request.new(pb_to_array(pb_request.headers), pb_request.body, nil)
241
+ response = @response_io.process_request(request)
242
+ session_id = Metasploit::Aggregator::Http::Request.parse_uri(request)
243
+ pb_request = Metasploit::Aggregator::Message::Request.new( headers: response.headers, body: response.body )
244
+ pb_response = Metasploit::Aggregator::Message::Response.new( uuid: session_id, response: pb_request)
245
+ @response_queue.push(pb_response)
246
+ end
247
+ end
248
+ ServerProxy.register_for_cleanup self
249
+ end
250
+ end
251
+
252
+ # A EnumeratorQueue wraps a Queue to yield the items added to it.
253
+ class EnumeratorQueue
254
+ extend Forwardable
255
+ def_delegators :@q, :push
256
+
257
+ def initialize(sentinel)
258
+ @q = Queue.new
259
+ @sentinel = sentinel
260
+ end
261
+
262
+ def each_item
263
+ return enum_for(:each_item) unless block_given?
264
+ loop do
265
+ r = @q.pop
266
+ break if r.equal?(@sentinel)
267
+ fail r if r.is_a? Exception
268
+ yield r
269
+ end
270
+ end
271
+ end
272
+
273
+ class ServerImpl < Metasploit::Aggregator::Pb::Service
274
+
275
+ def initialize
276
+ super
277
+ @local_server = Server.new
278
+ @requestThreads = {}
279
+ @listeners = []
280
+ end
281
+
282
+ def available(_no_params, _unused_call)
283
+ Metasploit::Aggregator::Message::Result.new( answer: @local_server.available? )
284
+ end
285
+
286
+ def version(_no_params, _unused_call)
287
+ Metasploit::Aggregator::Message::String_array.new( value: [ @local_server.version ] )
288
+ end
289
+
290
+ def sessions(_no_parms, _unused_call)
291
+ Metasploit::Aggregator::Message::Result_map.new( map: @local_server.sessions() )
292
+ end
293
+
294
+ def cables(_no_parms, _unused_call)
295
+ Metasploit::Aggregator::Message::String_array.new( value: @local_server.cables() )
296
+ end
297
+
298
+ def obtain_session(args, _unused_call)
299
+ payload, uuid = args.value
300
+ Metasploit::Aggregator::Message::Result.new( answer: @local_server.obtain_session(payload, uuid) )
301
+ end
302
+
303
+ def release_session(args, _unused_call)
304
+ payload = args.value.shift
305
+ Metasploit::Aggregator::Message::Result.new( answer: @local_server.release_session(payload) )
306
+ end
307
+
308
+ def session_details(args, _unused_call)
309
+ payload = args.value.shift
310
+ Metasploit::Aggregator::Message::Result_map.new( map: @local_server.session_details(payload) )
311
+ end
312
+
313
+ def add_cable(cable, _unused_call)
314
+ pem = nil
315
+ pem = cable.pem unless cable.pem.empty?
316
+ result = @local_server.add_cable(cable.type, cable.host, cable.port, pem)
317
+ Metasploit::Aggregator::Message::Result.new( answer: result )
318
+ end
319
+
320
+ def remove_cable(args, _unused_call)
321
+ host, port = args.value
322
+ result = @local_server.remove_cable(host, port)
323
+ Metasploit::Aggregator::Message::Result.new( answer: result )
324
+ end
325
+
326
+ def register_default(register, _unused_call)
327
+ payloads = nil
328
+ payloads = register.payloads unless register.payloads.empty?
329
+ result = @local_server.register_default(register.uuid, payloads)
330
+ Metasploit::Aggregator::Message::Result.new( answer: result )
331
+ end
332
+
333
+ def default(_no_params, _unused_call)
334
+ uuid = @local_server.default
335
+ return Metasploit::Aggregator::Message::String_array.new( value: [ uuid ] ) unless uuid.nil?
336
+ Metasploit::Aggregator::Message::String_array.new()
337
+ end
338
+
339
+ def available_addresses(_no_params, _unused_call)
340
+ addresses = @local_server.available_addresses
341
+ Metasploit::Aggregator::Message::String_array.new( value: addresses )
342
+ end
343
+
344
+ def process(responses)
345
+ requests = EnumeratorQueue.new(self)
346
+ uuid = nil
347
+
348
+ requestingThread = Thread.new do
349
+ loop do
350
+ sleep 0.1 # outer loop only occurs until uuid is set
351
+ next if uuid.nil?
352
+ request = @local_server.request(uuid)
353
+ # TODO: with this in place we can just get the request queue and pop each item to process and forward
354
+ unless request.nil?
355
+ body = ""
356
+ body = request.body unless request.body.nil?
357
+ pb_request = Metasploit::Aggregator::Message::Request.new( headers: request.headers, body: body )
358
+ requests.push(pb_request)
191
359
  end
192
360
  end
193
- @listener_client.close
361
+ end
362
+
363
+ Thread.new do
364
+ responses.each do |response|
365
+ uuid = response.uuid if uuid.nil?
366
+ next if response.response.nil?
367
+ request_pb = response.response
368
+ request = Metasploit::Aggregator::Http::Request.new(request_pb.headers, request_pb.body, nil)
369
+ @local_server.respond(response.uuid, request)
370
+ end
371
+ requestingThread.exit
372
+ requestingThread.join
373
+ requests.push(self)
374
+ end
375
+
376
+ requests.each_item
377
+ end
378
+ end
379
+
380
+ class GrpcServer
381
+ @exit_lock = Mutex.new
382
+
383
+ def initialize(host, port)
384
+ @host = host
385
+ @port = port
386
+
387
+ # TODO: investigate using Core::Channel to secure this communication
388
+ # server = TCPServer.new(@host, @port)
389
+ # sslContext = OpenSSL::SSL::SSLContext.new
390
+ # sslContext.key, sslContext.cert = Metasploit::Aggregator::ConnectionManager.ssl_generate_certificate
391
+ # sslServer = OpenSSL::SSL::SSLServer.new(server, sslContext)
392
+
393
+ @svr = GRPC::RpcServer.new
394
+ @svr.add_http2_port("#{@host}:#{@port}", :this_port_is_insecure)
395
+ @svr.handle(ServerImpl)
396
+
397
+ @exec = Thread.new do
398
+ GrpcServer.register_for_cleanup(self)
399
+ @svr.run_till_terminated
400
+ end
401
+ end
402
+
403
+ def stop(force = false)
404
+ GrpcServer.unregister_for_cleanup(self) unless force
405
+ @svr.stop if @svr.running?
406
+ end
407
+
408
+ protected
409
+
410
+ def self.register_for_cleanup(connection)
411
+ @exit_lock.synchronize do
412
+ unless @cleanup_list
413
+ @cleanup_list = ::Set.new
414
+ at_exit { GrpcServer.run_cleanup }
415
+ end
416
+ @cleanup_list.add connection
417
+ end
418
+ end
419
+
420
+ def self.unregister_for_cleanup(connection)
421
+ @exit_lock.synchronize do
422
+ @cleanup_list.delete connection if @cleanup_list
194
423
  end
195
424
  end
196
- end # ServerProxy
425
+
426
+ def self.run_cleanup
427
+ @exit_lock.synchronize do
428
+ if @cleanup_list
429
+ @cleanup_list.each do |connection|
430
+ connection.stop(true)
431
+ end
432
+ end
433
+ end
434
+ end
435
+
436
+ end
197
437
 
198
438
  class Server < Service
199
- # include Metasploit::Aggregator::ConnectionManager
200
439
 
201
440
  def initialize
202
- @manager = nil
203
441
  @router = Router.instance
204
- end
205
-
206
- def start
207
- @manager = Metasploit::Aggregator::ConnectionManager.new
208
- true
442
+ @manager = ConnectionManager.instance
209
443
  end
210
444
 
211
445
  def available?
@@ -266,7 +500,7 @@ module Metasploit
266
500
  end
267
501
 
268
502
  def default
269
- send, recv, console = @router.get_forward('default')
503
+ _send, _recv, console = @router.get_forward('default')
270
504
  console
271
505
  end
272
506
 
@@ -289,8 +523,7 @@ module Metasploit
289
523
 
290
524
  def request(uuid)
291
525
  # return requests here
292
- result = nil
293
- send, recv = @router.reverse_route(uuid)
526
+ send, _recv = @router.reverse_route(uuid)
294
527
  if send.length > 0
295
528
  result = send.pop
296
529
  end
@@ -298,7 +531,7 @@ module Metasploit
298
531
  end
299
532
 
300
533
  def respond(uuid, data)
301
- send, recv = @router.get_forward(uuid)
534
+ _send, recv = @router.get_forward(uuid)
302
535
  recv << data unless recv.nil?
303
536
  true
304
537
  end
@@ -310,72 +543,5 @@ module Metasploit
310
543
  response
311
544
  end
312
545
  end # class Server
313
-
314
- # wrapping class required to avoid MsgPack specific needs to parallel request processing.
315
- class AsyncMsgPackServer < Server
316
-
317
- def initialize
318
- super
319
- end
320
-
321
- # MsgPack specific wrapper for listener due to lack of parallel processing
322
- def request(uuid)
323
- result = super(uuid)
324
- sendMsg = nil
325
- if result
326
- begin
327
- sendMsg = result.to_msgpack
328
- rescue Exception => e
329
- Logger.log e.backtrace
330
- # when an error occurs here we should likely respond with an error of some sort to remove block on response
331
- end
332
- end
333
- sendMsg
334
- end
335
-
336
- # MsgPack specific wrapper for listener due to lack of parallel processing
337
- def respond(uuid, data)
338
- begin
339
- result = super(uuid, Metasploit::Aggregator::Http::Request.from_msgpack(data))
340
- result
341
- rescue Exception => e
342
- Logger.log e.backtrace
343
- end
344
- end
345
- end # AsyncMsgPackServer
346
-
347
- class MsgPackServer
348
-
349
- def initialize(host, port)
350
- @host = host
351
- @port = port
352
-
353
- # server = TCPServer.new(@host, @port)
354
- # sslContext = OpenSSL::SSL::SSLContext.new
355
- # sslContext.key, sslContext.cert = Metasploit::Aggregator::ConnectionManager.ssl_generate_certificate
356
- # sslServer = OpenSSL::SSL::SSLServer.new(server, sslContext)
357
- #
358
- @svr = MessagePack::RPC::Server.new # need to initialize this as ssl server
359
- # @svr.listen(sslServer, Server.new)
360
- @svr.listen(@host, @port, AsyncMsgPackServer.new)
361
-
362
- Thread.new { @svr.run }
363
- end
364
-
365
- def start
366
- c = MessagePack::RPC::Client.new(@host,@port)
367
- c.call(:start)
368
- c.close
369
- rescue MessagePack::RPC::TimeoutError => e
370
- Logger.log(e.to_s)
371
- end
372
-
373
- def stop
374
- c = MessagePack::RPC::Client.new(@host,@port)
375
- c.call(:stop)
376
- c.close
377
- @svr.close
378
- end
379
- end
380
546
  end
381
547
  end