Skip to content

Commit 8c51222

Browse files
authored
Land #1, db_nmap work with remote data service
Make db_nmap Work With Remote Data Service
2 parents e2a7ece + 770f092 commit 8c51222

File tree

9 files changed

+100
-42
lines changed

9 files changed

+100
-42
lines changed

lib/metasploit/framework/data_service/proxy/data_proxy_auto_loader.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module DataProxyAutoLoader
1515
autoload :LootDataProxy, 'metasploit/framework/data_service/proxy/loot_data_proxy'
1616
autoload :SessionEventDataProxy, 'metasploit/framework/data_service/proxy/session_event_data_proxy'
1717
autoload :CredentialDataProxy, 'metasploit/framework/data_service/proxy/credential_data_proxy'
18+
autoload :NmapDataProxy, 'metasploit/framework/data_service/proxy/nmap_data_proxy'
1819
include ServiceDataProxy
1920
include HostDataProxy
2021
include VulnDataProxy
@@ -27,4 +28,5 @@ module DataProxyAutoLoader
2728
include LootDataProxy
2829
include SessionEventDataProxy
2930
include CredentialDataProxy
31+
include NmapDataProxy
3032
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module NmapDataProxy
2+
3+
def import_nmap_xml_file(args = {})
4+
begin
5+
data_service = self.get_data_service()
6+
data_service.import_nmap_xml_file(args)
7+
rescue Exception => e
8+
puts "Call to #{data_service.class}#import_nmap_xml_file threw exception: #{e.message}"
9+
e.backtrace { |line| puts "#{line}\n"}
10+
end
11+
end
12+
end

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module DataServiceAutoLoader
1414
autoload :RemoteLootDataService, 'metasploit/framework/data_service/remote/http/remote_loot_data_service'
1515
autoload :RemoteSessionEventDataService, 'metasploit/framework/data_service/remote/http/remote_session_event_data_service'
1616
autoload :RemoteCredentialDataService, 'metasploit/framework/data_service/remote/http/remote_credential_data_service'
17+
autoload :RemoteNmapDataService, 'metasploit/framework/data_service/remote/http/remote_nmap_data_service'
1718
include RemoteHostDataService
1819
include RemoteEventDataService
1920
include RemoteNoteDataService
@@ -26,4 +27,5 @@ module DataServiceAutoLoader
2627
include RemoteLootDataService
2728
include RemoteSessionEventDataService
2829
include RemoteCredentialDataService
30+
include RemoteNmapDataService
2931
end

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,16 @@ module RemoteLootDataService
66
LOOT_PATH = '/api/1/msf/loot'
77

88
def loot(opts = {})
9-
json_to_open_struct_object(self.get_data(LOOT_PATH, opts), [])
9+
# TODO: Add an option to toggle whether the file data is returned or not
10+
loots = json_to_open_struct_object(self.get_data(LOOT_PATH, opts), [])
11+
# Save a local copy of the file
12+
loots.each do |loot|
13+
if loot.data
14+
local_path = File.join(Msf::Config.loot_directory, File.basename(loot.path))
15+
loot.path = process_file(loot.data, local_path)
16+
end
17+
end
18+
loots
1019
end
1120

1221
def report_loot(opts)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
require 'metasploit/framework/data_service/remote/http/response_data_helper'
2+
3+
module RemoteNmapDataService
4+
include ResponseDataHelper
5+
6+
NMAP_PATH = '/api/1/msf/nmap'
7+
8+
def import_nmap_xml_file(opts)
9+
filename = opts[:filename]
10+
data = ""
11+
File.open(filename, 'rb') do |f|
12+
data = f.read(f.stat.size)
13+
end
14+
15+
opts[:data] = Base64.urlsafe_encode64(data)
16+
17+
self.post_data(NMAP_PATH, opts)
18+
end
19+
end

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'ostruct'
2+
require 'digest'
23

34
#
45
# HTTP response helper class
@@ -23,6 +24,27 @@ def json_to_open_struct_object(response_wrapper, returns_on_error = nil)
2324
return returns_on_error
2425
end
2526

27+
# Processes a Base64 encoded file included in a JSON request.
28+
# Saves the file in the location specified in the parameter.
29+
#
30+
# @param base64_file [String] The Base64 encoded file.
31+
# @param save_path [String] The location to store the file. This should include the file's name.
32+
# @return [String] The location where the file was successfully stored.
33+
def process_file(base64_file, save_path)
34+
decoded_file = Base64.urlsafe_decode64(base64_file)
35+
begin
36+
# If we are running the data service on the same box this will ensure we only write
37+
# the file if it is somehow not there already.
38+
unless File.exists?(save_path) && File.read(save_path) == decoded_file
39+
File.open(save_path, 'w+') { |file| file.write(decoded_file) }
40+
end
41+
rescue Exception => e
42+
puts "There was an error writing the file: #{e}"
43+
e.backtrace.each { |line| puts "#{line}\n"}
44+
end
45+
save_path
46+
end
47+
2648
#
2749
# Converts a hash to an open struct
2850
#

lib/msf/core/db_manager/http/servlet/loot_servlet.rb

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,48 +27,11 @@ def self.get_loot
2727

2828
def self.report_loot
2929
lambda {
30-
3130
job = lambda { |opts|
32-
# This regex does a best attempt to determine if opts[:data] is valid Base64
33-
# See https://stackoverflow.com/questions/8571501/how-to-check-whether-the-string-is-base64-encoded-or-not
34-
if opts[:data] =~ /^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/
35-
opts[:data] = Base64.urlsafe_decode64(opts[:data]) if opts[:data]
36-
end
37-
38-
# This code is all for writing out the file locally.
39-
# It is copied from lib/msf/core/auxiliary/report.rb
40-
# We shouldn't duplicate it so a better method should be used
41-
if ! ::File.directory?(Msf::Config.loot_directory)
42-
FileUtils.mkdir_p(Msf::Config.loot_directory)
43-
end
44-
45-
ext = 'bin'
46-
if opts[:name]
47-
parts = opts[:name].to_s.split('.')
48-
if parts.length > 1 and parts[-1].length < 4
49-
ext = parts[-1]
50-
end
51-
end
52-
53-
case opts[:content_type]
54-
when /^text\/[\w\.]+$/
55-
ext = "txt"
56-
end
57-
# This method is available even if there is no database, don't bother checking
58-
host = Msf::Util::Host.normalize_host(opts[:host])
59-
60-
ws = (opts[:workspace] ? opts[:workspace] : 'default')
61-
name =
62-
Time.now.strftime("%Y%m%d%H%M%S") + "_" + ws + "_" +
63-
(host || 'unknown') + '_' + opts[:type][0,16] + '_' +
64-
Rex::Text.rand_text_numeric(6) + '.' + ext
65-
66-
name.gsub!(/[^a-z0-9\.\_]+/i, '')
67-
68-
path = File.join(Msf::Config.loot_directory, name)
69-
full_path = ::File.expand_path(path)
70-
File.open(full_path, "wb") do |fd|
71-
fd.write(opts[:data])
31+
if opts[:data]
32+
filename = File.basename(opts[:path])
33+
local_path = File.join(Msf::Config.loot_directory, filename)
34+
opts[:path] = process_file(opts[:data], local_path)
7235
end
7336

7437
get_db().report_loot(opts)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
module NmapServlet
2+
3+
def self.api_path
4+
'/api/1/msf/nmap'
5+
end
6+
7+
def self.registered(app)
8+
app.post NmapServlet.api_path, &import_nmap_xml_file
9+
end
10+
11+
#######
12+
private
13+
#######
14+
15+
def self.import_nmap_xml_file
16+
lambda {
17+
18+
job = lambda { |opts|
19+
nmap_file = File.basename(opts[:filename])
20+
nmap_file_path = File.join(Msf::Config.local_directory, nmap_file)
21+
opts[:filename] = process_file(opts[:data], nmap_file_path)
22+
get_db().import_nmap_xml_file(opts)
23+
}
24+
exec_report_job(request, &job)
25+
}
26+
end
27+
end

lib/msf/core/db_manager/http/sinatra_app.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
require 'msf/core/db_manager/http/servlet/loot_servlet'
1515
require 'msf/core/db_manager/http/servlet/session_event_servlet'
1616
require 'msf/core/db_manager/http/servlet/credential_servlet'
17+
require 'msf/core/db_manager/http/servlet/nmap_servlet'
1718

1819
class SinatraApp < Sinatra::Base
1920

@@ -35,4 +36,5 @@ class SinatraApp < Sinatra::Base
3536
register LootServlet
3637
register SessionEventServlet
3738
register CredentialServlet
39+
register NmapServlet
3840
end

0 commit comments

Comments
 (0)