Added msf red connection mechanism
parent
000f561d6f
commit
2cd9649139
|
@ -43,7 +43,7 @@ class RemoteHTTPDataService
|
|||
#
|
||||
def post_data(path, data_hash)
|
||||
begin
|
||||
raise 'Data to post to remote service cannot be null or empty' if (data_hash.nil? or data_hash.empty?)
|
||||
raise 'Data to post to remote service cannot be null or empty' if (data_hash.nil? || data_hash.empty?)
|
||||
|
||||
client = @client_pool.pop()
|
||||
request_opts = build_request_opts(POST_REQUEST, data_hash, path)
|
||||
|
@ -188,7 +188,7 @@ class RemoteHTTPDataService
|
|||
workspace = data_hash.delete(:wspace)
|
||||
end
|
||||
|
||||
if (workspace and (workspace.is_a?(OpenStruct) or workspace.is_a?(::Mdm::Workspace)))
|
||||
if (workspace && (workspace.is_a?(OpenStruct) || workspace.is_a?(::Mdm::Workspace)))
|
||||
data_hash['workspace'] = workspace.name
|
||||
end
|
||||
|
||||
|
@ -205,12 +205,12 @@ class RemoteHTTPDataService
|
|||
'ctype' => 'application/json',
|
||||
'uri' => path}
|
||||
|
||||
if (not data_hash.nil? and not data_hash.empty?)
|
||||
if (!data_hash.nil? && !data_hash.empty?)
|
||||
json_body = append_workspace(data_hash).to_json
|
||||
request_opts['data'] = json_body
|
||||
end
|
||||
|
||||
if (not @headers.nil? and not @header.empty?)
|
||||
if (!@headers.nil? && !@headers.empty?)
|
||||
request_opts['headers'] = @headers
|
||||
end
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ module RemoteSessionDataService
|
|||
end
|
||||
|
||||
#TODO: Fix
|
||||
opts[:time_stamp] = 1504200469610 #Time.now.utc
|
||||
opts[:time_stamp] = Time.now.utc
|
||||
self.post_data_async(SESSION_API_PATH, opts)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
module JobHandler
|
||||
def handle(job_details)
|
||||
raise 'JobHandler#handle is not implemented';
|
||||
end
|
||||
|
||||
def job_type_handled
|
||||
raise 'JobHandler#job_type_handled is not implemented';
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
require 'metasploit/framework/data_service/remote/msf_red/job_handler'
|
||||
|
||||
class MessageJobHandler
|
||||
include JobHandler
|
||||
|
||||
JOB_HANDLED = 'message'
|
||||
|
||||
def handle(message_hash)
|
||||
message = "User: #{message_hash['user_id']}, #{message_hash['message']}"
|
||||
banner = "*" * message.size
|
||||
puts "\n"
|
||||
puts "\n"
|
||||
puts banner
|
||||
puts message
|
||||
puts banner
|
||||
puts "\n"
|
||||
end
|
||||
|
||||
def job_type_handled
|
||||
JOB_HANDLED
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,123 @@
|
|||
require 'metasploit/framework/data_service'
|
||||
require 'metasploit/framework/data_service/remote/http/core'
|
||||
require 'metasploit/framework/data_service/remote/http/remote_service_endpoint'
|
||||
|
||||
class MSFRedService
|
||||
JOB_CHECK_INTERVAL_SEC = 5
|
||||
LOGIN_TIMEOUT_SEC = 10
|
||||
SESSION_KEY_VALUE = 'msf-session-key'
|
||||
LOGIN_ENDPOINT = '/login'
|
||||
JOBS_ENDPOINT = '/jobs'
|
||||
CONSOLE_SERVICE_HOST_NAME = 'console-service.metasploit.r7ops.com'
|
||||
CONSOLE_SERVICE_PORT = 8080
|
||||
|
||||
def initialize
|
||||
@client = Rex::Proto::Http::Client.new(CONSOLE_SERVICE_HOST_NAME, CONSOLE_SERVICE_PORT)
|
||||
@job_handlers = Hash.new()
|
||||
load_job_handlers
|
||||
end
|
||||
|
||||
# TODO: Obviously this is not secure
|
||||
def launch(username, password)
|
||||
if (do_login(username, password))
|
||||
inject_data_service
|
||||
start_job_thread
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
def load_job_handlers
|
||||
job_handler_path = File.dirname(__FILE__) + '/job_handlers/*'
|
||||
Dir.glob(job_handler_path).collect{|file_path|
|
||||
job_handler_class = File.basename(file_path, '.rb').classify
|
||||
require file_path
|
||||
job_handler_class_constant = job_handler_class.constantize
|
||||
job_handler = job_handler_class_constant.new
|
||||
@job_handlers[job_handler.job_type_handled] = job_handler
|
||||
}
|
||||
end
|
||||
|
||||
def inject_data_service
|
||||
remote_service_endpoint = Metasploit::Framework::DataService::RemoteServiceEndpoint.new(CONSOLE_SERVICE_HOST_NAME, CONSOLE_SERVICE_PORT)
|
||||
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(remote_service_endpoint)
|
||||
remote_data_service.set_header(SESSION_KEY_VALUE, @session_key)
|
||||
data_service_manager = Metasploit::Framework::DataService::DataProxy.instance
|
||||
data_service_manager.register_data_service(remote_data_service)
|
||||
end
|
||||
|
||||
def do_login(username, password)
|
||||
login_hash = {:username => username, :password => password}
|
||||
begin
|
||||
|
||||
request_opts = { 'method' => 'POST', 'ctype' => 'application/json', 'uri' => LOGIN_ENDPOINT, 'data' => login_hash.to_json }
|
||||
request = @client.request_raw(request_opts)
|
||||
response = @client._send_recv(request, LOGIN_TIMEOUT_SEC)
|
||||
|
||||
if response.code == 200
|
||||
data = JSON.parse(response.body)
|
||||
@session_key = data['session_key']
|
||||
puts "MSF Red console login successfull, session: #{@session_key}"
|
||||
return true
|
||||
else
|
||||
puts "Login failed: failed with code: #{response.code} message: #{response.body}"
|
||||
return false
|
||||
end
|
||||
rescue Exception => e
|
||||
puts "Problem with POST request: #{e.message}"
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def start_job_thread
|
||||
Thread.start {
|
||||
loop {
|
||||
sleep 5
|
||||
begin
|
||||
job_hash = get_next_job
|
||||
if (job_hash.nil? or job_hash.empty?)
|
||||
next
|
||||
end
|
||||
|
||||
type = job_hash['job_type']
|
||||
job_handler = @job_handlers[type]
|
||||
if (job_handler.nil?)
|
||||
puts "No registered job handler for type: #{type}"
|
||||
else
|
||||
job_handler.handle(job_hash['job_details'])
|
||||
end
|
||||
rescue Exception => e
|
||||
puts "Problem executing job: #{e.message}"
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def get_next_job
|
||||
request_opts = { 'method' => 'GET', 'ctype' => 'application/json', 'uri' => JOBS_ENDPOINT, 'headers' => {SESSION_KEY_VALUE => @session_key} }
|
||||
request = @client.request_raw(request_opts)
|
||||
response = @client._send_recv(request)
|
||||
|
||||
if response.code == 200
|
||||
if (response.body.nil? or response.body.empty?)
|
||||
return nil
|
||||
end
|
||||
|
||||
begin
|
||||
return JSON.parse(response.body)
|
||||
rescue Exception => e
|
||||
puts "Unable to parse: #{response.body}, reason: #{e.message}"
|
||||
return nil
|
||||
end
|
||||
|
||||
else
|
||||
puts "GET request: #{path} with body: #{json_body} failed with code: #{response.code} message: #{response.body}"
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -20,6 +20,7 @@ require 'msf/ui/console/command_dispatcher/jobs'
|
|||
require 'msf/ui/console/command_dispatcher/resource'
|
||||
require 'msf/ui/console/command_dispatcher/modules'
|
||||
require 'msf/util/document_generator'
|
||||
require 'metasploit/framework/data_service/remote/msf_red/msf_red_service'
|
||||
|
||||
module Msf
|
||||
module Ui
|
||||
|
@ -106,6 +107,7 @@ class Core
|
|||
"?" => "Help menu",
|
||||
"banner" => "Display an awesome metasploit banner",
|
||||
"cd" => "Change the current working directory",
|
||||
"msf_red_connect" => "Connect to MSF Platform",
|
||||
"connect" => "Communicate with a host",
|
||||
"color" => "Toggle color",
|
||||
"exit" => "Exit the console",
|
||||
|
@ -266,6 +268,20 @@ class Core
|
|||
|
||||
end
|
||||
|
||||
def cmd_msf_red_connect(*args)
|
||||
while (arg = args.shift)
|
||||
case arg
|
||||
when '-u'
|
||||
username = args.shift
|
||||
when '-p'
|
||||
password = args.shift
|
||||
end
|
||||
end
|
||||
|
||||
msf_red_service = MSFRedService.new()
|
||||
msf_red_service.launch(username, password)
|
||||
end
|
||||
|
||||
def cmd_connect_help
|
||||
print_line "Usage: connect [options] <host> <port>"
|
||||
print_line
|
||||
|
|
Loading…
Reference in New Issue