Merge branch 'goliath' into add_https
commit
2ffd627c56
|
@ -1,8 +1,6 @@
|
||||||
require 'singleton'
|
|
||||||
require 'open3'
|
require 'open3'
|
||||||
require 'rex/ui'
|
require 'rex/ui'
|
||||||
require 'rex/logging'
|
require 'rex/logging'
|
||||||
require 'msf/core/db_manager'
|
|
||||||
require 'metasploit/framework/data_service/remote/http/core'
|
require 'metasploit/framework/data_service/remote/http/core'
|
||||||
require 'metasploit/framework/data_service/proxy/data_proxy_auto_loader'
|
require 'metasploit/framework/data_service/proxy/data_proxy_auto_loader'
|
||||||
|
|
||||||
|
@ -14,11 +12,17 @@ module Metasploit
|
||||||
module Framework
|
module Framework
|
||||||
module DataService
|
module DataService
|
||||||
class DataProxy
|
class DataProxy
|
||||||
include Singleton
|
|
||||||
include DataProxyAutoLoader
|
include DataProxyAutoLoader
|
||||||
|
|
||||||
attr_reader :usable
|
attr_reader :usable
|
||||||
|
|
||||||
|
def initialize(opts = {})
|
||||||
|
@data_services = {}
|
||||||
|
@data_service_id = 0
|
||||||
|
@usable = false
|
||||||
|
setup(opts)
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Returns current error state
|
# Returns current error state
|
||||||
#
|
#
|
||||||
|
@ -47,34 +51,6 @@ class DataProxy
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Initializes the data service to be used - primarily on startup
|
|
||||||
#
|
|
||||||
def init(framework, opts)
|
|
||||||
@mutex.synchronize {
|
|
||||||
if (@initialized)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
if (opts['DisableDatabase'])
|
|
||||||
@error = 'disabled'
|
|
||||||
return
|
|
||||||
elsif (opts['DatabaseRemoteProcess'])
|
|
||||||
run_remote_db_process(opts)
|
|
||||||
else
|
|
||||||
run_local_db_process(framework, opts)
|
|
||||||
end
|
|
||||||
@usable = true
|
|
||||||
@initialized = true
|
|
||||||
rescue Exception => e
|
|
||||||
puts "Unable to initialize a dataservice #{e.message}"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Registers a data service with the proxy and immediately
|
# Registers a data service with the proxy and immediately
|
||||||
# set as primary if online
|
# set as primary if online
|
||||||
|
@ -130,6 +106,14 @@ class DataProxy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def respond_to?(method_name, include_private=false)
|
||||||
|
unless @data_service.nil?
|
||||||
|
return @data_service.respond_to?(method_name, include_private)
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Attempt to shutdown the local db process if it exists
|
# Attempt to shutdown the local db process if it exists
|
||||||
#
|
#
|
||||||
|
@ -144,10 +128,6 @@ class DataProxy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#########
|
|
||||||
protected
|
|
||||||
#########
|
|
||||||
|
|
||||||
def get_data_service
|
def get_data_service
|
||||||
raise 'No registered data_service' unless @data_service
|
raise 'No registered data_service' unless @data_service
|
||||||
return @data_service
|
return @data_service
|
||||||
|
@ -157,12 +137,21 @@ class DataProxy
|
||||||
private
|
private
|
||||||
#######
|
#######
|
||||||
|
|
||||||
def initialize
|
def setup(opts)
|
||||||
@data_services = {}
|
begin
|
||||||
@data_service_id = 0
|
db_manager = opts.delete(:db_manager)
|
||||||
@usable = false
|
if !db_manager.nil?
|
||||||
@initialized = false
|
register_data_service(db_manager, true)
|
||||||
@mutex = Mutex.new()
|
@usable = true
|
||||||
|
elsif opts['DatabaseRemoteProcess']
|
||||||
|
run_remote_db_process(opts)
|
||||||
|
@usable = true
|
||||||
|
else
|
||||||
|
@error = 'disabled'
|
||||||
|
end
|
||||||
|
rescue Exception => e
|
||||||
|
puts "Unable to initialize a dataservice #{e.message}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate(data_service)
|
def validate(data_service)
|
||||||
|
@ -182,15 +171,6 @@ class DataProxy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def run_local_db_process(framework, opts)
|
|
||||||
puts 'Initializing local db process'
|
|
||||||
db_manager = Msf::DBManager.new(framework)
|
|
||||||
if (db_manager.usable and not opts['SkipDatabaseInit'])
|
|
||||||
register_data_service(db_manager, true)
|
|
||||||
db_manager.init_db(opts)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_remote_db_process(opts)
|
def run_remote_db_process(opts)
|
||||||
# started with no signal to prevent ctrl-c from taking out db
|
# started with no signal to prevent ctrl-c from taking out db
|
||||||
db_script = File.join( Msf::Config.install_root, "msfdb -ns")
|
db_script = File.join( Msf::Config.install_root, "msfdb -ns")
|
||||||
|
|
|
@ -2,6 +2,7 @@ require 'metasploit/framework/data_service'
|
||||||
require 'metasploit/framework/data_service/remote/http/data_service_auto_loader'
|
require 'metasploit/framework/data_service/remote/http/data_service_auto_loader'
|
||||||
require 'net/http'
|
require 'net/http'
|
||||||
require 'net/https'
|
require 'net/https'
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
#
|
#
|
||||||
# Parent data service for managing metasploit data in/on a separate process/machine over HTTP(s)
|
# Parent data service for managing metasploit data in/on a separate process/machine over HTTP(s)
|
||||||
|
@ -13,7 +14,7 @@ class RemoteHTTPDataService
|
||||||
include Metasploit::Framework::DataService
|
include Metasploit::Framework::DataService
|
||||||
include DataServiceAutoLoader
|
include DataServiceAutoLoader
|
||||||
|
|
||||||
ONLINE_TEST_URL = "/api/1/msf/online"
|
ONLINE_TEST_URL = "/api/v1/online"
|
||||||
EXEC_ASYNC = { :exec_async => true }
|
EXEC_ASYNC = { :exec_async => true }
|
||||||
GET_REQUEST = 'GET'
|
GET_REQUEST = 'GET'
|
||||||
POST_REQUEST = 'POST'
|
POST_REQUEST = 'POST'
|
||||||
|
@ -30,71 +31,94 @@ class RemoteHTTPDataService
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# POST data and don't wait for the endpoint to process the data before getting a response
|
# POST data to the HTTP endpoint and don't wait for the endpoint to process the data before getting a response
|
||||||
#
|
#
|
||||||
def post_data_async(path, data_hash)
|
# @param path - The URI path to send the request
|
||||||
make_request(POST_REQUEST, path, data_hash.merge(EXEC_ASYNC))
|
# @param data_hash - A hash representation of the object to be posted. Cannot be nil or empty.
|
||||||
|
# @param query - A hash representation of the URI query data. Key-value pairs will be URL-encoded.
|
||||||
|
#
|
||||||
|
# @return A wrapped response (ResponseWrapper), see below.
|
||||||
|
#
|
||||||
|
def post_data_async(path, data_hash, query = nil)
|
||||||
|
make_request(POST_REQUEST, path, data_hash.merge(EXEC_ASYNC), query)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# POST data to the HTTP endpoint
|
# POST data to the HTTP endpoint
|
||||||
#
|
#
|
||||||
|
# @param path - The URI path to send the request
|
||||||
# @param data_hash - A hash representation of the object to be posted. Cannot be nil or empty.
|
# @param data_hash - A hash representation of the object to be posted. Cannot be nil or empty.
|
||||||
# @param path - The URI path to post to
|
# @param query - A hash representation of the URI query data. Key-value pairs will be URL-encoded.
|
||||||
#
|
#
|
||||||
# @return A wrapped response (ResponseWrapper), see below.
|
# @return A wrapped response (ResponseWrapper), see below.
|
||||||
#
|
#
|
||||||
def post_data(path, data_hash)
|
def post_data(path, data_hash, query = nil)
|
||||||
make_request(POST_REQUEST, path, data_hash)
|
make_request(POST_REQUEST, path, data_hash, query)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# GET data from the HTTP endpoint
|
# GET data from the HTTP endpoint
|
||||||
#
|
#
|
||||||
# @param path - The URI path to post to
|
# @param path - The URI path to send the request
|
||||||
# @param data_hash - A hash representation of the object to be posted. Can be nil or empty.
|
# @param data_hash - A hash representation of the object to be included. Can be nil or empty.
|
||||||
|
# @param query - A hash representation of the URI query data. Key-value pairs will be URL-encoded.
|
||||||
#
|
#
|
||||||
# @return A wrapped response (ResponseWrapper), see below.
|
# @return A wrapped response (ResponseWrapper), see below.
|
||||||
#
|
#
|
||||||
def get_data(path, data_hash = nil)
|
def get_data(path, data_hash = nil, query = nil)
|
||||||
make_request(GET_REQUEST, path, data_hash)
|
make_request(GET_REQUEST, path, data_hash, query)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Send DELETE request to delete the specified resource from the HTTP endpoint
|
# Send DELETE request to delete the specified resource from the HTTP endpoint
|
||||||
#
|
#
|
||||||
# @param path - The URI path to send the delete
|
# @param path - The URI path to send the request
|
||||||
# @param data_hash - A hash representation of the object to be deleted. Cannot be nil or empty.
|
# @param data_hash - A hash representation of the object to be deleted. Cannot be nil or empty.
|
||||||
|
# @param query - A hash representation of the URI query data. Key-value pairs will be URL-encoded.
|
||||||
#
|
#
|
||||||
# @return A wrapped response (ResponseWrapper), see below.
|
# @return A wrapped response (ResponseWrapper), see below.
|
||||||
#
|
#
|
||||||
def delete_data(path, data_hash)
|
def delete_data(path, data_hash, query = nil)
|
||||||
make_request(DELETE_REQUEST, path, data_hash)
|
make_request(DELETE_REQUEST, path, data_hash, query)
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_request(request_type, path, data_hash = nil)
|
#
|
||||||
|
# Make the specified request_type
|
||||||
|
#
|
||||||
|
# @param request_type - A string representation of the HTTP method
|
||||||
|
# @param path - The URI path to send the request
|
||||||
|
# @param data_hash - A hash representation of the object to be included in the request. Cannot be nil or empty.
|
||||||
|
# @param query - A hash representation of the URI query data. Key-value pairs will be URL-encoded.
|
||||||
|
#
|
||||||
|
# @return A wrapped response (ResponseWrapper)
|
||||||
|
#
|
||||||
|
def make_request(request_type, path, data_hash = nil, query = nil)
|
||||||
begin
|
begin
|
||||||
puts "#{Time.now} - HTTP #{request_type} request to #{path} with #{data_hash ? data_hash : "nil"}"
|
query_str = (!query.nil? && !query.empty?) ? URI.encode_www_form(query) : nil
|
||||||
|
uri = URI::HTTP::build({path: path, query: query_str})
|
||||||
|
puts "#{Time.now} - HTTP #{request_type} request to #{uri.request_uri} with #{data_hash ? data_hash : "nil"}"
|
||||||
|
|
||||||
client = @client_pool.pop()
|
client = @client_pool.pop()
|
||||||
case request_type
|
case request_type
|
||||||
when GET_REQUEST
|
when GET_REQUEST
|
||||||
request = Net::HTTP::Get.new(path)
|
request = Net::HTTP::Get.new(uri.request_uri)
|
||||||
when POST_REQUEST
|
when POST_REQUEST
|
||||||
request = Net::HTTP::Post.new(path)
|
request = Net::HTTP::Post.new(uri.request_uri)
|
||||||
when DELETE_REQUEST
|
when DELETE_REQUEST
|
||||||
request = Net::HTTP::Delete.new(path)
|
request = Net::HTTP::Delete.new(uri.request_uri)
|
||||||
else
|
else
|
||||||
raise Exception, 'A request_type must be specified'
|
raise Exception, 'A request_type must be specified'
|
||||||
end
|
end
|
||||||
built_request = build_request(request, data_hash)
|
built_request = build_request(request, data_hash)
|
||||||
response = client.request(built_request)
|
response = client.request(built_request)
|
||||||
|
|
||||||
if response.code == "200"
|
case response
|
||||||
# puts 'request sent successfully'
|
when Net::HTTPOK
|
||||||
return SuccessResponse.new(response)
|
# puts 'request sent successfully'
|
||||||
else
|
return SuccessResponse.new(response)
|
||||||
puts "HTTP #{request_type} request: #{path} failed with code: #{response.code} message: #{response.body}"
|
else
|
||||||
return FailedResponse.new(response)
|
puts "HTTP #{request_type} request: #{uri.request_uri} failed with code: #{response.code} message: #{response.body}"
|
||||||
|
return FailedResponse.new(response)
|
||||||
end
|
end
|
||||||
rescue EOFError => e
|
rescue EOFError => e
|
||||||
puts "ERROR: No data was returned from the server."
|
puts "ERROR: No data was returned from the server."
|
||||||
|
@ -147,9 +171,7 @@ class RemoteHTTPDataService
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_header(key, value)
|
def set_header(key, value)
|
||||||
if (@headers.nil?)
|
@headers = Hash.new() if @headers.nil?
|
||||||
@headers = Hash.new()
|
|
||||||
end
|
|
||||||
|
|
||||||
@headers[key] = value
|
@headers[key] = value
|
||||||
end
|
end
|
||||||
|
@ -199,24 +221,20 @@ class RemoteHTTPDataService
|
||||||
|
|
||||||
def append_workspace(data_hash)
|
def append_workspace(data_hash)
|
||||||
workspace = data_hash[:workspace]
|
workspace = data_hash[:workspace]
|
||||||
unless (workspace)
|
workspace = data_hash.delete(:wspace) unless workspace
|
||||||
workspace = data_hash.delete(:wspace)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (workspace && (workspace.is_a?(OpenStruct) || workspace.is_a?(::Mdm::Workspace)))
|
if workspace && (workspace.is_a?(OpenStruct) || workspace.is_a?(::Mdm::Workspace))
|
||||||
data_hash[:workspace] = workspace.name
|
data_hash[:workspace] = workspace.name
|
||||||
end
|
end
|
||||||
|
|
||||||
if (workspace.nil?)
|
data_hash[:workspace] = current_workspace_name if workspace.nil?
|
||||||
data_hash[:workspace] = current_workspace_name
|
|
||||||
end
|
|
||||||
|
|
||||||
data_hash
|
data_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_request(request, data_hash)
|
def build_request(request, data_hash)
|
||||||
request.content_type = 'application/json'
|
request.content_type = 'application/json'
|
||||||
if (!data_hash.nil? && !data_hash.empty?)
|
if !data_hash.nil? && !data_hash.empty?
|
||||||
data_hash.each do |k,v|
|
data_hash.each do |k,v|
|
||||||
if v.is_a?(Msf::Session)
|
if v.is_a?(Msf::Session)
|
||||||
puts "#{Time.now} - DEBUG: Dropping Msf::Session object before converting to JSON."
|
puts "#{Time.now} - DEBUG: Dropping Msf::Session object before converting to JSON."
|
||||||
|
@ -230,7 +248,7 @@ class RemoteHTTPDataService
|
||||||
request.body = json_body
|
request.body = json_body
|
||||||
end
|
end
|
||||||
|
|
||||||
if (!@headers.nil? && !@headers.empty?)
|
if !@headers.nil? && !@headers.empty?
|
||||||
@headers.each do |key, value|
|
@headers.each do |key, value|
|
||||||
request[key] = value
|
request[key] = value
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||||
module RemoteCredentialDataService
|
module RemoteCredentialDataService
|
||||||
include ResponseDataHelper
|
include ResponseDataHelper
|
||||||
|
|
||||||
CREDENTIAL_API_PATH = '/api/1/msf/credential'
|
CREDENTIAL_API_PATH = '/api/v1/credentials'
|
||||||
# "MDM_CLASS" is a little misleading since it is not in that repo but trying to keep naming consistent across DataServices
|
# "MDM_CLASS" is a little misleading since it is not in that repo but trying to keep naming consistent across DataServices
|
||||||
CREDENTIAL_MDM_CLASS = 'Metasploit::Credential::Core'
|
CREDENTIAL_MDM_CLASS = 'Metasploit::Credential::Core'
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module RemoteEventDataService
|
module RemoteEventDataService
|
||||||
EVENT_API_PATH = '/api/1/msf/event'
|
EVENT_API_PATH = '/api/v1/events'
|
||||||
|
|
||||||
def report_event(opts)
|
def report_event(opts)
|
||||||
self.post_data_async(EVENT_API_PATH, opts)
|
self.post_data_async(EVENT_API_PATH, opts)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module RemoteExploitDataService
|
module RemoteExploitDataService
|
||||||
EXPLOIT_API_PATH = '/api/1/msf/exploit'
|
EXPLOIT_API_PATH = '/api/v1/exploits'
|
||||||
|
|
||||||
def report_exploit_attempt(host, opts)
|
def report_exploit_attempt(host, opts)
|
||||||
opts[:host] = host
|
opts[:host] = host
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||||
module RemoteHostDataService
|
module RemoteHostDataService
|
||||||
include ResponseDataHelper
|
include ResponseDataHelper
|
||||||
|
|
||||||
HOST_API_PATH = '/api/1/msf/host'
|
HOST_API_PATH = '/api/v1/hosts'
|
||||||
HOST_SEARCH_PATH = HOST_API_PATH + "/search"
|
HOST_SEARCH_PATH = HOST_API_PATH + "/search"
|
||||||
HOST_MDM_CLASS = 'Mdm::Host'
|
HOST_MDM_CLASS = 'Mdm::Host'
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||||
module RemoteLootDataService
|
module RemoteLootDataService
|
||||||
include ResponseDataHelper
|
include ResponseDataHelper
|
||||||
|
|
||||||
LOOT_API_PATH = '/api/1/msf/loot'
|
LOOT_API_PATH = '/api/v1/loots'
|
||||||
LOOT_MDM_CLASS = 'Mdm::Loot'
|
LOOT_MDM_CLASS = 'Mdm::Loot'
|
||||||
|
|
||||||
def loot(opts = {})
|
def loot(opts = {})
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||||
module RemoteNmapDataService
|
module RemoteNmapDataService
|
||||||
include ResponseDataHelper
|
include ResponseDataHelper
|
||||||
|
|
||||||
NMAP_PATH = '/api/1/msf/nmap'
|
NMAP_PATH = '/api/v1/nmaps'
|
||||||
|
|
||||||
def import_nmap_xml_file(opts)
|
def import_nmap_xml_file(opts)
|
||||||
filename = opts[:filename]
|
filename = opts[:filename]
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||||
module RemoteNoteDataService
|
module RemoteNoteDataService
|
||||||
include ResponseDataHelper
|
include ResponseDataHelper
|
||||||
|
|
||||||
NOTE_API_PATH = '/api/1/msf/note'
|
NOTE_API_PATH = '/api/v1/notes'
|
||||||
|
|
||||||
def report_note(opts)
|
def report_note(opts)
|
||||||
self.post_data_async(NOTE_API_PATH, opts)
|
self.post_data_async(NOTE_API_PATH, opts)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module RemoteServiceDataService
|
module RemoteServiceDataService
|
||||||
SERVICE_API_PATH = '/api/1/msf/service'
|
SERVICE_API_PATH = '/api/v1/services'
|
||||||
|
|
||||||
def report_service(opts)
|
def report_service(opts)
|
||||||
self.post_data_async(SERVICE_API_PATH, opts)
|
self.post_data_async(SERVICE_API_PATH, opts)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module RemoteSessionDataService
|
module RemoteSessionDataService
|
||||||
|
|
||||||
SESSION_API_PATH = '/api/1/msf/session'
|
SESSION_API_PATH = '/api/v1/sessions'
|
||||||
SESSION_MDM_CLASS = 'Mdm::Session'
|
SESSION_MDM_CLASS = 'Mdm::Session'
|
||||||
|
|
||||||
def report_session(opts)
|
def report_session(opts)
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||||
module RemoteSessionEventDataService
|
module RemoteSessionEventDataService
|
||||||
include ResponseDataHelper
|
include ResponseDataHelper
|
||||||
|
|
||||||
SESSION_EVENT_API_PATH = '/api/1/msf/session_event'
|
SESSION_EVENT_API_PATH = '/api/v1/session-events'
|
||||||
SESSION_EVENT_MDM_CLASS = 'Mdm::SessionEvent'
|
SESSION_EVENT_MDM_CLASS = 'Mdm::SessionEvent'
|
||||||
|
|
||||||
def session_events(opts = {})
|
def session_events(opts = {})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module RemoteVulnDataService
|
module RemoteVulnDataService
|
||||||
|
|
||||||
VULN_API_PATH = '/api/1/msf/vuln'
|
VULN_API_PATH = '/api/v1/vulns'
|
||||||
def report_vuln(opts)
|
def report_vuln(opts)
|
||||||
self.post_data_async(VULN_API_PATH, opts)
|
self.post_data_async(VULN_API_PATH, opts)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||||
module RemoteWebDataService
|
module RemoteWebDataService
|
||||||
include ResponseDataHelper
|
include ResponseDataHelper
|
||||||
|
|
||||||
WEB_API_PATH = '/api/1/msf/web'
|
WEB_API_PATH = '/api/v1/webs'
|
||||||
|
|
||||||
def report_web_site(opts)
|
def report_web_site(opts)
|
||||||
self.post_data_async(WEB_API_PATH, opts)
|
self.post_data_async(WEB_API_PATH, opts)
|
||||||
|
|
|
@ -3,8 +3,9 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
|
||||||
module RemoteWorkspaceDataService
|
module RemoteWorkspaceDataService
|
||||||
include ResponseDataHelper
|
include ResponseDataHelper
|
||||||
|
|
||||||
WORKSPACE_COUNTS_API_PATH = '/api/1/msf/workspace/counts'
|
# TODO: should counts be a flag in query data for the workspaces resource?
|
||||||
WORKSPACE_API_PATH = '/api/1/msf/workspace'
|
WORKSPACE_COUNTS_API_PATH = '/api/v1/workspaces/counts'
|
||||||
|
WORKSPACE_API_PATH = '/api/v1/workspaces'
|
||||||
WORKSPACE_MDM_CLASS = 'Mdm::Workspace'
|
WORKSPACE_MDM_CLASS = 'Mdm::Workspace'
|
||||||
DEFAULT_WORKSPACE_NAME = 'default'
|
DEFAULT_WORKSPACE_NAME = 'default'
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ module RemoteWorkspaceDataService
|
||||||
end
|
end
|
||||||
|
|
||||||
def workspace_associations_counts()
|
def workspace_associations_counts()
|
||||||
json_to_mdm_object(self.get_data(WORKSPACE_API_PATH, []), WORKSPACE_MDM_CLASS, [])
|
json_to_mdm_object(self.get_data(WORKSPACE_COUNTS_API_PATH, []), WORKSPACE_MDM_CLASS, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
#########
|
#########
|
||||||
|
|
|
@ -18,7 +18,7 @@ module Msf::DBManager::Connection
|
||||||
migrate
|
migrate
|
||||||
|
|
||||||
# Set the default workspace
|
# Set the default workspace
|
||||||
framework.db.workspace = framework.db.default_workspace
|
self.workspace = self.default_workspace
|
||||||
rescue ::Exception => exception
|
rescue ::Exception => exception
|
||||||
self.error = exception
|
self.error = exception
|
||||||
elog("DB.connect threw an exception: #{exception}")
|
elog("DB.connect threw an exception: #{exception}")
|
||||||
|
|
|
@ -56,7 +56,7 @@ module Msf::DBManager::Host
|
||||||
|
|
||||||
# Exactly like report_host but waits for the database to create a host and returns it.
|
# Exactly like report_host but waits for the database to create a host and returns it.
|
||||||
def find_or_create_host(opts)
|
def find_or_create_host(opts)
|
||||||
host = get_host(opts)
|
host = get_host(opts.clone)
|
||||||
return host unless host.nil?
|
return host unless host.nil?
|
||||||
|
|
||||||
report_host(opts)
|
report_host(opts)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module CredentialServlet
|
module CredentialServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/credential'
|
'/api/v1/credentials'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module EventServlet
|
module EventServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/event'
|
'/api/v1/events'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module ExploitServlet
|
module ExploitServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/exploit'
|
'/api/v1/exploits'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module HostServlet
|
module HostServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/host'
|
'/api/v1/hosts'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module LootServlet
|
module LootServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/loot'
|
'/api/v1/loots'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module NmapServlet
|
module NmapServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/nmap'
|
'/api/v1/nmaps'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module NoteServlet
|
module NoteServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/note'
|
'/api/v1/notes'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module OnlineTestServlet
|
module OnlineTestServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/online'
|
'/api/v1/online'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module ServiceServlet
|
module ServiceServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/service'
|
'/api/v1/services'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module SessionEventServlet
|
module SessionEventServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/session_event'
|
'/api/v1/session-events'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module SessionServlet
|
module SessionServlet
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/session'
|
'/api/v1/sessions'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module VulnServlet
|
module VulnServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/vuln'
|
'/api/v1/vulns'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module WebServlet
|
module WebServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/web'
|
'/api/v1/webs'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module WorkspaceServlet
|
module WorkspaceServlet
|
||||||
|
|
||||||
def self.api_path
|
def self.api_path
|
||||||
'/api/1/msf/workspace'
|
'/api/v1/workspaces'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.registered(app)
|
def self.registered(app)
|
||||||
|
|
|
@ -58,20 +58,23 @@ module Msf::DBManager::Workspace
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_all_workspaces()
|
def delete_all_workspaces()
|
||||||
delete_workspaces(workspaces.map(&:name))
|
return delete_workspaces(workspaces.map(&:name))
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_workspaces(names)
|
def delete_workspaces(names)
|
||||||
|
status_msg = []
|
||||||
|
error_msg = []
|
||||||
|
|
||||||
switched = false
|
switched = false
|
||||||
# Delete workspaces
|
# Delete workspaces
|
||||||
names.each do |name|
|
names.each do |name|
|
||||||
workspace = framework.db.find_workspace(name)
|
workspace = framework.db.find_workspace(name)
|
||||||
if workspace.nil?
|
if workspace.nil?
|
||||||
print_error("Workspace not found: #{name}")
|
error << "Workspace not found: #{name}"
|
||||||
elsif workspace.default?
|
elsif workspace.default?
|
||||||
workspace.destroy
|
workspace.destroy
|
||||||
workspace = framework.db.add_workspace(name)
|
workspace = framework.db.add_workspace(name)
|
||||||
print_status("Deleted and recreated the default workspace")
|
status_msg << 'Deleted and recreated the default workspace'
|
||||||
else
|
else
|
||||||
# switch to the default workspace if we're about to delete the current one
|
# switch to the default workspace if we're about to delete the current one
|
||||||
if framework.db.workspace.name == workspace.name
|
if framework.db.workspace.name == workspace.name
|
||||||
|
@ -80,10 +83,11 @@ module Msf::DBManager::Workspace
|
||||||
end
|
end
|
||||||
# now destroy the named workspace
|
# now destroy the named workspace
|
||||||
workspace.destroy
|
workspace.destroy
|
||||||
print_status("Deleted workspace: #{name}")
|
status_msg << "Deleted workspace: #{name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
print_status("Switched workspace: #{framework.db.workspace.name}") if switched
|
(status_msg << "Switched workspace: #{framework.db.workspace.name}") if switched
|
||||||
|
return status_msg, error_msg
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -199,7 +199,7 @@ class Framework
|
||||||
# @return [Metasploit::Framework::DataService::DataProxy]
|
# @return [Metasploit::Framework::DataService::DataProxy]
|
||||||
def db
|
def db
|
||||||
synchronize {
|
synchronize {
|
||||||
@db ||= Metasploit::Framework::DataService::DataProxy.instance
|
@db ||= get_db
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -268,6 +268,19 @@ protected
|
||||||
attr_writer :db # :nodoc:
|
attr_writer :db # :nodoc:
|
||||||
attr_writer :uuid_db # :nodoc:
|
attr_writer :uuid_db # :nodoc:
|
||||||
attr_writer :browser_profiles # :nodoc:
|
attr_writer :browser_profiles # :nodoc:
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def get_db
|
||||||
|
if !options['DatabaseRemoteProcess'] && !options['DisableDatabase']
|
||||||
|
db_manager = Msf::DBManager.new(self)
|
||||||
|
db_manager.init_db(options)
|
||||||
|
options[:db_manager] = db_manager
|
||||||
|
end
|
||||||
|
|
||||||
|
Metasploit::Framework::DataService::DataProxy.new(options)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class FrameworkEventSubscriber
|
class FrameworkEventSubscriber
|
||||||
|
|
|
@ -104,7 +104,7 @@ module Msf::Modules::Metadata::Store
|
||||||
|
|
||||||
def get_user_store
|
def get_user_store
|
||||||
store_dir = ::File.join(Msf::Config.config_directory, "store")
|
store_dir = ::File.join(Msf::Config.config_directory, "store")
|
||||||
FileUtils.mkdir(store_dir) if !::File.exist?(store_dir)
|
FileUtils.makedirs(store_dir) if !::File.exist?(store_dir)
|
||||||
return ::File.join(store_dir, UserMetaDataFile)
|
return ::File.join(store_dir, UserMetaDataFile)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -393,7 +393,6 @@ class Creds
|
||||||
}
|
}
|
||||||
|
|
||||||
tbl = Rex::Text::Table.new(tbl_opts)
|
tbl = Rex::Text::Table.new(tbl_opts)
|
||||||
opts = {}
|
|
||||||
opts[:wspace] = framework.db.workspace
|
opts[:wspace] = framework.db.workspace
|
||||||
query = framework.db.creds(opts)
|
query = framework.db.creds(opts)
|
||||||
|
|
||||||
|
|
|
@ -89,13 +89,11 @@ module Msf
|
||||||
end
|
end
|
||||||
|
|
||||||
def cmd_set_data_service(service_id)
|
def cmd_set_data_service(service_id)
|
||||||
data_proxy = Metasploit::Framework::DataService::DataProxy.instance
|
framework.db.set_data_service(service_id)
|
||||||
data_proxy.set_data_service(service_id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def cmd_list_data_services()
|
def cmd_list_data_services()
|
||||||
data_service_manager = Metasploit::Framework::DataService::DataProxy.instance
|
framework.db.print_data_services
|
||||||
data_service_manager.print_data_services
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def cmd_add_data_service(*args)
|
def cmd_add_data_service(*args)
|
||||||
|
@ -127,8 +125,7 @@ module Msf
|
||||||
|
|
||||||
endpoint = "#{protocol}://#{host}:#{port}"
|
endpoint = "#{protocol}://#{host}:#{port}"
|
||||||
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(endpoint, https_opts)
|
remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(endpoint, https_opts)
|
||||||
data_service_manager = Metasploit::Framework::DataService::DataProxy.instance
|
framework.db.register_data_service(remote_data_service)
|
||||||
data_service_manager.register_data_service(remote_data_service)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def cmd_add_data_service_help
|
def cmd_add_data_service_help
|
||||||
|
@ -157,8 +154,7 @@ module Msf
|
||||||
end
|
end
|
||||||
|
|
||||||
puts 'Reporting test host to data service'
|
puts 'Reporting test host to data service'
|
||||||
data_service = Metasploit::Framework::DataService::DataProxy.instance
|
framework.db.report_host host
|
||||||
data_service.report_host host
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def cmd_test_data_service_loot(*args)
|
def cmd_test_data_service_loot(*args)
|
||||||
|
@ -181,8 +177,7 @@ module Msf
|
||||||
end
|
end
|
||||||
|
|
||||||
puts 'Reporting test loot to data service'
|
puts 'Reporting test loot to data service'
|
||||||
data_service = Metasploit::Framework::DataService::DataProxy.instance
|
framework.db.report_loot loot
|
||||||
data_service.report_loot loot
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def cmd_perf_test_data_service_loot(*args)
|
def cmd_perf_test_data_service_loot(*args)
|
||||||
|
@ -226,11 +221,10 @@ module Msf
|
||||||
end
|
end
|
||||||
|
|
||||||
puts 'Reporting test loot to data service'
|
puts 'Reporting test loot to data service'
|
||||||
data_service = Metasploit::Framework::DataService::DataProxy.instance
|
|
||||||
start_time = Time.now
|
start_time = Time.now
|
||||||
puts "#{start_time} - Staring loot perf test"
|
puts "#{start_time} - Staring loot perf test"
|
||||||
loots.each do |loot|
|
loots.each do |loot|
|
||||||
data_service.report_loot loot
|
framework.db.report_loot loot
|
||||||
end
|
end
|
||||||
end_time = Time.now
|
end_time = Time.now
|
||||||
puts "#{end_time} - Ending loot perf test. Duration was #{end_time - start_time}"
|
puts "#{end_time} - Ending loot perf test. Duration was #{end_time - start_time}"
|
||||||
|
@ -284,9 +278,11 @@ module Msf
|
||||||
end
|
end
|
||||||
framework.db.workspace = workspace
|
framework.db.workspace = workspace
|
||||||
elsif deleting and names
|
elsif deleting and names
|
||||||
framework.db.delete_workspaces(names)
|
status_msg, error_msg = framework.db.delete_workspaces(names)
|
||||||
|
print_msgs(status_msg, error_msg)
|
||||||
elsif delete_all
|
elsif delete_all
|
||||||
framework.db.delete_all_workspaces()
|
status_msg, error_msg = framework.db.delete_all_workspaces()
|
||||||
|
print_msgs(status_msg, error_msg)
|
||||||
elsif renaming
|
elsif renaming
|
||||||
if names.length != 2
|
if names.length != 2
|
||||||
print_error("Wrong number of arguments to rename")
|
print_error("Wrong number of arguments to rename")
|
||||||
|
@ -2021,6 +2017,18 @@ module Msf
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def print_msgs(status_msg, error_msg)
|
||||||
|
status_msg.each do |s|
|
||||||
|
print_status(s)
|
||||||
|
end
|
||||||
|
|
||||||
|
error_msg.each do |e|
|
||||||
|
print_error(e)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end end end end
|
end end end end
|
||||||
|
|
|
@ -122,7 +122,6 @@ class Driver < Msf::Ui::Driver
|
||||||
enstack_dispatcher(dispatcher)
|
enstack_dispatcher(dispatcher)
|
||||||
end
|
end
|
||||||
|
|
||||||
framework.db.init(framework, opts)
|
|
||||||
if (framework.db.active)
|
if (framework.db.active)
|
||||||
require 'msf/ui/console/command_dispatcher/db'
|
require 'msf/ui/console/command_dispatcher/db'
|
||||||
enstack_dispatcher(CommandDispatcher::Db)
|
enstack_dispatcher(CommandDispatcher::Db)
|
||||||
|
|
|
@ -77,7 +77,7 @@ RSpec.describe Msf::Auxiliary::Cisco do
|
||||||
|
|
||||||
context '#cisco_ios_config_eater' do
|
context '#cisco_ios_config_eater' do
|
||||||
before(:example) do
|
before(:example) do
|
||||||
expect(aux_cisco).to receive(:myworkspace).and_return(workspace)
|
expect(aux_cisco).to receive(:myworkspace).at_least(:once).and_return(workspace)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'deals with udp ports' do
|
it 'deals with udp ports' do
|
||||||
|
|
|
@ -6,12 +6,12 @@ RSpec.shared_context 'Msf::DBManager' do
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:db_manager) do
|
let(:db_manager) do
|
||||||
framework.db
|
framework.db.get_data_service
|
||||||
end
|
end
|
||||||
|
|
||||||
before(:example) do
|
before(:example) do
|
||||||
# already connected due to use_transactional_fixtures, but need some of the side-effects of #connect
|
# already connected due to use_transactional_fixtures, but need some of the side-effects of #connect
|
||||||
framework.db.workspace = framework.db.default_workspace
|
db_manager.workspace = db_manager.default_workspace
|
||||||
allow(db_manager).to receive(:active).and_return(active)
|
allow(db_manager).to receive(:active).and_return(active)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,6 @@ RSpec.shared_examples_for 'Msf::DBManager::Host' do
|
||||||
it { is_expected.to respond_to :get_host }
|
it { is_expected.to respond_to :get_host }
|
||||||
it { is_expected.to respond_to :has_host? }
|
it { is_expected.to respond_to :has_host? }
|
||||||
it { is_expected.to respond_to :hosts }
|
it { is_expected.to respond_to :hosts }
|
||||||
it { is_expected.to respond_to :normalize_host }
|
|
||||||
it { is_expected.to respond_to :report_host }
|
it { is_expected.to respond_to :report_host }
|
||||||
it { is_expected.to respond_to :update_host_via_sysinfo }
|
it { is_expected.to respond_to :update_host_via_sysinfo }
|
||||||
end
|
end
|
|
@ -172,20 +172,6 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with workspace from either :workspace or session' do
|
context 'with workspace from either :workspace or session' do
|
||||||
it 'should pass normalized host from session as :host to #find_or_create_host' do
|
|
||||||
normalized_host = double('Normalized Host')
|
|
||||||
expect(db_manager).to receive(:normalize_host).with(session).and_return(normalized_host)
|
|
||||||
# stub report_vuln so its use of find_or_create_host and Msf::Util::Host.normalize_host doesn't interfere.
|
|
||||||
expect(db_manager).to receive(:report_vuln)
|
|
||||||
|
|
||||||
expect(db_manager).to receive(:find_or_create_host).with(
|
|
||||||
hash_including(
|
|
||||||
:host => normalized_host
|
|
||||||
)
|
|
||||||
).and_return(host)
|
|
||||||
|
|
||||||
report_session
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with session responds to arch' do
|
context 'with session responds to arch' do
|
||||||
let(:arch) do
|
let(:arch) do
|
||||||
|
@ -515,20 +501,6 @@ RSpec.shared_examples_for 'Msf::DBManager::Session' do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with workspace from either :workspace or session' do
|
context 'with workspace from either :workspace or session' do
|
||||||
it 'should pass normalized host from session as :host to #find_or_create_host' do
|
|
||||||
normalized_host = double('Normalized Host')
|
|
||||||
allow(db_manager).to receive(:normalize_host).with(session).and_return(normalized_host)
|
|
||||||
# stub report_vuln so its use of find_or_create_host and Msf::Util::Host.normalize_host doesn't interfere.
|
|
||||||
allow(db_manager).to receive(:report_vuln)
|
|
||||||
|
|
||||||
expect(db_manager).to receive(:find_or_create_host).with(
|
|
||||||
hash_including(
|
|
||||||
:host => normalized_host
|
|
||||||
)
|
|
||||||
).and_return(host)
|
|
||||||
|
|
||||||
report_session
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with session responds to arch' do
|
context 'with session responds to arch' do
|
||||||
let(:arch) do
|
let(:arch) do
|
||||||
|
|
Loading…
Reference in New Issue