From 810165522fbac9c432282fa4bf77e01362e27e25 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Thu, 10 Jan 2019 12:45:28 -0600 Subject: [PATCH 01/14] WIP: update loot api docs --- documentation/api/v1/loot_api_doc.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/documentation/api/v1/loot_api_doc.rb b/documentation/api/v1/loot_api_doc.rb index 9fd6737052..1d8e1f5e8b 100644 --- a/documentation/api/v1/loot_api_doc.rb +++ b/documentation/api/v1/loot_api_doc.rb @@ -18,6 +18,11 @@ module LootApiDoc INFO_DESC = 'Information about the loot.' MODULE_RUN_ID_DESC = 'The ID of the module run record this loot is associated with.' + # Some of the attributes expect different data when doing a create. + CREATE_DATA_DESC = "Base64 encoded copy of the file's contents." + CREATE_DATA_EXAMPLE = 'dGhpcyBpcyB0aGUgZmlsZSdzIGNvbnRlbnRz' + CREATE_PATH_DESC = 'The name to give the file on the server.' + CREATE_PATH_EXAMPLE = 'password_file.txt' # Swagger documentation for loot model swagger_schema :Loot do @@ -87,8 +92,8 @@ module LootApiDoc property :host, type: :string, format: :ipv4, description: HOST_DESC, example: RootApiDoc::HOST_EXAMPLE property :service, '$ref': :Service property :ltype, type: :string, description: LTYPE_DESC, example: LTYPE_EXAMPLE, required: true - property :path, type: :string, description: PATH_DESC, example: PATH_EXAMPLE, required: true - property :data, type: :string, description: DATA_DESC + property :path, type: :string, description: CREATE_PATH_DESC, example: CREATE_PATH_EXAMPLE, required: true + property :data, type: :string, description: CREATE_DATA_DESC, example: CREATE_DATA_EXAMPLE property :ctype, type: :string, description: CONTENT_TYPE_DESC, example: CONTENT_TYPE_EXAMPLE property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE, required: true property :info, type: :string, description: INFO_DESC From e16845886119302770025f9d31aeb652134961a8 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Mon, 14 Jan 2019 15:51:51 -0600 Subject: [PATCH 02/14] Make calls to get the associated host when getting loot --- .../data_service/remote/http/remote_loot_data_service.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb b/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb index 313f1b6919..1ebad17fc1 100644 --- a/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb +++ b/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb @@ -16,6 +16,11 @@ module RemoteLootDataService local_path = File.join(Msf::Config.loot_directory, File.basename(loot.path)) loot.path = process_file(loot.data, local_path) end + if loot.host_id + host_opts = { id: loot.host_id } + host_path = get_path_select(host_opts, RemoteHostDataService::HOST_API_PATH) + loot.host = json_to_mdm_object(self.get_data(host_path, nil, host_opts), RemoteHostDataService::HOST_MDM_CLASS).first + end end loots end From 42c9553283bae882a08e6c49ef62bf89382613e6 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Tue, 15 Jan 2019 12:47:37 -0600 Subject: [PATCH 03/14] Dont do a separate lookup for loot.host, use the included JSON This is just a temporary change. Eventually we should be doing separate lookups for associated objects as that is the RESTful way of doing it. Implementing this now to prevent extra load on the server until we can put a better system in place of doing multiple lookups with a single call. --- .../remote/http/remote_loot_data_service.rb | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb b/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb index 1ebad17fc1..37ba6a6b8c 100644 --- a/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb +++ b/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb @@ -8,21 +8,22 @@ module RemoteLootDataService def loot(opts = {}) path = get_path_select(opts, LOOT_API_PATH) - # TODO: Add an option to toggle whether the file data is returned or not - loots = json_to_mdm_object(self.get_data(path, nil, opts), LOOT_MDM_CLASS, []) - # Save a local copy of the file - loots.each do |loot| - if loot.data - local_path = File.join(Msf::Config.loot_directory, File.basename(loot.path)) - loot.path = process_file(loot.data, local_path) + data = self.get_data(path, nil, opts) + rv = json_to_mdm_object(data, LOOT_MDM_CLASS, []) + parsed_body = JSON.parse(data.response.body, symbolize_names: true) + data = parsed_body[:data] + data.each do |loot| + # TODO: Add an option to toggle whether the file data is returned or not + if loot[:data] + local_path = File.join(Msf::Config.loot_directory, File.basename(loot[:path])) + rv[data.index(loot)].path = process_file(loot[:data], local_path) end - if loot.host_id - host_opts = { id: loot.host_id } - host_path = get_path_select(host_opts, RemoteHostDataService::HOST_API_PATH) - loot.host = json_to_mdm_object(self.get_data(host_path, nil, host_opts), RemoteHostDataService::HOST_MDM_CLASS).first + if loot[:host] + host_object = to_ar(RemoteHostDataService::HOST_MDM_CLASS.constantize, loot[:host]) + rv[data.index(loot)].host = host_object end end - loots + rv end def report_loot(opts) From 5c308b14481f4edb9d35bf5eff44f7c9df403250 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Tue, 15 Jan 2019 16:45:04 -0600 Subject: [PATCH 04/14] Remove nested loot object from host JSON The code on the framework side that was utilizing this was removed a while ago. It was never actually being used anywhere, and was causing issues with getting host objects back when the loot contained non-UTF-8 characters --- lib/msf/core/web_services/servlet/host_servlet.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/msf/core/web_services/servlet/host_servlet.rb b/lib/msf/core/web_services/servlet/host_servlet.rb index f090ac1666..6aeb92c16d 100644 --- a/lib/msf/core/web_services/servlet/host_servlet.rb +++ b/lib/msf/core/web_services/servlet/host_servlet.rb @@ -30,9 +30,8 @@ module HostServlet begin sanitized_params = sanitize_params(params, env['rack.request.query_hash']) data = get_db.hosts(sanitized_params) - includes = [:loots] data = data.first if is_single_object?(data, sanitized_params) - set_json_data_response(response: data, includes: includes) + set_json_data_response(response: data) rescue => e print_error_and_create_response(error: e, message: 'There was an error retrieving hosts:', code: 500) end From dc7d611780ee08591efe96a27d8a50edeeb065b9 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Tue, 15 Jan 2019 18:01:43 -0600 Subject: [PATCH 05/14] Base64 encode the data field for each loot operation --- documentation/api/v1/loot_api_doc.rb | 9 ++++----- lib/msf/core/web_services/servlet/loot_servlet.rb | 12 ++++++++---- lib/msf/core/web_services/servlet_helper.rb | 7 +++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/documentation/api/v1/loot_api_doc.rb b/documentation/api/v1/loot_api_doc.rb index 1d8e1f5e8b..185598f0f7 100644 --- a/documentation/api/v1/loot_api_doc.rb +++ b/documentation/api/v1/loot_api_doc.rb @@ -10,7 +10,8 @@ module LootApiDoc LTYPE_EXAMPLE = "'file', 'image', 'config_file', etc." PATH_DESC = 'The on-disk path to the loot file.' PATH_EXAMPLE = '/path/to/file.txt' - DATA_DESC = 'The contents of the file.' + DATA_DESC = "Base64 encoded copy of the file's contents." + DATA_EXAMPLE = 'dGhpcyBpcyB0aGUgZmlsZSdzIGNvbnRlbnRz' CONTENT_TYPE_DESC = 'The mime/content type of the file at {#path}. Used to server the file correctly so browsers understand whether to render or download the file.' CONTENT_TYPE_EXAMPLE = 'text/plain' NAME_DESC = 'The name of the loot.' @@ -19,8 +20,6 @@ module LootApiDoc MODULE_RUN_ID_DESC = 'The ID of the module run record this loot is associated with.' # Some of the attributes expect different data when doing a create. - CREATE_DATA_DESC = "Base64 encoded copy of the file's contents." - CREATE_DATA_EXAMPLE = 'dGhpcyBpcyB0aGUgZmlsZSdzIGNvbnRlbnRz' CREATE_PATH_DESC = 'The name to give the file on the server.' CREATE_PATH_EXAMPLE = 'password_file.txt' @@ -33,7 +32,7 @@ module LootApiDoc property :service_id, type: :integer, format: :int32, description: SERVICE_ID_DESC property :ltype, type: :string, description: LTYPE_DESC, example: LTYPE_EXAMPLE property :path, type: :string, description: PATH_DESC, example: PATH_EXAMPLE - property :data, type: :string, description: DATA_DESC + property :data, type: :string, description: DATA_DESC, example: DATA_EXAMPLE property :content_type, type: :string, description: CONTENT_TYPE_DESC, example: CONTENT_TYPE_EXAMPLE property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE property :info, type: :string, description: INFO_DESC @@ -93,7 +92,7 @@ module LootApiDoc property :service, '$ref': :Service property :ltype, type: :string, description: LTYPE_DESC, example: LTYPE_EXAMPLE, required: true property :path, type: :string, description: CREATE_PATH_DESC, example: CREATE_PATH_EXAMPLE, required: true - property :data, type: :string, description: CREATE_DATA_DESC, example: CREATE_DATA_EXAMPLE + property :data, type: :string, description: DATA_DESC, example: DATA_EXAMPLE property :ctype, type: :string, description: CONTENT_TYPE_DESC, example: CONTENT_TYPE_EXAMPLE property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE, required: true property :info, type: :string, description: INFO_DESC diff --git a/lib/msf/core/web_services/servlet/loot_servlet.rb b/lib/msf/core/web_services/servlet/loot_servlet.rb index f71e884f26..ae80e74fbe 100644 --- a/lib/msf/core/web_services/servlet/loot_servlet.rb +++ b/lib/msf/core/web_services/servlet/loot_servlet.rb @@ -26,9 +26,7 @@ module LootServlet sanitized_params = sanitize_params(params, env['rack.request.query_hash']) data = get_db.loots(sanitized_params) includes = [:host] - data.each do |loot| - loot.data = Base64.urlsafe_encode64(loot.data) if loot.data - end + data = encode_loot_data(data) data = data.first if is_single_object?(data, sanitized_params) set_json_data_response(response: data, includes: includes) rescue => e @@ -48,7 +46,8 @@ module LootServlet opts[:data] = Base64.urlsafe_decode64(opts[:data]) end - get_db.report_loot(opts) + data = get_db.report_loot(opts) + encode_loot_data(data) } exec_report_job(request, &job) } @@ -62,6 +61,7 @@ module LootServlet tmp_params = sanitize_params(params) opts[:id] = tmp_params[:id] if tmp_params[:id] data = get_db.update_loot(opts) + data = encode_loot_data(data) set_json_data_response(response: data) rescue => e print_error_and_create_response(error: e, message: 'There was an error updating the loot:', code: 500) @@ -75,6 +75,10 @@ module LootServlet begin opts = parse_json_request(request, false) data = get_db.delete_loot(opts) + # The rails delete operation returns a frozen object. We need to Base64 encode the data + # before converting to JSON. So we'll work with a duplicate of the original if it is frozen. + data.map! { |loot| loot.dup if loot.frozen? } + data = encode_loot_data(data) set_json_data_response(response: data) rescue => e print_error_and_create_response(error: e, message: 'There was an error deleting the loot:', code: 500) diff --git a/lib/msf/core/web_services/servlet_helper.rb b/lib/msf/core/web_services/servlet_helper.rb index 1a5173e07d..ecfb37df5b 100644 --- a/lib/msf/core/web_services/servlet_helper.rb +++ b/lib/msf/core/web_services/servlet_helper.rb @@ -131,6 +131,13 @@ module ServletHelper response end + def encode_loot_data(data) + Array.wrap(data).each do |loot| + loot.data = Base64.urlsafe_encode64(loot.data) if loot.data + end + data + end + # Get Warden::Proxy object from the Rack environment. # @return [Warden::Proxy] The Warden::Proxy object from the Rack environment. def warden From d6462fed63ee30b7eb291182c23ab3520dc3bcf4 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Wed, 16 Jan 2019 10:01:22 -0600 Subject: [PATCH 06/14] Dont allow users to update loot.data --- lib/msf/core/db_manager/loot.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/msf/core/db_manager/loot.rb b/lib/msf/core/db_manager/loot.rb index e0cb0b983d..58211fcaad 100644 --- a/lib/msf/core/db_manager/loot.rb +++ b/lib/msf/core/db_manager/loot.rb @@ -99,6 +99,7 @@ module Msf::DBManager::Loot def update_loot(opts) ::ActiveRecord::Base.connection_pool.with_connection { wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework, false) + raise ArgumentError, "Updating the data attribute is not allowed." if opts[:data] opts[:workspace] = wspace if wspace id = opts.delete(:id) From ffada8147eb434075f34cd8dd7cd08224a4c384c Mon Sep 17 00:00:00 2001 From: James Barnett Date: Wed, 16 Jan 2019 10:42:00 -0600 Subject: [PATCH 07/14] Update loot API docs --- documentation/api/v1/loot_api_doc.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/documentation/api/v1/loot_api_doc.rb b/documentation/api/v1/loot_api_doc.rb index 185598f0f7..4c9d528e3b 100644 --- a/documentation/api/v1/loot_api_doc.rb +++ b/documentation/api/v1/loot_api_doc.rb @@ -210,7 +210,14 @@ module LootApiDoc key :description, 'The updated attributes to overwrite to the loot.' key :required, true schema do - key :'$ref', :Loot + property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_DESC, example: RootApiDoc::WORKSPACE_POST_EXAMPLE + property :host_id, type: :integer, format: :int32, description: HOST_ID_DESC + property :service_id, type: :integer, format: :int32, description: SERVICE_ID_DESC + property :ltype, type: :string, description: LTYPE_DESC, example: LTYPE_EXAMPLE, required: true + property :path, type: :string, description: CREATE_PATH_DESC, example: CREATE_PATH_EXAMPLE, required: true + property :ctype, type: :string, description: CONTENT_TYPE_DESC, example: CONTENT_TYPE_EXAMPLE + property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE, required: true + property :info, type: :string, description: INFO_DESC end end From 705c269d27aebba7044259b79bf8f2c771d0d5d4 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Wed, 16 Jan 2019 10:59:07 -0600 Subject: [PATCH 08/14] Handle empty data values for loot --- lib/msf/core/web_services/servlet_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/web_services/servlet_helper.rb b/lib/msf/core/web_services/servlet_helper.rb index ecfb37df5b..59f5f71c5d 100644 --- a/lib/msf/core/web_services/servlet_helper.rb +++ b/lib/msf/core/web_services/servlet_helper.rb @@ -133,7 +133,7 @@ module ServletHelper def encode_loot_data(data) Array.wrap(data).each do |loot| - loot.data = Base64.urlsafe_encode64(loot.data) if loot.data + loot.data = Base64.urlsafe_encode64(loot.data) if loot.data && !loot.data.empty? end data end From fd6527bac84e21ae912cda7f83d23598c300fa5c Mon Sep 17 00:00:00 2001 From: James Barnett Date: Wed, 16 Jan 2019 15:20:41 -0600 Subject: [PATCH 09/14] Prepend loot filenames with unique string This should help prevent accidentally overwriting files with the same name --- documentation/api/v1/loot_api_doc.rb | 2 +- lib/msf/core/db_manager/loot.rb | 2 +- lib/msf/core/web_services/servlet/loot_servlet.rb | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/documentation/api/v1/loot_api_doc.rb b/documentation/api/v1/loot_api_doc.rb index 4c9d528e3b..efb7bcb864 100644 --- a/documentation/api/v1/loot_api_doc.rb +++ b/documentation/api/v1/loot_api_doc.rb @@ -20,7 +20,7 @@ module LootApiDoc MODULE_RUN_ID_DESC = 'The ID of the module run record this loot is associated with.' # Some of the attributes expect different data when doing a create. - CREATE_PATH_DESC = 'The name to give the file on the server.' + CREATE_PATH_DESC = 'The name to give the file on the server. Note that this will prepend a unique string to the given value.' CREATE_PATH_EXAMPLE = 'password_file.txt' # Swagger documentation for loot model diff --git a/lib/msf/core/db_manager/loot.rb b/lib/msf/core/db_manager/loot.rb index 58211fcaad..80d59b3c2f 100644 --- a/lib/msf/core/db_manager/loot.rb +++ b/lib/msf/core/db_manager/loot.rb @@ -99,7 +99,7 @@ module Msf::DBManager::Loot def update_loot(opts) ::ActiveRecord::Base.connection_pool.with_connection { wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework, false) - raise ArgumentError, "Updating the data attribute is not allowed." if opts[:data] + raise ArgumentError, "Updating the data attribute is not permitted." if opts[:data] opts[:workspace] = wspace if wspace id = opts.delete(:id) diff --git a/lib/msf/core/web_services/servlet/loot_servlet.rb b/lib/msf/core/web_services/servlet/loot_servlet.rb index ae80e74fbe..0472b75455 100644 --- a/lib/msf/core/web_services/servlet/loot_servlet.rb +++ b/lib/msf/core/web_services/servlet/loot_servlet.rb @@ -41,7 +41,7 @@ module LootServlet job = lambda { |opts| if opts[:data] filename = File.basename(opts[:path]) - local_path = File.join(Msf::Config.loot_directory, filename) + local_path = File.join(Msf::Config.loot_directory, "#{SecureRandom.hex(10)}-#{filename}") opts[:path] = process_file(opts[:data], local_path) opts[:data] = Base64.urlsafe_decode64(opts[:data]) end @@ -60,6 +60,10 @@ module LootServlet opts = parse_json_request(request, false) tmp_params = sanitize_params(params) opts[:id] = tmp_params[:id] if tmp_params[:id] + if opts[:path] + filename = File.basename(opts[:path]) + opts[:path] = File.join(Msf::Config.loot_directory, "#{SecureRandom.hex(10)}-#{filename}") + end data = get_db.update_loot(opts) data = encode_loot_data(data) set_json_data_response(response: data) From 4b87d544306c9c7730ec01abef46f66a54b1a818 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Wed, 16 Jan 2019 15:29:27 -0600 Subject: [PATCH 10/14] Add comment explaining why we prevent loot.data update --- lib/msf/core/db_manager/loot.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/msf/core/db_manager/loot.rb b/lib/msf/core/db_manager/loot.rb index 80d59b3c2f..21be477303 100644 --- a/lib/msf/core/db_manager/loot.rb +++ b/lib/msf/core/db_manager/loot.rb @@ -99,6 +99,7 @@ module Msf::DBManager::Loot def update_loot(opts) ::ActiveRecord::Base.connection_pool.with_connection { wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework, false) + # Prevent changing the data field to ensure the file contents remain the same as what was originally looted. raise ArgumentError, "Updating the data attribute is not permitted." if opts[:data] opts[:workspace] = wspace if wspace From 9e3a39bcf93a2cc9edac5b63b5b5673f36836d67 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Fri, 25 Jan 2019 12:34:52 -0600 Subject: [PATCH 11/14] Dont try to process empty loot --- .../data_service/remote/http/remote_loot_data_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb b/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb index 37ba6a6b8c..4df2da69ea 100644 --- a/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb +++ b/lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb @@ -14,7 +14,7 @@ module RemoteLootDataService data = parsed_body[:data] data.each do |loot| # TODO: Add an option to toggle whether the file data is returned or not - if loot[:data] + if loot[:data] && !loot[:data].empty? local_path = File.join(Msf::Config.loot_directory, File.basename(loot[:path])) rv[data.index(loot)].path = process_file(loot[:data], local_path) end From 27a0cbfcabadcf761b9c91b2a54968c62099ea70 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Fri, 25 Jan 2019 13:34:14 -0600 Subject: [PATCH 12/14] Rename file on disk when updating path attribute --- lib/msf/core/db_manager/loot.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/msf/core/db_manager/loot.rb b/lib/msf/core/db_manager/loot.rb index 21be477303..b3fec2b7ed 100644 --- a/lib/msf/core/db_manager/loot.rb +++ b/lib/msf/core/db_manager/loot.rb @@ -105,6 +105,13 @@ module Msf::DBManager::Loot id = opts.delete(:id) loot = Mdm::Loot.find(id) + + # If the user updates the path attribute (or filename) we need to update the file + # on disk to reflect that. + if opts[:path] && File.exists?(loot.path) + File.rename(loot.path, opts[:path]) + end + loot.update!(opts) return loot } From b98133cded9b4497f8d2bb4e637ae798dab13f50 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Fri, 25 Jan 2019 16:36:17 -0600 Subject: [PATCH 13/14] Dont assign unique file name when theres no file on disk --- lib/msf/core/db_manager/loot.rb | 12 ++++++------ lib/msf/core/web_services/servlet/loot_servlet.rb | 6 +++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/msf/core/db_manager/loot.rb b/lib/msf/core/db_manager/loot.rb index b3fec2b7ed..00ff3aabfc 100644 --- a/lib/msf/core/db_manager/loot.rb +++ b/lib/msf/core/db_manager/loot.rb @@ -10,18 +10,18 @@ module Msf::DBManager::Loot # This methods returns a list of all loot in the database # def loots(opts) - data = opts.delete(:data) - # Remove path from search conditions as this won't accommodate remote data - # service usage where the client and server storage locations differ. - opts.delete(:path) - search_term = opts.delete(:search_term) - ::ActiveRecord::Base.connection_pool.with_connection { # If we have the ID, there is no point in creating a complex query. if opts[:id] && !opts[:id].to_s.empty? return Array.wrap(Mdm::Loot.find(opts[:id])) end + # Remove path from search conditions as this won't accommodate remote data + # service usage where the client and server storage locations differ. + opts.delete(:path) + search_term = opts.delete(:search_term) + data = opts.delete(:data) + wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) opts[:workspace_id] = wspace.id diff --git a/lib/msf/core/web_services/servlet/loot_servlet.rb b/lib/msf/core/web_services/servlet/loot_servlet.rb index 0472b75455..e0b20457c8 100644 --- a/lib/msf/core/web_services/servlet/loot_servlet.rb +++ b/lib/msf/core/web_services/servlet/loot_servlet.rb @@ -60,7 +60,11 @@ module LootServlet opts = parse_json_request(request, false) tmp_params = sanitize_params(params) opts[:id] = tmp_params[:id] if tmp_params[:id] - if opts[:path] + db_record = get_db.loots(opts).first + # Give the file a unique name to prevent accidental overwrites. Only do this if there is actually a file + # on disk. If there is not a file on disk we assume that this DB record is for tracking a file outside + # of metasploit, so we don't want to assign them a unique file name and overwrite that. + if opts[:path] && File.exists?(db_record.path) filename = File.basename(opts[:path]) opts[:path] = File.join(Msf::Config.loot_directory, "#{SecureRandom.hex(10)}-#{filename}") end From e55f459b87eebf08e49f8bf43490da28b3dea956 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Fri, 25 Jan 2019 16:55:01 -0600 Subject: [PATCH 14/14] Update loot docs to clear up path re-write functionality --- documentation/api/v1/loot_api_doc.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/api/v1/loot_api_doc.rb b/documentation/api/v1/loot_api_doc.rb index efb7bcb864..cb90a5d7c9 100644 --- a/documentation/api/v1/loot_api_doc.rb +++ b/documentation/api/v1/loot_api_doc.rb @@ -20,7 +20,7 @@ module LootApiDoc MODULE_RUN_ID_DESC = 'The ID of the module run record this loot is associated with.' # Some of the attributes expect different data when doing a create. - CREATE_PATH_DESC = 'The name to give the file on the server. Note that this will prepend a unique string to the given value.' + CREATE_PATH_DESC = 'The name to give the file on the server. All files are stored in a server configured path, so a full path is not needed. If there is a corresponding file on disk, the given value will be prepended with a unique string to prevent accidental overwrites of other files.' CREATE_PATH_EXAMPLE = 'password_file.txt' # Swagger documentation for loot model