Skip to content

Commit b86e481

Browse files
committed
Land #14, complete remote loot CRUD operations
2 parents aeee3bb + c50b8b5 commit b86e481

File tree

7 files changed

+176
-80
lines changed

7 files changed

+176
-80
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,32 @@ def report_loot(opts)
1212
end
1313
end
1414

15+
# TODO: Shouldn't this proxy to RemoteLootDataService#find_or_create_loot ?
16+
# It's currently skipping the "find" part
17+
def find_or_create_loot(opts)
18+
report_loot(opts)
19+
end
20+
1521
def loots(wspace, opts = {})
1622
begin
1723
data_service = self.get_data_service
1824
opts[:wspace] = wspace
1925
data_service.loot(opts)
2026
rescue Exception => e
2127
elog "Problem retrieving loot: #{e.message}"
28+
e.backtrace.each { |line| elog "#{line}\n" }
2229
end
2330
end
2431

2532
alias_method :loot, :loots
33+
34+
def update_loot(opts)
35+
begin
36+
data_service = self.get_data_service
37+
data_service.update_loot(opts)
38+
rescue Exception => e
39+
elog "Problem updating loot: #{e.message}"
40+
e.backtrace.each { |line| elog "#{line}\n" }
41+
end
42+
end
2643
end

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module RemoteLootDataService
88

99
def loot(opts = {})
1010
# TODO: Add an option to toggle whether the file data is returned or not
11-
loots = json_to_mdm_object(self.get_data(LOOT_API_PATH, opts), LOOT_MDM_CLASS, [])
11+
loots = json_to_mdm_object(self.get_data(LOOT_API_PATH, nil, opts), LOOT_MDM_CLASS, [])
1212
# Save a local copy of the file
1313
loots.each do |loot|
1414
if loot.data
@@ -30,4 +30,17 @@ def find_or_create_loot(opts)
3030
def report_loots(loot)
3131
self.post_data(LOOT_API_PATH, loot)
3232
end
33+
34+
def update_loot(opts)
35+
path = LOOT_API_PATH
36+
if opts && opts[:id]
37+
id = opts.delete(:id)
38+
path = "#{LOOT_API_PATH}/#{id}"
39+
end
40+
json_to_mdm_object(self.put_data(path, opts), LOOT_MDM_CLASS, [])
41+
end
42+
43+
def delete_loot(opts)
44+
json_to_mdm_object(self.delete_data(LOOT_API_PATH, opts), LOOT_MDM_CLASS, [])
45+
end
3346
end

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def json_to_mdm_object(response_wrapper, mdm_class, returns_on_error = nil)
4545
end
4646
rescue Exception => e
4747
elog "Mdm Object conversion failed #{e.message}"
48+
e.backtrace.each { |line| elog "#{line}\n" }
4849
end
4950
end
5051

@@ -66,7 +67,8 @@ def process_file(base64_file, save_path)
6667
File.open(save_path, 'w+') { |file| file.write(decoded_file) }
6768
end
6869
rescue Exception => e
69-
elog "There was an error writing the file: #{e.message}"
70+
elog "There was an error writing the file: #{e}"
71+
e.backtrace.each { |line| elog "#{line}\n"}
7072
end
7173
save_path
7274
end

lib/msf/core/db_manager/host.rb

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,17 @@ def del_host(wspace, address, comm='')
99
}
1010
end
1111

12+
# Deletes Host entries based on the IDs passed in.
13+
#
14+
# @param opts[:ids] [Array] Array containing Integers corresponding to the IDs of the Loot entries to delete.
15+
# @return [Array] Array containing the Mdm::Loot objects that were successfully deleted.
1216
def delete_host(opts)
13-
wspace = opts[:workspace] || opts[:wspace] || workspace
14-
if wspace.is_a? String
15-
wspace = find_workspace(wspace)
16-
end
17+
raise ArgumentError.new("The following options are required: :ids") if opts[:ids].nil?
1718

1819
::ActiveRecord::Base.connection_pool.with_connection {
19-
hosts = []
20-
if opts[:address] || opts[:host]
21-
opt_addr = opts[:address] || opts[:host]
22-
host = wspace.hosts.find_by_address(opt_addr)
23-
return { error: { message: "Unable to find host by specified address" } } if host.nil? || host.class != ::Mdm::Host
24-
hosts << host
25-
elsif opts[:addresses]
26-
return { error: { message: "Unable to find host by specified addresses" } } if opts[:addresses].class != Array
27-
hosts = wspace.hosts.where(address: opts[:addresses])
28-
return { error: { message: "Unable to find hosts for specified addresses" } } if hosts.nil?
29-
end
30-
3120
deleted = []
32-
hosts.each do |host|
21+
opts[:ids].each do |host_id|
22+
host = Mdm::Host.find(host_id)
3323
begin
3424
deleted << host.destroy
3525
rescue # refs suck

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

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,15 @@ def self.api_path
44
'/api/v1/loots'
55
end
66

7+
def self.api_path_with_id
8+
"#{LootServlet.api_path}/?:id?"
9+
end
10+
711
def self.registered(app)
812
app.get LootServlet.api_path, &get_loot
913
app.post LootServlet.api_path, &report_loot
14+
app.put LootServlet.api_path_with_id, &update_loot
15+
app.delete LootServlet.api_path, &delete_loot
1016
end
1117

1218
#######
@@ -17,8 +23,12 @@ def self.get_loot
1723
lambda {
1824
begin
1925
opts = parse_json_request(request, false)
20-
data = get_db().loots(opts)
21-
set_json_response(data)
26+
data = get_db().loots(params.symbolize_keys)
27+
includes = [:host]
28+
data.each do |loot|
29+
loot.data = Base64.urlsafe_encode64(loot.data) if loot.data
30+
end
31+
set_json_response(data, includes)
2232
rescue Exception => e
2333
set_error_on_response(e)
2434
end
@@ -32,11 +42,38 @@ def self.report_loot
3242
filename = File.basename(opts[:path])
3343
local_path = File.join(Msf::Config.loot_directory, filename)
3444
opts[:path] = process_file(opts[:data], local_path)
45+
opts[:data] = Base64.urlsafe_decode64(opts[:data])
3546
end
3647

3748
get_db().report_loot(opts)
3849
}
3950
exec_report_job(request, &job)
4051
}
4152
end
53+
54+
def self.update_loot
55+
lambda {
56+
begin
57+
opts = parse_json_request(request, false)
58+
tmp_params = params.symbolize_keys
59+
opts[:id] = tmp_params[:id] if tmp_params[:id]
60+
data = get_db().update_loot(opts)
61+
set_json_response(data)
62+
rescue Exception => e
63+
set_error_on_response(e)
64+
end
65+
}
66+
end
67+
68+
def self.delete_loot
69+
lambda {
70+
begin
71+
opts = parse_json_request(request, false)
72+
data = get_db().delete_loot(opts)
73+
set_json_response(data)
74+
rescue Exception => e
75+
set_error_on_response(e)
76+
end
77+
}
78+
end
4279
end

lib/msf/core/db_manager/loot.rb

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,12 @@ def loots(opts)
2929
if wspace.kind_of? String
3030
wspace = find_workspace(wspace)
3131
end
32+
opts[:workspace_id] = wspace.id
3233

3334
::ActiveRecord::Base.connection_pool.with_connection {
34-
wspace.loots.where(opts)
35+
search_term = opts.delete(:search_term)
36+
column_search_conditions = Msf::Util::DBManager.create_all_column_search_conditions(Mdm::Loot, search_term)
37+
Mdm::Loot.includes(:host).where(opts).where(column_search_conditions)
3538
}
3639
end
3740
alias_method :loot, :loots
@@ -87,4 +90,45 @@ def report_loot(opts)
8790
ret[:loot] = loot
8891
}
8992
end
93+
94+
# Update the attributes of a Loot entry with the values in opts.
95+
# The values in opts should match the attributes to update.
96+
#
97+
# @param opts [Hash] Hash containing the updated values. Key should match the attribute to update. Must contain :id of record to update.
98+
# @return [Mdm::Loot] The updated Mdm::Loot object.
99+
def update_loot(opts)
100+
wspace = opts.delete(:workspace)
101+
if wspace.kind_of? String
102+
wspace = find_workspace(wspace)
103+
opts[:workspace] = wspace
104+
end
105+
106+
::ActiveRecord::Base.connection_pool.with_connection {
107+
id = opts.delete(:id)
108+
Mdm::Loot.update(id, opts)
109+
}
110+
end
111+
112+
# Deletes Loot entries based on the IDs passed in.
113+
#
114+
# @param opts[:ids] [Array] Array containing Integers corresponding to the IDs of the Loot entries to delete.
115+
# @return [Array] Array containing the Mdm::Loot objects that were successfully deleted.
116+
def delete_loot(opts)
117+
raise ArgumentError.new("The following options are required: :ids") if opts[:ids].nil?
118+
119+
::ActiveRecord::Base.connection_pool.with_connection {
120+
deleted = []
121+
opts[:ids].each do |loot_id|
122+
loot = Mdm::Loot.find(loot_id)
123+
begin
124+
deleted << loot.destroy
125+
rescue # refs suck
126+
elog("Forcibly deleting #{loot}")
127+
deleted << loot.delete
128+
end
129+
end
130+
131+
return deleted
132+
}
133+
end
90134
end

0 commit comments

Comments
 (0)