metasploit-aggregator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ Metasploit [![Build Status](https://travis-ci.org/rapid7/metasploit-aggregator.svg?branch=master)](https://travis-ci.org/rapid7/metasploit-aggregator) [![Code Climate](https://img.shields.io/codeclimate/github/rapid7/metasploit-aggregator.svg)](https://codeclimate.com/github/rapid7/metasploit-aggregator)
2
+ ==
3
+ The Metasploit Aggregator is released under a BSD-style license. See
4
+ COPYING for more details.
5
+
6
+ Bug tracking and development information can be found at:
7
+ https://github.com/rapid7/metasploit-aggregator
8
+
9
+ New bugs and feature requests should be directed to:
10
+ http://r-7.co/MSF-BUGv1
11
+
12
+ API documentation for writing modules can be found at:
13
+ https://rapid7.github.io/metasploit-aggregator/api
14
+
15
+ Questions and suggestions can be sent to:
16
+ https://lists.sourceforge.net/lists/listinfo/metasploit-hackers
17
+
18
+ Installing
19
+ --
20
+
21
+ Using Metasploit Aggregator
22
+ --
23
+
24
+ Contributing
25
+ --
26
+ [Contributing](https://github.com/rapid7/metasploit-aggregator/blob/master/CONTRIBUTING.md).
27
+
28
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/msfaggregator ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'msf/aggregator'
5
+ require 'msf/aggregator/cable'
6
+ require 'msf/aggregator/logger'
7
+
8
+ admin_host = '127.0.0.1'
9
+ admin_port = 2447
10
+ listener = '127.0.0.1'
11
+ remote_console = '127.0.0.1'
12
+ # cert_file = './cert.pem'
13
+ # cert_string = File.new(cert_file).read
14
+ cert_string = nil
15
+
16
+ # server = Msf::Aggregator::Server.new('127.0.0.1', 1337)
17
+ server = Msf::Aggregator::MsgPackServer.new(admin_host, admin_port)
18
+ server.start
19
+ Logger.log "Starting administration service on #{admin_host}:#{admin_port}"
20
+
21
+ loop do
22
+ command = $stdin.gets
23
+ if command.chomp == 'exit'
24
+ exit
25
+ elsif command.chomp == 'clear'
26
+ forwarder.requests = []
27
+ forwarder.responses = []
28
+ elsif command.chomp == 'pause'
29
+ Logger.log "paused"
30
+ elsif command.chomp == 'start'
31
+ server.start
32
+ elsif command.chomp == 'stop'
33
+ server.stop
34
+ elsif command.chomp == 'park'
35
+ client.release_session($stdin.gets.chomp)
36
+ end
37
+ end
@@ -0,0 +1,256 @@
1
+ require 'socket'
2
+ require 'openssl'
3
+ require 'thread'
4
+ require 'msgpack'
5
+ require 'msgpack/rpc'
6
+
7
+ require 'msf/aggregator/version'
8
+ require 'msf/aggregator/cable'
9
+ require 'msf/aggregator/connection_manager'
10
+ require 'msf/aggregator/https_forwarder'
11
+ require 'msf/aggregator/logger'
12
+
13
+ module Msf
14
+ module Aggregator
15
+
16
+ class Service
17
+ # return availability status of the service
18
+ def available?
19
+ # index for impl
20
+ end
21
+
22
+ # returns map of sessions available from the service
23
+ def sessions
24
+ # index for impl
25
+ end
26
+
27
+ def cables
28
+ # index for impl
29
+ end
30
+
31
+ # sets forwarding for a specific session to promote
32
+ # that session for local use, obtained sessions are
33
+ # not reported in getSessions
34
+ def obtain_session(payload, lhost, lport)
35
+ # index for impl
36
+ end
37
+
38
+ # parks a session and makes it available in the getSessions
39
+ def release_session(payload)
40
+ # index for impl
41
+ end
42
+
43
+ # start a listening port maintained on the service
44
+ # connections are forwarded to any registered default
45
+ # TODO: may want to require a type here for future proof of api
46
+ def add_cable(type, host, port, certificate = nil)
47
+ # index for impl
48
+ end
49
+
50
+ def remove_cable(host, port)
51
+ # index for impl
52
+ end
53
+
54
+ def register_default(lhost, lport, payload_list)
55
+ # index for impl
56
+ end
57
+
58
+ # returns list of IP addressed available to the service
59
+ # TODO: consider also reporting "used" ports (may not be needed)
60
+ def available_addresses
61
+ # index for impl
62
+ end
63
+ end
64
+
65
+ class ServerProxy < Service
66
+ @host = @port = @socket = nil
67
+ @response_queue = []
68
+
69
+ def initialize(host, port)
70
+ @host = host
71
+ @port = port
72
+ @client = MessagePack::RPC::Client.new(@host, @port)
73
+ end
74
+
75
+ def available?
76
+ @client.call(:available?)
77
+ rescue MessagePack::RPC::ConnectionTimeoutError => e
78
+ false
79
+ end
80
+
81
+ def sessions
82
+ @client.call(:sessions)
83
+ rescue MessagePack::RPC::TimeoutError => e
84
+ Logger.log(e.to_s)
85
+ end
86
+
87
+ def cables
88
+ @client.call(:cables)
89
+ rescue MessagePack::RPC::TimeoutError => e
90
+ Logger.log(e.to_s)
91
+ end
92
+
93
+
94
+ def obtain_session(payload, lhost, lport)
95
+ @client.call(:obtain_session, payload, lhost, lport)
96
+ rescue MessagePack::RPC::TimeoutError => e
97
+ Logger.log(e.to_s)
98
+ end
99
+
100
+ def release_session(payload)
101
+ @client.call(:release_session, payload)
102
+ rescue MessagePack::RPC::TimeoutError => e
103
+ Logger.log(e.to_s)
104
+ end
105
+
106
+ def add_cable(type, host, port, certificate = nil)
107
+ @client.call(:add_cable, type, host, port, certificate)
108
+ rescue MessagePack::RPC::TimeoutError => e
109
+ Logger.log(e.to_s)
110
+ end
111
+
112
+ def remove_cable(host, port)
113
+ @client.call(:remove_cable, host, port)
114
+ rescue MessagePack::RPC::TimeoutError => e
115
+ Logger.log(e.to_s)
116
+ end
117
+
118
+ def register_default(lhost, lport, payload_list)
119
+ @client.call(:register_default, lhost, lport, payload_list)
120
+ rescue MessagePack::RPC::TimeoutError => e
121
+ Logger.log(e.to_s)
122
+ end
123
+
124
+ def available_addresses
125
+ @client.call(:available_addresses)
126
+ rescue MessagePack::RPC::TimeoutError => e
127
+ Logger.log(e.to_s)
128
+ end
129
+
130
+ def stop
131
+ @client.close
132
+ end
133
+ end # ServerProxy
134
+
135
+ class Server < Service
136
+ # include Metasploit::Aggregator::ConnectionManager
137
+
138
+ def initialize
139
+ @manager = nil
140
+ end
141
+
142
+ def start
143
+ @manager = Msf::Aggregator::ConnectionManager.new
144
+ true
145
+ end
146
+
147
+ def available?
148
+ !@manager.nil?
149
+ end
150
+
151
+ def sessions
152
+ @manager.connections
153
+ end
154
+
155
+ def cables
156
+ @manager.cables
157
+ end
158
+
159
+ def obtain_session(payload, rhost, rport)
160
+ # return session object details or UUID/uri
161
+ # forwarding will cause new session creation on the console
162
+ # TODO: check and set lock on payload requested see note below in register_default
163
+ @manager.register_forward(rhost, rport, [ payload ])
164
+ true # update later to return if lock obtained
165
+ end
166
+
167
+ def release_session(payload)
168
+ @manager.park(payload)
169
+ true # return always return success for now
170
+ end
171
+
172
+ def add_cable(type, host, port, certificate = nil)
173
+ unless @manager.nil?
174
+ case type
175
+ when Cable::HTTPS
176
+ # TODO: check if already listening on that port
177
+ @manager.add_cable_https(host, port, certificate)
178
+ when Cable::HTTP
179
+ @manager.add_cable_http(host, port)
180
+ else
181
+ Logger.log("#{type} cables are not supported.")
182
+ end
183
+ end
184
+ true
185
+ end
186
+
187
+ def remove_cable(host, port)
188
+ unless @manager.nil?
189
+ @manager.remove_cable(host, port)
190
+ end
191
+ end
192
+
193
+ def register_default(lhost, lport, payload_list)
194
+ # add this payload list to each forwarder for this remote console
195
+ # TODO: consider adding boolean param to ConnectionManager.register_forward to 'lock'
196
+ @manager.register_forward(lhost, lport, payload_list)
197
+ true
198
+ end
199
+
200
+ def available_addresses
201
+ addr_list = Socket.ip_address_list
202
+ addresses = []
203
+ addr_list.each do |addr|
204
+ addresses << addr.ip_address
205
+ end
206
+ addresses
207
+ end
208
+
209
+ def stop
210
+ unless @manager.nil?
211
+ @manager.stop
212
+ end
213
+ @manager = nil
214
+ true
215
+ end
216
+
217
+ def release_session(host)
218
+ @manager.park(host)
219
+ end
220
+ end # class Server
221
+
222
+ class MsgPackServer
223
+
224
+ def initialize(host, port)
225
+ @host = host
226
+ @port = port
227
+
228
+ # server = TCPServer.new(@host, @port)
229
+ # sslContext = OpenSSL::SSL::SSLContext.new
230
+ # sslContext.key, sslContext.cert = Msf::Aggregator::ConnectionManager.ssl_generate_certificate
231
+ # sslServer = OpenSSL::SSL::SSLServer.new(server, sslContext)
232
+ #
233
+ @svr = MessagePack::RPC::Server.new # need to initialize this as ssl server
234
+ # @svr.listen(sslServer, Server.new)
235
+ @svr.listen(@host, @port, Server.new)
236
+
237
+ Thread.new { @svr.run }
238
+ end
239
+
240
+ def start
241
+ c = MessagePack::RPC::Client.new(@host,@port)
242
+ c.call(:start)
243
+ c.close
244
+ rescue MessagePack::RPC::TimeoutError => e
245
+ Logger.log(e.to_s)
246
+ end
247
+
248
+ def stop
249
+ c = MessagePack::RPC::Client.new(@host,@port)
250
+ c.call(:stop)
251
+ c.close
252
+ @svr.close
253
+ end
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,18 @@
1
+ module Msf
2
+ module Aggregator
3
+ class Cable
4
+ HTTPS = 'https'
5
+ HTTP = 'http'
6
+
7
+ attr_reader :forwarder
8
+ attr_reader :server
9
+ attr_reader :thread
10
+
11
+ def initialize(thread, server, forwarder)
12
+ @thread = thread
13
+ @forwarder = forwarder
14
+ @server = server
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,184 @@
1
+ require 'openssl'
2
+ require 'socket'
3
+
4
+ require 'msf/aggregator/logger'
5
+ require 'msf/aggregator/http_forwarder'
6
+ require 'msf/aggregator/https_forwarder'
7
+ require 'msf/aggregator/cable'
8
+
9
+ module Msf
10
+ module Aggregator
11
+
12
+ class ConnectionManager
13
+
14
+ def initialize
15
+ @cables = []
16
+ @manager_mutex = Mutex.new
17
+ @default_route = []
18
+ @router = Router.instance
19
+ end
20
+
21
+ def self.ssl_generate_certificate
22
+ yr = 24*3600*365
23
+ vf = Time.at(Time.now.to_i - rand(yr * 3) - yr)
24
+ vt = Time.at(vf.to_i + (10 * yr))
25
+ cn = 'localhost'
26
+ key = OpenSSL::PKey::RSA.new(2048){ }
27
+ cert = OpenSSL::X509::Certificate.new
28
+ cert.version = 2
29
+ cert.serial = (rand(0xFFFFFFFF) << 32) + rand(0xFFFFFFFF)
30
+ cert.subject = OpenSSL::X509::Name.new([["CN", cn]])
31
+ cert.issuer = OpenSSL::X509::Name.new([["CN", cn]])
32
+ cert.not_before = vf
33
+ cert.not_after = vt
34
+ cert.public_key = key.public_key
35
+
36
+ ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
37
+ cert.extensions = [
38
+ ef.create_extension("basicConstraints","CA:FALSE")
39
+ ]
40
+ ef.issuer_certificate = cert
41
+
42
+ cert.sign(key, OpenSSL::Digest::SHA256.new)
43
+
44
+ [key, cert, nil]
45
+ end
46
+
47
+ def ssl_parse_certificate(certificate)
48
+ key, cert, chain = nil
49
+ unless certificate.nil?
50
+ begin
51
+ # parse the cert
52
+ key = OpenSSL::PKey::RSA.new(certificate, "")
53
+ cert = OpenSSL::X509::Certificate.new(certificate)
54
+ # TODO: ensure this parses all certificate in object provided
55
+ rescue OpenSSL::PKey::RSAError => e
56
+ Logger.log(e.message)
57
+ end
58
+ end
59
+ return key, cert, chain
60
+ end
61
+
62
+ def add_cable_https(host, port, certificate)
63
+ @manager_mutex.synchronize do
64
+ forwarder = Msf::Aggregator::HttpsForwarder.new
65
+ forwarder.log_messages = true
66
+ server = TCPServer.new(host, port)
67
+ ssl_context = OpenSSL::SSL::SSLContext.new
68
+ unless certificate.nil?
69
+ ssl_context.key, ssl_context.cert = ssl_parse_certificate(certificate)
70
+ else
71
+ ssl_context.key, ssl_context.cert = Msf::Aggregator::ConnectionManager.ssl_generate_certificate
72
+ end
73
+ ssl_server = OpenSSL::SSL::SSLServer.new(server, ssl_context)
74
+
75
+ handler = connect_cable(ssl_server, host, port, forwarder)
76
+ @cables << Cable.new(handler, server, forwarder)
77
+ handler
78
+ end
79
+ end
80
+
81
+ def add_cable_http(host, port)
82
+ @manager_mutex.synchronize do
83
+ forwarder = Msf::Aggregator::HttpForwarder.new
84
+ forwarder.log_messages = true
85
+ server = TCPServer.new(host, port)
86
+
87
+ handler = connect_cable(server, host, port, forwarder)
88
+ @cables << Cable.new(handler, server, forwarder)
89
+ end
90
+ end
91
+
92
+ def register_forward(rhost, rport, payload_list = nil)
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
99
+ if payload_list.nil?
100
+ # add the this host and port as the new default route
101
+ @default_route = [rhost, rport]
102
+ @router.add_route(rhost, rport, nil)
103
+ else
104
+ payload_list.each do |payload|
105
+ @router.add_route(rhost, rport, payload)
106
+ end
107
+ end
108
+ end
109
+
110
+ def connections
111
+ connections = {}
112
+ @cables.each do |cable|
113
+ connections = connections.merge cable.forwarder.connections
114
+ end
115
+ connections
116
+ end
117
+
118
+ def cables
119
+ local_cables = []
120
+ @cables.each do |cable|
121
+ addr = cable.server.local_address
122
+ local_cables << addr.ip_address + ':' + addr.ip_port.to_s
123
+ end
124
+ local_cables
125
+ end
126
+
127
+ def connect_cable(server, host, port, forwarder)
128
+ Logger.log "Listening on port #{host}:#{port}"
129
+
130
+ handler = Thread.new do
131
+ begin
132
+ loop do
133
+ Logger.log "waiting for connection on #{host}:#{port}"
134
+ connection = server.accept
135
+ Logger.log "got connection on #{host}:#{port}"
136
+ Thread.new do
137
+ begin
138
+ forwarder.forward(connection)
139
+ rescue
140
+ Logger.log $!
141
+ end
142
+ Logger.log "completed connection on #{host}:#{port}"
143
+ end
144
+ end
145
+ end
146
+ end
147
+ handler
148
+ end
149
+
150
+ def remove_cable(host, port)
151
+ @manager_mutex.synchronize do
152
+ closed_servers = []
153
+ @cables.each do |cable|
154
+ addr = cable.server.local_address
155
+ if addr.ip_address == host && addr.ip_port == port.to_i
156
+ cable.server.close
157
+ cable.thread.exit
158
+ closed_servers << cable
159
+ end
160
+ end
161
+ @cables -= closed_servers
162
+ end
163
+ return true
164
+ end
165
+
166
+
167
+ def stop
168
+ @manager_mutex.synchronize do
169
+ @cables.each do |listener|
170
+ listener.server.close
171
+ listener.thread.exit
172
+ end
173
+ end
174
+ end
175
+
176
+ def park(payload)
177
+ @router.add_route(nil, nil, payload)
178
+ Logger.log "parking #{payload}"
179
+ end
180
+
181
+ private :ssl_parse_certificate
182
+ end
183
+ end
184
+ end