Land #9926, check remote data service before connecting
This PR adds a check prior to connecting to a remote data service to verify it is online and returning expected data. This prevents crashes that were occurring when unexpected responses were returned
This commit is contained in:
commit
a5172e066d
|
@ -6,8 +6,10 @@ require 'metasploit/framework/data_service/stubs/note_data_service'
|
|||
require 'metasploit/framework/data_service/stubs/web_data_service'
|
||||
require 'metasploit/framework/data_service/stubs/service_data_service'
|
||||
require 'metasploit/framework/data_service/stubs/session_data_service'
|
||||
require 'metasploit/framework/data_service/stubs/session_event_service'
|
||||
require 'metasploit/framework/data_service/stubs/exploit_data_service'
|
||||
require 'metasploit/framework/data_service/stubs/loot_data_service'
|
||||
require 'metasploit/framework/data_service/stubs/msf_data_service'
|
||||
|
||||
#
|
||||
# All data service implementations should include this module to ensure proper implementation
|
||||
|
@ -23,8 +25,10 @@ module DataService
|
|||
include NoteDataService
|
||||
include ServiceDataService
|
||||
include SessionDataService
|
||||
include SessionEventDataService
|
||||
include ExploitDataService
|
||||
include LootDataService
|
||||
include MsfDataService
|
||||
|
||||
def name
|
||||
raise 'DataService#name is not implemented';
|
||||
|
@ -34,6 +38,10 @@ module DataService
|
|||
raise 'DataService#active is not implemented';
|
||||
end
|
||||
|
||||
def active=(value)
|
||||
raise 'DataService#active= is not implemented';
|
||||
end
|
||||
|
||||
def is_local?
|
||||
raise 'DataService#is_local? is not implemented';
|
||||
end
|
||||
|
|
|
@ -52,30 +52,36 @@ class DataProxy
|
|||
end
|
||||
|
||||
#
|
||||
# Registers a data service with the proxy and immediately
|
||||
# set as primary if online
|
||||
# Registers the specified data service with the proxy
|
||||
# and immediately sets it as the primary if active
|
||||
#
|
||||
def register_data_service(data_service, online=false)
|
||||
def register_data_service(data_service)
|
||||
validate(data_service)
|
||||
data_service_id = @data_service_id += 1
|
||||
@data_services[data_service_id] = data_service
|
||||
set_data_service(data_service_id, online)
|
||||
set_data_service(data_service_id)
|
||||
end
|
||||
|
||||
#
|
||||
# Set the data service to be used
|
||||
#
|
||||
def set_data_service(data_service_id, online=false)
|
||||
def set_data_service(data_service_id)
|
||||
data_service = @data_services[data_service_id.to_i]
|
||||
if data_service.nil?
|
||||
raise "Data service with id: #{data_service_id} does not exist"
|
||||
end
|
||||
|
||||
if !online && !data_service.active
|
||||
raise "Data service not online: #{data_service.name}, not setting as active"
|
||||
if !data_service.is_local? && !data_service.active
|
||||
raise "Data service #{data_service.name} is not online, and won't be set as active"
|
||||
end
|
||||
|
||||
prev_data_service = @current_data_service
|
||||
@current_data_service = data_service
|
||||
# reset the previous data service's active flag if it is remote
|
||||
# to ensure checks are performed the next time it is set
|
||||
if !prev_data_service.nil? && !prev_data_service.is_local?
|
||||
prev_data_service.active = false
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -154,12 +160,12 @@ class DataProxy
|
|||
begin
|
||||
db_manager = opts.delete(:db_manager)
|
||||
if !db_manager.nil?
|
||||
register_data_service(db_manager, true)
|
||||
register_data_service(db_manager)
|
||||
@usable = true
|
||||
else
|
||||
@error = 'disabled'
|
||||
end
|
||||
rescue Exception => e
|
||||
rescue => e
|
||||
raise "Unable to initialize data service: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,6 +19,7 @@ module DataProxyAutoLoader
|
|||
autoload :DbExportDataProxy, 'metasploit/framework/data_service/proxy/db_export_data_proxy'
|
||||
autoload :DbImportDataProxy, 'metasploit/framework/data_service/proxy/db_import_data_proxy'
|
||||
autoload :VulnAttemptDataProxy, 'metasploit/framework/data_service/proxy/vuln_attempt_data_proxy'
|
||||
autoload :MsfDataProxy, 'metasploit/framework/data_service/proxy/msf_data_proxy'
|
||||
|
||||
include ServiceDataProxy
|
||||
include HostDataProxy
|
||||
|
@ -36,4 +37,5 @@ module DataProxyAutoLoader
|
|||
include DbExportDataProxy
|
||||
include DbImportDataProxy
|
||||
include VulnAttemptDataProxy
|
||||
include MsfDataProxy
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
module MsfDataProxy
|
||||
def get_msf_version
|
||||
begin
|
||||
data_service = self.get_data_service
|
||||
data_service.get_msf_version
|
||||
rescue => e
|
||||
self.log_error(e, "Problem retrieving Metasploit version")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,6 +25,7 @@ class RemoteHTTPDataService
|
|||
# @param [String] endpoint A valid http or https URL. Cannot be nil
|
||||
#
|
||||
def initialize(endpoint, framework, https_opts = {})
|
||||
@active = false
|
||||
validate_endpoint(endpoint)
|
||||
@endpoint = URI.parse(endpoint)
|
||||
@https_opts = https_opts
|
||||
|
@ -40,6 +41,26 @@ class RemoteHTTPDataService
|
|||
|
||||
end
|
||||
|
||||
def name
|
||||
"remote_data_service: (#{@endpoint})"
|
||||
end
|
||||
|
||||
def active
|
||||
# checks if data service is online when @active is falsey and makes the assignment
|
||||
# this is to prevent repetitive calls to check if data service is online
|
||||
# logic should be enhanced to considering data service connectivity
|
||||
# and future data service implementations
|
||||
@active ||= is_online?
|
||||
end
|
||||
|
||||
def active=(value)
|
||||
@active = value
|
||||
end
|
||||
|
||||
def is_local?
|
||||
false
|
||||
end
|
||||
|
||||
def error
|
||||
'none'
|
||||
end
|
||||
|
@ -152,7 +173,7 @@ class RemoteHTTPDataService
|
|||
rescue EOFError => e
|
||||
elog "No data was returned from the data service for request type/path : #{request_type}/#{path}, message: #{e.message}"
|
||||
return FailedResponse.new('')
|
||||
rescue Exception => e
|
||||
rescue => e
|
||||
elog "Problem with HTTP request for type/path: #{request_type}/#{path} message: #{e.message}"
|
||||
return FailedResponse.new('')
|
||||
ensure
|
||||
|
@ -160,21 +181,6 @@ class RemoteHTTPDataService
|
|||
end
|
||||
end
|
||||
|
||||
#
|
||||
# TODO: fix this
|
||||
#
|
||||
def active
|
||||
return true
|
||||
end
|
||||
|
||||
def name
|
||||
"remote_data_service: (#{@endpoint})"
|
||||
end
|
||||
|
||||
def is_local?
|
||||
false
|
||||
end
|
||||
|
||||
def set_header(key, value)
|
||||
@headers = Hash.new() if @headers.nil?
|
||||
|
||||
|
@ -224,6 +230,19 @@ class RemoteHTTPDataService
|
|||
raise 'Endpoint cannot be nil' if endpoint.nil?
|
||||
end
|
||||
|
||||
#
|
||||
# Checks if the data service is online by making a request
|
||||
# for the Metasploit version number from the remote endpoint
|
||||
#
|
||||
def is_online?
|
||||
response = self.get_msf_version
|
||||
if response && !response[:metasploit_version].empty?
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def build_request(request, data_hash)
|
||||
request.content_type = 'application/json'
|
||||
if !data_hash.nil? && !data_hash.empty?
|
||||
|
|
|
@ -17,6 +17,7 @@ module DataServiceAutoLoader
|
|||
autoload :RemoteNmapDataService, 'metasploit/framework/data_service/remote/http/remote_nmap_data_service'
|
||||
autoload :RemoteDbExportDataService, 'metasploit/framework/data_service/remote/http/remote_db_export_data_service'
|
||||
autoload :RemoteVulnAttemptDataService, 'metasploit/framework/data_service/remote/http/remote_vuln_attempt_data_service'
|
||||
autoload :RemoteMsfDataService, 'metasploit/framework/data_service/remote/http/remote_msf_data_service'
|
||||
|
||||
include RemoteHostDataService
|
||||
include RemoteEventDataService
|
||||
|
@ -33,4 +34,5 @@ module DataServiceAutoLoader
|
|||
include RemoteNmapDataService
|
||||
include RemoteDbExportDataService
|
||||
include RemoteVulnAttemptDataService
|
||||
include RemoteMsfDataService
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||
|
||||
module RemoteMsfDataService
|
||||
include ResponseDataHelper
|
||||
|
||||
MSF_API_PATH = '/api/v1/msf'
|
||||
MSF_VERSION_API_PATH = "#{MSF_API_PATH}/version"
|
||||
|
||||
def get_msf_version
|
||||
json_to_hash(self.get_data(MSF_VERSION_API_PATH, nil, nil))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
module MsfDataService
|
||||
def get_msf_version
|
||||
raise 'MsfDataService#get_msf_version is not implemented'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
module MsfServlet
|
||||
|
||||
def self.api_path
|
||||
'/api/v1/msf'
|
||||
end
|
||||
|
||||
def self.api_version_path
|
||||
"#{MsfServlet.api_path}/version"
|
||||
end
|
||||
|
||||
def self.registered(app)
|
||||
app.get MsfServlet.api_version_path, &get_msf_version
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
def self.get_msf_version
|
||||
lambda {
|
||||
set_json_response({metasploit_version: Metasploit::Framework::VERSION})
|
||||
}
|
||||
end
|
||||
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
module OnlineTestServlet
|
||||
|
||||
def self.api_path
|
||||
'/api/v1/online'
|
||||
end
|
||||
|
||||
def self.registered(app)
|
||||
app.get OnlineTestServlet.api_path, &get_active
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
def self.get_active
|
||||
lambda {
|
||||
set_empty_response()
|
||||
}
|
||||
end
|
||||
|
||||
end
|
|
@ -5,7 +5,7 @@ require 'msf/core/db_manager/http/servlet/note_servlet'
|
|||
require 'msf/core/db_manager/http/servlet/vuln_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/event_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/web_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/online_test_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/msf_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/workspace_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/service_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/session_servlet'
|
||||
|
@ -26,7 +26,7 @@ class SinatraApp < Sinatra::Base
|
|||
register VulnServlet
|
||||
register EventServlet
|
||||
register WebServlet
|
||||
register OnlineTestServlet
|
||||
register MsfServlet
|
||||
register NoteServlet
|
||||
register WorkspaceServlet
|
||||
register ServiceServlet
|
||||
|
|
|
@ -1994,7 +1994,7 @@ class Db
|
|||
framework.db.register_data_service(remote_data_service)
|
||||
print_line "Registered data service: #{remote_data_service.name}"
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
rescue Exception => e
|
||||
rescue => e
|
||||
print_error "There was a problem registering the remote data service: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
@ -2004,7 +2004,7 @@ class Db
|
|||
data_service = framework.db.set_data_service(service_id)
|
||||
framework.db.workspace = framework.db.default_workspace
|
||||
data_service
|
||||
rescue Exception => e
|
||||
rescue => e
|
||||
print_error "Unable to set data service: #{e.message}"
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue