metasploit-aggregator 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|