metasploit-aggregator 0.1.0 → 0.1.1
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/bin/msfaggregator +5 -5
- data/lib/{msf → metasploit}/aggregator.rb +133 -19
- data/lib/{msf → metasploit}/aggregator/cable.rb +1 -1
- data/lib/{msf → metasploit}/aggregator/connection_manager.rb +12 -21
- data/lib/{msf → metasploit}/aggregator/forwarder.rb +6 -7
- data/lib/metasploit/aggregator/http.rb +13 -0
- data/lib/metasploit/aggregator/http/request.rb +53 -0
- data/lib/metasploit/aggregator/http/requester.rb +41 -0
- data/lib/{msf → metasploit}/aggregator/http/responder.rb +21 -39
- data/lib/metasploit/aggregator/http/ssl_requester.rb +27 -0
- data/lib/{msf → metasploit}/aggregator/http/ssl_responder.rb +3 -3
- data/lib/{msf → metasploit}/aggregator/http_forwarder.rb +9 -9
- data/lib/{msf → metasploit}/aggregator/https_forwarder.rb +9 -9
- data/lib/{msf → metasploit}/aggregator/logger.rb +0 -0
- data/lib/metasploit/aggregator/router.rb +68 -0
- data/lib/metasploit/aggregator/version.rb +5 -0
- data/metasploit-aggregator.gemspec +2 -2
- metadata +17 -14
- metadata.gz.sig +3 -2
- data/lib/msf/aggregator/http/request.rb +0 -28
- data/lib/msf/aggregator/router.rb +0 -41
- data/lib/msf/aggregator/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aac104842b24e3ca148b53fec7496fdd91f05cbe
|
4
|
+
data.tar.gz: fe6729af2bc28c2a0a3d2d27e41b55501cff5bb6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7bdd018ad0bdae6925db322e986eced83c1a904f9ba09671b715496271b7d6642a671b9abdcc59626249751579dd94bc966cefac6490a803424f40e21684c822
|
7
|
+
data.tar.gz: 321958012678ee3f900c9f2675bcbe721571d3def92906b136a1cdfea68ec96552a1d7953b2e1f0d7c7985f3f3f45a6cc36c72f7efc7ee2a21471ada2615f8bc
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/bin/msfaggregator
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
require '
|
4
|
+
require 'metasploit/aggregator'
|
5
|
+
require 'metasploit/aggregator/cable'
|
6
|
+
require 'metasploit/aggregator/logger'
|
7
7
|
|
8
8
|
admin_host = '127.0.0.1'
|
9
9
|
admin_port = 2447
|
@@ -13,8 +13,8 @@ remote_console = '127.0.0.1'
|
|
13
13
|
# cert_string = File.new(cert_file).read
|
14
14
|
cert_string = nil
|
15
15
|
|
16
|
-
# server =
|
17
|
-
server =
|
16
|
+
# server = Metasploit::Aggregator::Server.new('127.0.0.1', 1337)
|
17
|
+
server = Metasploit::Aggregator::MsgPackServer.new(admin_host, admin_port)
|
18
18
|
server.start
|
19
19
|
Logger.log "Starting administration service on #{admin_host}:#{admin_port}"
|
20
20
|
|
@@ -3,14 +3,16 @@ require 'openssl'
|
|
3
3
|
require 'thread'
|
4
4
|
require 'msgpack'
|
5
5
|
require 'msgpack/rpc'
|
6
|
+
require 'securerandom'
|
6
7
|
|
7
|
-
require '
|
8
|
-
require '
|
9
|
-
require '
|
10
|
-
require '
|
11
|
-
require '
|
8
|
+
require 'metasploit/aggregator/version'
|
9
|
+
require 'metasploit/aggregator/cable'
|
10
|
+
require 'metasploit/aggregator/connection_manager'
|
11
|
+
require 'metasploit/aggregator/https_forwarder'
|
12
|
+
require 'metasploit/aggregator/http'
|
13
|
+
require 'metasploit/aggregator/logger'
|
12
14
|
|
13
|
-
module
|
15
|
+
module Metasploit
|
14
16
|
module Aggregator
|
15
17
|
|
16
18
|
class Service
|
@@ -31,7 +33,7 @@ module Msf
|
|
31
33
|
# sets forwarding for a specific session to promote
|
32
34
|
# that session for local use, obtained sessions are
|
33
35
|
# not reported in getSessions
|
34
|
-
def obtain_session(payload,
|
36
|
+
def obtain_session(payload, uuid)
|
35
37
|
# index for impl
|
36
38
|
end
|
37
39
|
|
@@ -51,7 +53,11 @@ module Msf
|
|
51
53
|
# index for impl
|
52
54
|
end
|
53
55
|
|
54
|
-
def register_default(
|
56
|
+
def register_default(uuid, payload_list)
|
57
|
+
# index for impl
|
58
|
+
end
|
59
|
+
|
60
|
+
def default
|
55
61
|
# index for impl
|
56
62
|
end
|
57
63
|
|
@@ -60,9 +66,15 @@ module Msf
|
|
60
66
|
def available_addresses
|
61
67
|
# index for impl
|
62
68
|
end
|
69
|
+
|
70
|
+
# register the object to pass request from cables to
|
71
|
+
def register_response_channel(requester)
|
72
|
+
|
73
|
+
end
|
63
74
|
end
|
64
75
|
|
65
76
|
class ServerProxy < Service
|
77
|
+
attr_reader :uuid
|
66
78
|
@host = @port = @socket = nil
|
67
79
|
@response_queue = []
|
68
80
|
|
@@ -70,6 +82,7 @@ module Msf
|
|
70
82
|
@host = host
|
71
83
|
@port = port
|
72
84
|
@client = MessagePack::RPC::Client.new(@host, @port)
|
85
|
+
@uuid = SecureRandom.uuid
|
73
86
|
end
|
74
87
|
|
75
88
|
def available?
|
@@ -91,8 +104,8 @@ module Msf
|
|
91
104
|
end
|
92
105
|
|
93
106
|
|
94
|
-
def obtain_session(payload,
|
95
|
-
@client.call(:obtain_session, payload,
|
107
|
+
def obtain_session(payload, uuid)
|
108
|
+
@client.call(:obtain_session, payload, uuid)
|
96
109
|
rescue MessagePack::RPC::TimeoutError => e
|
97
110
|
Logger.log(e.to_s)
|
98
111
|
end
|
@@ -115,8 +128,14 @@ module Msf
|
|
115
128
|
Logger.log(e.to_s)
|
116
129
|
end
|
117
130
|
|
118
|
-
def register_default(
|
119
|
-
@client.call(:register_default,
|
131
|
+
def register_default(uuid, payload_list)
|
132
|
+
@client.call(:register_default, uuid, payload_list)
|
133
|
+
rescue MessagePack::RPC::TimeoutError => e
|
134
|
+
Logger.log(e.to_s)
|
135
|
+
end
|
136
|
+
|
137
|
+
def default
|
138
|
+
@client.call(:default)
|
120
139
|
rescue MessagePack::RPC::TimeoutError => e
|
121
140
|
Logger.log(e.to_s)
|
122
141
|
end
|
@@ -129,6 +148,39 @@ module Msf
|
|
129
148
|
|
130
149
|
def stop
|
131
150
|
@client.close
|
151
|
+
@client = nil
|
152
|
+
@listening_thread.join if @listening_thread
|
153
|
+
end
|
154
|
+
|
155
|
+
def register_response_channel(requester)
|
156
|
+
unless requester.kind_of? Metasploit::Aggregator::Http::Requester
|
157
|
+
raise ArgumentError("response channel class invalid")
|
158
|
+
end
|
159
|
+
@response_io = requester
|
160
|
+
start_responding
|
161
|
+
end
|
162
|
+
|
163
|
+
def start_responding
|
164
|
+
@listening_thread = Thread.new do
|
165
|
+
@listener_client = MessagePack::RPC::Client.new(@host, @port) unless @listener_client
|
166
|
+
while @client
|
167
|
+
begin
|
168
|
+
sleep 0.1 # polling for now need
|
169
|
+
result, result_obj, session_id, response_obj = nil
|
170
|
+
result = @listener_client.call(:request, @uuid)
|
171
|
+
next unless result # just continue to poll if no request is found
|
172
|
+
result_obj = Metasploit::Aggregator::Http::Request.from_msgpack(result)
|
173
|
+
session_id = Metasploit::Aggregator::Http::Request.parse_uri(result_obj)
|
174
|
+
response_obj = @response_io.process_request(result_obj)
|
175
|
+
@listener_client.call(:respond, session_id, response_obj.to_msgpack)
|
176
|
+
rescue MessagePack::RPC::TimeoutError
|
177
|
+
next
|
178
|
+
rescue
|
179
|
+
Logger.log $!
|
180
|
+
end
|
181
|
+
end
|
182
|
+
@listener_client.close
|
183
|
+
end
|
132
184
|
end
|
133
185
|
end # ServerProxy
|
134
186
|
|
@@ -137,10 +189,11 @@ module Msf
|
|
137
189
|
|
138
190
|
def initialize
|
139
191
|
@manager = nil
|
192
|
+
@router = Router.instance
|
140
193
|
end
|
141
194
|
|
142
195
|
def start
|
143
|
-
@manager =
|
196
|
+
@manager = Metasploit::Aggregator::ConnectionManager.new
|
144
197
|
true
|
145
198
|
end
|
146
199
|
|
@@ -156,11 +209,11 @@ module Msf
|
|
156
209
|
@manager.cables
|
157
210
|
end
|
158
211
|
|
159
|
-
def obtain_session(payload,
|
212
|
+
def obtain_session(payload, uuid)
|
160
213
|
# return session object details or UUID/uri
|
161
214
|
# forwarding will cause new session creation on the console
|
162
215
|
# TODO: check and set lock on payload requested see note below in register_default
|
163
|
-
@manager.register_forward(
|
216
|
+
@manager.register_forward(uuid, [ payload ])
|
164
217
|
true # update later to return if lock obtained
|
165
218
|
end
|
166
219
|
|
@@ -190,13 +243,18 @@ module Msf
|
|
190
243
|
end
|
191
244
|
end
|
192
245
|
|
193
|
-
def register_default(
|
246
|
+
def register_default(uuid, payload_list)
|
194
247
|
# add this payload list to each forwarder for this remote console
|
195
248
|
# TODO: consider adding boolean param to ConnectionManager.register_forward to 'lock'
|
196
|
-
@manager.register_forward(
|
249
|
+
@manager.register_forward(uuid, payload_list)
|
197
250
|
true
|
198
251
|
end
|
199
252
|
|
253
|
+
def default
|
254
|
+
send, recv, console = @router.get_forward('default')
|
255
|
+
console
|
256
|
+
end
|
257
|
+
|
200
258
|
def available_addresses
|
201
259
|
addr_list = Socket.ip_address_list
|
202
260
|
addresses = []
|
@@ -217,8 +275,64 @@ module Msf
|
|
217
275
|
def release_session(host)
|
218
276
|
@manager.park(host)
|
219
277
|
end
|
278
|
+
|
279
|
+
def request(uuid)
|
280
|
+
# return requests here
|
281
|
+
result = nil
|
282
|
+
send, recv = @router.reverse_route(uuid)
|
283
|
+
if send.length > 0
|
284
|
+
result = send.pop
|
285
|
+
end
|
286
|
+
result
|
287
|
+
end
|
288
|
+
|
289
|
+
def respond(uuid, data)
|
290
|
+
send, recv = @router.get_forward(uuid)
|
291
|
+
recv << data unless recv.nil?
|
292
|
+
true
|
293
|
+
end
|
294
|
+
|
295
|
+
def register_response_channel(io)
|
296
|
+
# not implemented "client only method"
|
297
|
+
response = "register_response_channel not implemented on server"
|
298
|
+
Logger.log response
|
299
|
+
response
|
300
|
+
end
|
220
301
|
end # class Server
|
221
302
|
|
303
|
+
# wrapping class required to avoid MsgPack specific needs to parallel request processing.
|
304
|
+
class AsyncMsgPackServer < Server
|
305
|
+
|
306
|
+
def initialize
|
307
|
+
super
|
308
|
+
end
|
309
|
+
|
310
|
+
# MsgPack specific wrapper for listener due to lack of parallel processing
|
311
|
+
def request(uuid)
|
312
|
+
result = super(uuid)
|
313
|
+
sendMsg = nil
|
314
|
+
if result
|
315
|
+
begin
|
316
|
+
sendMsg = result.to_msgpack
|
317
|
+
rescue Exception => e
|
318
|
+
Logger.log e.backtrace
|
319
|
+
# when an error occurs here we should likely respond with an error of some sort to remove block on response
|
320
|
+
end
|
321
|
+
end
|
322
|
+
sendMsg
|
323
|
+
end
|
324
|
+
|
325
|
+
# MsgPack specific wrapper for listener due to lack of parallel processing
|
326
|
+
def respond(uuid, data)
|
327
|
+
begin
|
328
|
+
result = super(uuid, Metasploit::Aggregator::Http::Request.from_msgpack(data))
|
329
|
+
result
|
330
|
+
rescue Exception => e
|
331
|
+
Logger.log e.backtrace
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end # AsyncMsgPackServer
|
335
|
+
|
222
336
|
class MsgPackServer
|
223
337
|
|
224
338
|
def initialize(host, port)
|
@@ -227,12 +341,12 @@ module Msf
|
|
227
341
|
|
228
342
|
# server = TCPServer.new(@host, @port)
|
229
343
|
# sslContext = OpenSSL::SSL::SSLContext.new
|
230
|
-
# sslContext.key, sslContext.cert =
|
344
|
+
# sslContext.key, sslContext.cert = Metasploit::Aggregator::ConnectionManager.ssl_generate_certificate
|
231
345
|
# sslServer = OpenSSL::SSL::SSLServer.new(server, sslContext)
|
232
346
|
#
|
233
347
|
@svr = MessagePack::RPC::Server.new # need to initialize this as ssl server
|
234
348
|
# @svr.listen(sslServer, Server.new)
|
235
|
-
@svr.listen(@host, @port,
|
349
|
+
@svr.listen(@host, @port, AsyncMsgPackServer.new)
|
236
350
|
|
237
351
|
Thread.new { @svr.run }
|
238
352
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'socket'
|
3
3
|
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
require '
|
7
|
-
require '
|
4
|
+
require 'metasploit/aggregator/logger'
|
5
|
+
require 'metasploit/aggregator/http_forwarder'
|
6
|
+
require 'metasploit/aggregator/https_forwarder'
|
7
|
+
require 'metasploit/aggregator/cable'
|
8
8
|
|
9
|
-
module
|
9
|
+
module Metasploit
|
10
10
|
module Aggregator
|
11
11
|
|
12
12
|
class ConnectionManager
|
@@ -14,7 +14,6 @@ module Msf
|
|
14
14
|
def initialize
|
15
15
|
@cables = []
|
16
16
|
@manager_mutex = Mutex.new
|
17
|
-
@default_route = []
|
18
17
|
@router = Router.instance
|
19
18
|
end
|
20
19
|
|
@@ -61,14 +60,14 @@ module Msf
|
|
61
60
|
|
62
61
|
def add_cable_https(host, port, certificate)
|
63
62
|
@manager_mutex.synchronize do
|
64
|
-
forwarder =
|
63
|
+
forwarder = Metasploit::Aggregator::HttpsForwarder.new
|
65
64
|
forwarder.log_messages = true
|
66
65
|
server = TCPServer.new(host, port)
|
67
66
|
ssl_context = OpenSSL::SSL::SSLContext.new
|
68
67
|
unless certificate.nil?
|
69
68
|
ssl_context.key, ssl_context.cert = ssl_parse_certificate(certificate)
|
70
69
|
else
|
71
|
-
ssl_context.key, ssl_context.cert =
|
70
|
+
ssl_context.key, ssl_context.cert = Metasploit::Aggregator::ConnectionManager.ssl_generate_certificate
|
72
71
|
end
|
73
72
|
ssl_server = OpenSSL::SSL::SSLServer.new(server, ssl_context)
|
74
73
|
|
@@ -80,7 +79,7 @@ module Msf
|
|
80
79
|
|
81
80
|
def add_cable_http(host, port)
|
82
81
|
@manager_mutex.synchronize do
|
83
|
-
forwarder =
|
82
|
+
forwarder = Metasploit::Aggregator::HttpForwarder.new
|
84
83
|
forwarder.log_messages = true
|
85
84
|
server = TCPServer.new(host, port)
|
86
85
|
|
@@ -89,20 +88,12 @@ module Msf
|
|
89
88
|
end
|
90
89
|
end
|
91
90
|
|
92
|
-
def register_forward(
|
93
|
-
@cables.each do |cable|
|
94
|
-
addr = cable.server.local_address
|
95
|
-
if addr.ip_address == rhost && addr.ip_port == rport.to_i
|
96
|
-
raise ArgumentError.new("#{rhost}:#{rport} is not a valid forward")
|
97
|
-
end
|
98
|
-
end
|
91
|
+
def register_forward(uuid, payload_list = nil)
|
99
92
|
if payload_list.nil?
|
100
|
-
|
101
|
-
@default_route = [rhost, rport]
|
102
|
-
@router.add_route(rhost, rport, nil)
|
93
|
+
@router.add_route(uuid, nil)
|
103
94
|
else
|
104
95
|
payload_list.each do |payload|
|
105
|
-
@router.add_route(
|
96
|
+
@router.add_route(uuid, payload)
|
106
97
|
end
|
107
98
|
end
|
108
99
|
end
|
@@ -174,7 +165,7 @@ module Msf
|
|
174
165
|
end
|
175
166
|
|
176
167
|
def park(payload)
|
177
|
-
@router.add_route(nil,
|
168
|
+
@router.add_route(nil, payload)
|
178
169
|
Logger.log "parking #{payload}"
|
179
170
|
end
|
180
171
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module Metasploit
|
2
2
|
module Aggregator
|
3
3
|
class Forwarder
|
4
4
|
CONNECTION_TIMEOUT = 60 # one minute
|
@@ -9,7 +9,6 @@ module Msf
|
|
9
9
|
def initialize
|
10
10
|
@log_messages = false
|
11
11
|
@response_queues = {}
|
12
|
-
@responding_threads = {}
|
13
12
|
@forwarder_mutex = Mutex.new
|
14
13
|
@router = Router.instance
|
15
14
|
end
|
@@ -25,9 +24,9 @@ module Msf
|
|
25
24
|
connections = {}
|
26
25
|
@response_queues.each_pair do |connection, queue|
|
27
26
|
forward = 'parked'
|
28
|
-
|
29
|
-
unless
|
30
|
-
forward = "#{
|
27
|
+
send, recv, console = @router.get_forward(connection)
|
28
|
+
unless console.nil?
|
29
|
+
forward = "#{console}"
|
31
30
|
end
|
32
31
|
connections[connection] = forward
|
33
32
|
end
|
@@ -43,8 +42,8 @@ module Msf
|
|
43
42
|
end
|
44
43
|
end
|
45
44
|
stale_sessions.each do |uri|
|
46
|
-
@response_queues
|
47
|
-
|
45
|
+
stale_queue = @response_queues.delete(uri)
|
46
|
+
stale_queue.stop_processing unless stale_queue.nil?
|
48
47
|
end
|
49
48
|
end
|
50
49
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'metasploit/aggregator/http/request'
|
2
|
+
require 'metasploit/aggregator/http/requester'
|
3
|
+
require 'metasploit/aggregator/http/responder'
|
4
|
+
require 'metasploit/aggregator/http/ssl_requester'
|
5
|
+
require 'metasploit/aggregator/http/ssl_responder'
|
6
|
+
|
7
|
+
module Metasploit
|
8
|
+
module Aggregator
|
9
|
+
module Http
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Metasploit
|
2
|
+
module Aggregator
|
3
|
+
module Http
|
4
|
+
class Request
|
5
|
+
attr_reader :headers
|
6
|
+
attr_reader :body
|
7
|
+
attr_reader :socket
|
8
|
+
|
9
|
+
def initialize(request_headers, request_body, socket)
|
10
|
+
@headers = request_headers
|
11
|
+
@body = request_body
|
12
|
+
@socket = socket
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.parse_uri(http_request)
|
16
|
+
req = http_request.headers[0]
|
17
|
+
parts = req.split(/ /)
|
18
|
+
uri = nil
|
19
|
+
if parts.length >= 2
|
20
|
+
uri = req.split(/ /)[1]
|
21
|
+
uri = uri.chomp('/')
|
22
|
+
end
|
23
|
+
uri
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_msgpack
|
27
|
+
MessagePack.dump ({
|
28
|
+
:headers => headers,
|
29
|
+
:body => body
|
30
|
+
})
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.from_msgpack(string)
|
34
|
+
data = MessagePack.load string
|
35
|
+
self.new(data['headers'], data['body'].force_encoding(Encoding::ASCII_8BIT), nil)
|
36
|
+
end
|
37
|
+
|
38
|
+
# provide a default response in Request form
|
39
|
+
def self.parked()
|
40
|
+
parked_message = []
|
41
|
+
parked_message << 'HTTP/1.1 200 OK'
|
42
|
+
parked_message << 'Content-Type: application/octet-stream'
|
43
|
+
parked_message << 'Connection: close'
|
44
|
+
parked_message << 'Server: Apache'
|
45
|
+
parked_message << 'Content-Length: 0'
|
46
|
+
parked_message << ' '
|
47
|
+
parked_message << ' '
|
48
|
+
self.new(parked_message, '', nil)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "metasploit/aggregator/http/request"
|
2
|
+
require "metasploit/aggregator/http/responder"
|
3
|
+
|
4
|
+
module Metasploit
|
5
|
+
module Aggregator
|
6
|
+
module Http
|
7
|
+
# a Requester takes in Request object and to send to a known port and protocol
|
8
|
+
# and receives a response that it also returns as a Request object
|
9
|
+
class Requester
|
10
|
+
def initialize(host, port)
|
11
|
+
@host = host
|
12
|
+
@port = port
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_request(request)
|
16
|
+
socket = get_connection(@host, @port)
|
17
|
+
write_request(socket, request)
|
18
|
+
response_obj = Metasploit::Aggregator::Http::Responder.get_data(socket, true)
|
19
|
+
close_connection(socket)
|
20
|
+
response_obj
|
21
|
+
end
|
22
|
+
|
23
|
+
def write_request(connection, request)
|
24
|
+
request.headers.each do |header|
|
25
|
+
connection.write(header)
|
26
|
+
end
|
27
|
+
connection.write(request.body) unless request.body.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_connection(host, port)
|
31
|
+
TCPSocket.new host, port
|
32
|
+
end
|
33
|
+
|
34
|
+
def close_connection(connection)
|
35
|
+
connection.close
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,8 +1,11 @@
|
|
1
|
-
require "
|
1
|
+
require "metasploit/aggregator/http/request"
|
2
2
|
|
3
|
-
module
|
3
|
+
module Metasploit
|
4
4
|
module Aggregator
|
5
5
|
module Http
|
6
|
+
# a Responder acts a a gateway to convert data from a port to into a Request object
|
7
|
+
# used in the aggregator. It also reverses this process as a gateway for sending Request object
|
8
|
+
# back as responses to the original Request.
|
6
9
|
class Responder
|
7
10
|
|
8
11
|
attr_accessor :queue
|
@@ -16,6 +19,7 @@ module Msf
|
|
16
19
|
@thread = Thread.new { process_requests }
|
17
20
|
@time = Time.now
|
18
21
|
@router = Router.instance
|
22
|
+
@pending_requests = nil
|
19
23
|
end
|
20
24
|
|
21
25
|
def process_requests
|
@@ -26,53 +30,35 @@ module Msf
|
|
26
30
|
connection = request_task.socket
|
27
31
|
request_task.headers
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
host, port = @router.get_forward(@uri)
|
32
|
-
if host.nil?
|
33
|
+
send, recv = @router.get_forward(@uri)
|
34
|
+
if send.nil?
|
33
35
|
# when no forward found park the connection for now
|
34
36
|
# in the future this may get smarter and return a 404 or something
|
35
37
|
send_parked_response(connection)
|
36
38
|
next
|
37
39
|
end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
client = get_connection(host, port)
|
43
|
-
rescue StandardError => e
|
44
|
-
log 'error on console connect ' + e.to_s
|
45
|
-
send_parked_response(connection)
|
46
|
-
next
|
47
|
-
end
|
48
|
-
|
49
|
-
log 'connected to console'
|
41
|
+
# response from get_forward will be a queue to push messages onto and a response queue to retrieve result from
|
42
|
+
send << request_task
|
43
|
+
@pending_request = connection
|
50
44
|
|
51
|
-
|
52
|
-
client.write line
|
53
|
-
end
|
54
|
-
unless request_task.body.nil?
|
55
|
-
client.write request_task.body
|
56
|
-
end
|
57
|
-
client.flush
|
58
|
-
# log "From victim: \n" + request_lines.join()
|
45
|
+
log 'queued to console'
|
59
46
|
|
47
|
+
# now get the response once available and send back using this connection
|
60
48
|
begin
|
61
|
-
|
62
|
-
|
49
|
+
request_obj = recv.pop
|
50
|
+
@pending_request = nil
|
63
51
|
request_obj.headers.each do |line|
|
64
52
|
connection.write line
|
65
|
-
response += line
|
66
53
|
end
|
67
54
|
unless request_obj.body.nil?
|
68
55
|
connection.write request_obj.body
|
69
56
|
end
|
70
57
|
connection.flush
|
71
|
-
|
58
|
+
log 'message delivered from console'
|
72
59
|
rescue
|
73
60
|
log $!
|
74
61
|
end
|
75
|
-
close_connection(client)
|
76
62
|
close_connection(connection)
|
77
63
|
rescue Exception => e
|
78
64
|
log "an error occurred processing request from #{@uri}"
|
@@ -83,20 +69,16 @@ module Msf
|
|
83
69
|
|
84
70
|
def stop_processing
|
85
71
|
@thread.exit
|
72
|
+
if @pending_request
|
73
|
+
send_parked_response(@pending_request)
|
74
|
+
close_connection(@pending_request)
|
75
|
+
end
|
86
76
|
end
|
87
77
|
|
88
78
|
def send_parked_response(connection)
|
89
79
|
address = connection.peeraddr[3]
|
90
80
|
log "sending parked response to #{address}"
|
91
|
-
|
92
|
-
parked_message << 'HTTP/1.1 200 OK'
|
93
|
-
parked_message << 'Content-Type: application/octet-stream'
|
94
|
-
parked_message << 'Connection: close'
|
95
|
-
parked_message << 'Server: Apache'
|
96
|
-
parked_message << 'Content-Length: 0'
|
97
|
-
parked_message << ' '
|
98
|
-
parked_message << ' '
|
99
|
-
parked_message.each do |line|
|
81
|
+
Metasploit::Aggregator::Http::Request.parked.headers.each do |line|
|
100
82
|
connection.puts line
|
101
83
|
end
|
102
84
|
close_connection(connection)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "metasploit/aggregator/http/requester"
|
2
|
+
|
3
|
+
module Metasploit
|
4
|
+
module Aggregator
|
5
|
+
module Http
|
6
|
+
class SslRequester < Requester
|
7
|
+
def initialize(host, port)
|
8
|
+
super(host, port)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_connection(host, port)
|
12
|
+
tcp_client = TCPSocket.new host, port
|
13
|
+
ssl_context = OpenSSL::SSL::SSLContext.new
|
14
|
+
ssl_context.ssl_version = :TLSv1
|
15
|
+
ssl_client = OpenSSL::SSL::SSLSocket.new tcp_client, ssl_context
|
16
|
+
ssl_client.connect
|
17
|
+
ssl_client
|
18
|
+
end
|
19
|
+
|
20
|
+
def close_connection(connection)
|
21
|
+
connection.sync_close = true
|
22
|
+
connection.close
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require "
|
2
|
-
require "
|
1
|
+
require "metasploit/aggregator/http/request"
|
2
|
+
require "metasploit/aggregator/http/responder"
|
3
3
|
|
4
|
-
module
|
4
|
+
module Metasploit
|
5
5
|
module Aggregator
|
6
6
|
module Http
|
7
7
|
class SslResponder < Responder
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'socket'
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
require '
|
2
|
+
require 'metasploit/aggregator/forwarder'
|
3
|
+
require 'metasploit/aggregator/http/request'
|
4
|
+
require 'metasploit/aggregator/http/responder'
|
5
|
+
require 'metasploit/aggregator/logger'
|
6
|
+
require 'metasploit/aggregator/router'
|
7
7
|
|
8
|
-
module
|
8
|
+
module Metasploit
|
9
9
|
module Aggregator
|
10
10
|
class HttpForwarder < Forwarder
|
11
11
|
CONNECTION_TIMEOUT = 60 # one minute
|
@@ -19,12 +19,12 @@ module Msf
|
|
19
19
|
|
20
20
|
def forward(connection)
|
21
21
|
#forward input requests
|
22
|
-
request_obj =
|
23
|
-
uri =
|
22
|
+
request_obj = Metasploit::Aggregator::Http::Responder.get_data(connection, false)
|
23
|
+
uri = Metasploit::Aggregator::Http::Request.parse_uri(request_obj)
|
24
24
|
@forwarder_mutex.synchronize do
|
25
25
|
unless uri.nil?
|
26
26
|
unless @response_queues[uri]
|
27
|
-
uri_responder =
|
27
|
+
uri_responder = Metasploit::Aggregator::Http::Responder.new(uri)
|
28
28
|
uri_responder.log_messages = @log_messages
|
29
29
|
@response_queues[uri] = uri_responder
|
30
30
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'openssl'
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
require '
|
7
|
-
require '
|
3
|
+
require 'metasploit/aggregator/forwarder'
|
4
|
+
require 'metasploit/aggregator/http/request'
|
5
|
+
require 'metasploit/aggregator/http/ssl_responder'
|
6
|
+
require 'metasploit/aggregator/logger'
|
7
|
+
require 'metasploit/aggregator/router'
|
8
8
|
|
9
|
-
module
|
9
|
+
module Metasploit
|
10
10
|
module Aggregator
|
11
11
|
|
12
12
|
class HttpsForwarder < Forwarder
|
@@ -17,12 +17,12 @@ module Msf
|
|
17
17
|
|
18
18
|
def forward(connection)
|
19
19
|
#forward input requests
|
20
|
-
request_obj =
|
21
|
-
uri =
|
20
|
+
request_obj = Metasploit::Aggregator::Http::SslResponder.get_data(connection, false)
|
21
|
+
uri = Metasploit::Aggregator::Http::Request.parse_uri(request_obj)
|
22
22
|
@forwarder_mutex.synchronize do
|
23
23
|
unless uri.nil?
|
24
24
|
unless @response_queues[uri]
|
25
|
-
uri_responder =
|
25
|
+
uri_responder = Metasploit::Aggregator::Http::SslResponder.new(uri)
|
26
26
|
uri_responder.log_messages = @log_messages
|
27
27
|
@response_queues[uri] = uri_responder
|
28
28
|
end
|
File without changes
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Metasploit
|
4
|
+
module Aggregator
|
5
|
+
class Router
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@mutex = Mutex.new
|
10
|
+
@forward_routes = {}
|
11
|
+
@queue_by_uuid = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_route(uuid, payload)
|
15
|
+
# for now always replace in future may check if same route to avoid request loss
|
16
|
+
forward = uuid.nil? ? [] : [Queue.new, Queue.new, uuid]
|
17
|
+
@mutex.synchronize do
|
18
|
+
if payload.nil?
|
19
|
+
@forward_routes['default'] = forward
|
20
|
+
return
|
21
|
+
end
|
22
|
+
@forward_routes[payload] = forward
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_route(payload)
|
27
|
+
unless payload.nil?
|
28
|
+
@mutex.synchronize do
|
29
|
+
@forward_routes.delete(payload)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_forward(uri)
|
35
|
+
unless @forward_routes[uri].nil?
|
36
|
+
@forward_routes[uri]
|
37
|
+
else
|
38
|
+
@forward_routes['default']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def reverse_route(uuid)
|
43
|
+
# this method is not ready for prime time yet as will not
|
44
|
+
# support multiple sessions properly, either need unique Queue
|
45
|
+
# for each session which can be identified on reverse or need
|
46
|
+
# pipeline request order to ensure all results are posted in order only
|
47
|
+
# this could create deadlocks while one session waits for a response
|
48
|
+
# if another fails to respond.
|
49
|
+
|
50
|
+
# try returning a uuid queue filled an aggregate of queues for all requests
|
51
|
+
# for a reverse uuid, and refill that on each reverse, this puts logic
|
52
|
+
# here that I do not like but accomplishes the requirement for now
|
53
|
+
unless @queue_by_uuid[uuid]
|
54
|
+
@queue_by_uuid[uuid] = Queue.new
|
55
|
+
end
|
56
|
+
@forward_routes.each_pair do |key, val|
|
57
|
+
request, response, remote_uuid = val
|
58
|
+
next unless remote_uuid == uuid
|
59
|
+
while !request.empty?
|
60
|
+
@queue_by_uuid[uuid] << request.pop
|
61
|
+
end
|
62
|
+
end
|
63
|
+
[] unless @queue_by_uuid[uuid].length > 0
|
64
|
+
[@queue_by_uuid[uuid], nil, uuid]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require '
|
4
|
+
require 'metasploit/aggregator/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "metasploit-aggregator"
|
8
|
-
spec.version =
|
8
|
+
spec.version = Metasploit::Aggregator::VERSION
|
9
9
|
spec.authors = ['Metasploit Hackers']
|
10
10
|
spec.email = ['metasploit-hackers@lists.sourceforge.net']
|
11
11
|
spec.summary = "metasploit-aggregator"
|
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.1.
|
4
|
+
version: 0.1.1
|
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:
|
91
|
+
date: 2017-01-31 00:00:00.000000000 Z
|
92
92
|
dependencies:
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
94
|
name: bundler
|
@@ -178,18 +178,21 @@ files:
|
|
178
178
|
- README.md
|
179
179
|
- Rakefile
|
180
180
|
- bin/msfaggregator
|
181
|
-
- lib/
|
182
|
-
- lib/
|
183
|
-
- lib/
|
184
|
-
- lib/
|
185
|
-
- lib/
|
186
|
-
- lib/
|
187
|
-
- lib/
|
188
|
-
- lib/
|
189
|
-
- lib/
|
190
|
-
- lib/
|
191
|
-
- lib/
|
192
|
-
- lib/
|
181
|
+
- lib/metasploit/aggregator.rb
|
182
|
+
- lib/metasploit/aggregator/cable.rb
|
183
|
+
- lib/metasploit/aggregator/connection_manager.rb
|
184
|
+
- lib/metasploit/aggregator/forwarder.rb
|
185
|
+
- lib/metasploit/aggregator/http.rb
|
186
|
+
- lib/metasploit/aggregator/http/request.rb
|
187
|
+
- lib/metasploit/aggregator/http/requester.rb
|
188
|
+
- lib/metasploit/aggregator/http/responder.rb
|
189
|
+
- lib/metasploit/aggregator/http/ssl_requester.rb
|
190
|
+
- lib/metasploit/aggregator/http/ssl_responder.rb
|
191
|
+
- lib/metasploit/aggregator/http_forwarder.rb
|
192
|
+
- lib/metasploit/aggregator/https_forwarder.rb
|
193
|
+
- lib/metasploit/aggregator/logger.rb
|
194
|
+
- lib/metasploit/aggregator/router.rb
|
195
|
+
- lib/metasploit/aggregator/version.rb
|
193
196
|
- metasploit-aggregator.gemspec
|
194
197
|
homepage: https://www.msf.com
|
195
198
|
licenses:
|
metadata.gz.sig
CHANGED
@@ -1,2 +1,3 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
�����~jB�{��B6��i�_�*++�|�h綏�q+bD~h���@g��F0�g��딮$��m�r꣓��)FEw�8����(�tk���Z����t�&q�b������0��<a18�8���yO��e0"ΒQ��H,"<C�5O@�åmwՓ�ݽ���RG��6�]h綀
|
2
|
+
�D���\����
|
3
|
+
��fUH=>'��(xgj~�zX98ʓMW?�y�(��gͬ��$1Н`m
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Msf
|
2
|
-
module Aggregator
|
3
|
-
module Http
|
4
|
-
class Request
|
5
|
-
attr_reader :headers
|
6
|
-
attr_reader :body
|
7
|
-
attr_reader :socket
|
8
|
-
|
9
|
-
def initialize(request_headers, request_body, socket)
|
10
|
-
@headers = request_headers
|
11
|
-
@body = request_body
|
12
|
-
@socket = socket
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.parse_uri(http_request)
|
16
|
-
req = http_request.headers[0]
|
17
|
-
parts = req.split(/ /)
|
18
|
-
uri = nil
|
19
|
-
if parts.length >= 2
|
20
|
-
uri = req.split(/ /)[1]
|
21
|
-
uri = uri.chomp('/')
|
22
|
-
end
|
23
|
-
uri
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'singleton'
|
2
|
-
|
3
|
-
module Msf
|
4
|
-
module Aggregator
|
5
|
-
class Router
|
6
|
-
include Singleton
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@mutex = Mutex.new
|
10
|
-
@forward_routes = {}
|
11
|
-
end
|
12
|
-
|
13
|
-
def add_route(rhost, rport, payload)
|
14
|
-
forward = [rhost, rport]
|
15
|
-
@mutex.synchronize do
|
16
|
-
if payload.nil?
|
17
|
-
@forward_routes['default'] = forward
|
18
|
-
return
|
19
|
-
end
|
20
|
-
@forward_routes[payload] = forward
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def remove_route(payload)
|
25
|
-
unless payload.nil?
|
26
|
-
@mutex.synchronize do
|
27
|
-
@forward_routes.delete(payload)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def get_forward(uri)
|
33
|
-
unless @forward_routes[uri].nil?
|
34
|
-
@forward_routes[uri]
|
35
|
-
else
|
36
|
-
@forward_routes['default']
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|