Skip to content

Commit 2cd9649

Browse files
committed
Added msf red connection mechanism
1 parent 000f561 commit 2cd9649

File tree

6 files changed

+176
-5
lines changed

6 files changed

+176
-5
lines changed

lib/metasploit/framework/data_service/remote/http/core.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def post_data_async(path, data_hash)
4343
#
4444
def post_data(path, data_hash)
4545
begin
46-
raise 'Data to post to remote service cannot be null or empty' if (data_hash.nil? or data_hash.empty?)
46+
raise 'Data to post to remote service cannot be null or empty' if (data_hash.nil? || data_hash.empty?)
4747

4848
client = @client_pool.pop()
4949
request_opts = build_request_opts(POST_REQUEST, data_hash, path)
@@ -188,7 +188,7 @@ def append_workspace(data_hash)
188188
workspace = data_hash.delete(:wspace)
189189
end
190190

191-
if (workspace and (workspace.is_a?(OpenStruct) or workspace.is_a?(::Mdm::Workspace)))
191+
if (workspace && (workspace.is_a?(OpenStruct) || workspace.is_a?(::Mdm::Workspace)))
192192
data_hash['workspace'] = workspace.name
193193
end
194194

@@ -205,12 +205,12 @@ def build_request_opts(request_type, data_hash, path)
205205
'ctype' => 'application/json',
206206
'uri' => path}
207207

208-
if (not data_hash.nil? and not data_hash.empty?)
208+
if (!data_hash.nil? && !data_hash.empty?)
209209
json_body = append_workspace(data_hash).to_json
210210
request_opts['data'] = json_body
211211
end
212212

213-
if (not @headers.nil? and not @header.empty?)
213+
if (!@headers.nil? && !@headers.empty?)
214214
request_opts['headers'] = @headers
215215
end
216216

lib/metasploit/framework/data_service/remote/http/remote_session_data_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def report_session(opts)
1111
end
1212

1313
#TODO: Fix
14-
opts[:time_stamp] = 1504200469610 #Time.now.utc
14+
opts[:time_stamp] = Time.now.utc
1515
self.post_data_async(SESSION_API_PATH, opts)
1616
end
1717

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module JobHandler
2+
def handle(job_details)
3+
raise 'JobHandler#handle is not implemented';
4+
end
5+
6+
def job_type_handled
7+
raise 'JobHandler#job_type_handled is not implemented';
8+
end
9+
end
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
require 'metasploit/framework/data_service/remote/msf_red/job_handler'
2+
3+
class MessageJobHandler
4+
include JobHandler
5+
6+
JOB_HANDLED = 'message'
7+
8+
def handle(message_hash)
9+
message = "User: #{message_hash['user_id']}, #{message_hash['message']}"
10+
banner = "*" * message.size
11+
puts "\n"
12+
puts "\n"
13+
puts banner
14+
puts message
15+
puts banner
16+
puts "\n"
17+
end
18+
19+
def job_type_handled
20+
JOB_HANDLED
21+
end
22+
23+
end
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
require 'metasploit/framework/data_service'
2+
require 'metasploit/framework/data_service/remote/http/core'
3+
require 'metasploit/framework/data_service/remote/http/remote_service_endpoint'
4+
5+
class MSFRedService
6+
JOB_CHECK_INTERVAL_SEC = 5
7+
LOGIN_TIMEOUT_SEC = 10
8+
SESSION_KEY_VALUE = 'msf-session-key'
9+
LOGIN_ENDPOINT = '/login'
10+
JOBS_ENDPOINT = '/jobs'
11+
CONSOLE_SERVICE_HOST_NAME = 'console-service.metasploit.r7ops.com'
12+
CONSOLE_SERVICE_PORT = 8080
13+
14+
def initialize
15+
@client = Rex::Proto::Http::Client.new(CONSOLE_SERVICE_HOST_NAME, CONSOLE_SERVICE_PORT)
16+
@job_handlers = Hash.new()
17+
load_job_handlers
18+
end
19+
20+
# TODO: Obviously this is not secure
21+
def launch(username, password)
22+
if (do_login(username, password))
23+
inject_data_service
24+
start_job_thread
25+
end
26+
27+
end
28+
29+
#######
30+
private
31+
#######
32+
33+
def load_job_handlers
34+
job_handler_path = File.dirname(__FILE__) + '/job_handlers/*'
35+
Dir.glob(job_handler_path).collect{|file_path|
36+
job_handler_class = File.basename(file_path, '.rb').classify
37+
require file_path
38+
job_handler_class_constant = job_handler_class.constantize
39+
job_handler = job_handler_class_constant.new
40+
@job_handlers[job_handler.job_type_handled] = job_handler
41+
}
42+
end
43+
44+
def inject_data_service
45+
remote_service_endpoint = Metasploit::Framework::DataService::RemoteServiceEndpoint.new(CONSOLE_SERVICE_HOST_NAME, CONSOLE_SERVICE_PORT)
46+
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(remote_service_endpoint)
47+
remote_data_service.set_header(SESSION_KEY_VALUE, @session_key)
48+
data_service_manager = Metasploit::Framework::DataService::DataProxy.instance
49+
data_service_manager.register_data_service(remote_data_service)
50+
end
51+
52+
def do_login(username, password)
53+
login_hash = {:username => username, :password => password}
54+
begin
55+
56+
request_opts = { 'method' => 'POST', 'ctype' => 'application/json', 'uri' => LOGIN_ENDPOINT, 'data' => login_hash.to_json }
57+
request = @client.request_raw(request_opts)
58+
response = @client._send_recv(request, LOGIN_TIMEOUT_SEC)
59+
60+
if response.code == 200
61+
data = JSON.parse(response.body)
62+
@session_key = data['session_key']
63+
puts "MSF Red console login successfull, session: #{@session_key}"
64+
return true
65+
else
66+
puts "Login failed: failed with code: #{response.code} message: #{response.body}"
67+
return false
68+
end
69+
rescue Exception => e
70+
puts "Problem with POST request: #{e.message}"
71+
return false
72+
end
73+
end
74+
75+
76+
def start_job_thread
77+
Thread.start {
78+
loop {
79+
sleep 5
80+
begin
81+
job_hash = get_next_job
82+
if (job_hash.nil? or job_hash.empty?)
83+
next
84+
end
85+
86+
type = job_hash['job_type']
87+
job_handler = @job_handlers[type]
88+
if (job_handler.nil?)
89+
puts "No registered job handler for type: #{type}"
90+
else
91+
job_handler.handle(job_hash['job_details'])
92+
end
93+
rescue Exception => e
94+
puts "Problem executing job: #{e.message}"
95+
end
96+
}
97+
}
98+
end
99+
100+
def get_next_job
101+
request_opts = { 'method' => 'GET', 'ctype' => 'application/json', 'uri' => JOBS_ENDPOINT, 'headers' => {SESSION_KEY_VALUE => @session_key} }
102+
request = @client.request_raw(request_opts)
103+
response = @client._send_recv(request)
104+
105+
if response.code == 200
106+
if (response.body.nil? or response.body.empty?)
107+
return nil
108+
end
109+
110+
begin
111+
return JSON.parse(response.body)
112+
rescue Exception => e
113+
puts "Unable to parse: #{response.body}, reason: #{e.message}"
114+
return nil
115+
end
116+
117+
else
118+
puts "GET request: #{path} with body: #{json_body} failed with code: #{response.code} message: #{response.body}"
119+
return nil
120+
end
121+
end
122+
123+
end

lib/msf/ui/console/command_dispatcher/core.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
require 'msf/ui/console/command_dispatcher/resource'
2121
require 'msf/ui/console/command_dispatcher/modules'
2222
require 'msf/util/document_generator'
23+
require 'metasploit/framework/data_service/remote/msf_red/msf_red_service'
2324

2425
module Msf
2526
module Ui
@@ -106,6 +107,7 @@ def commands
106107
"?" => "Help menu",
107108
"banner" => "Display an awesome metasploit banner",
108109
"cd" => "Change the current working directory",
110+
"msf_red_connect" => "Connect to MSF Platform",
109111
"connect" => "Communicate with a host",
110112
"color" => "Toggle color",
111113
"exit" => "Exit the console",
@@ -266,6 +268,20 @@ def cmd_banner(*args)
266268

267269
end
268270

271+
def cmd_msf_red_connect(*args)
272+
while (arg = args.shift)
273+
case arg
274+
when '-u'
275+
username = args.shift
276+
when '-p'
277+
password = args.shift
278+
end
279+
end
280+
281+
msf_red_service = MSFRedService.new()
282+
msf_red_service.launch(username, password)
283+
end
284+
269285
def cmd_connect_help
270286
print_line "Usage: connect [options] <host> <port>"
271287
print_line

0 commit comments

Comments
 (0)