metasploit-aggregator 0.1.2 → 0.1.3

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