Skip to content

Commit 5232e99

Browse files
committed
creds command converted
1 parent 31bb0ff commit 5232e99

File tree

5 files changed

+151
-106
lines changed

5 files changed

+151
-106
lines changed

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ def create_credential(opts)
1212
end
1313
end
1414

15-
def credentials(opts = {})
15+
def creds(opts = {})
1616
begin
1717
data_service = self.get_data_service
18-
opts[:wspace] = wspace
19-
data_service.loot(opts)
18+
data_service.creds(opts)
2019
rescue Exception => e
2120
puts "Call to #{data_service.class}#credentials threw exception: #{e.message}"
2221
e.backtrace.each { |line| puts "#{line}\n" }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module RemoteCredentialDataService
55

66
CREDENTIAL_PATH = '/api/1/msf/credential'
77

8-
def credential(opts = {})
8+
def creds(opts = {})
99
json_to_open_struct_object(self.get_data(CREDENTIAL_PATH, opts), [])
1010
end
1111

lib/msf/core/db_manager/cred.rb

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
11
module Msf::DBManager::Cred
22
# This methods returns a list of all credentials in the database
3-
def creds(wspace=workspace)
4-
::ActiveRecord::Base.connection_pool.with_connection {
5-
Mdm::Cred.where("hosts.workspace_id = ?", wspace.id).joins(:service => :host)
6-
}
3+
def creds(opts)
4+
query = nil
5+
::ActiveRecord::Base.connection_pool.with_connection {
6+
query = Metasploit::Credential::Core.where( workspace_id: framework.db.workspace.id )
7+
query = query.includes(:private, :public, :logins).references(:private, :public, :logins)
8+
query = query.includes(logins: [ :service, { service: :host } ])
9+
10+
if opts[:type].present?
11+
query = query.where(metasploit_credential_privates: { type: opts[:type] })
12+
end
13+
14+
if opts[:svcs].present?
15+
query = query.where(Mdm::Service[:name].in(opts[:svcs]))
16+
end
17+
18+
if opts[:ports].present?
19+
query = query.where(Mdm::Service[:port].in(opts[:ports]))
20+
end
21+
22+
if opts[:user].present?
23+
# If we have a user regex, only include those that match
24+
query = query.where('"metasploit_credential_publics"."username" ~* ?', opts[:user])
25+
end
26+
27+
if opts[:pass].present?
28+
# If we have a password regex, only include those that match
29+
query = query.where('"metasploit_credential_privates"."data" ~* ?', opts[:pass])
30+
end
31+
32+
if opts[:host_ranges] || opts[:ports] || opts[:svcs]
33+
# Only find Cores that have non-zero Logins if the user specified a
34+
# filter based on host, port, or service name
35+
query = query.where(Metasploit::Credential::Login[:id].not_eq(nil))
36+
end
37+
}
38+
query
739
end
840

941
# This method iterates the creds table calling the supplied block with the

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ def self.api_path
55
end
66

77
def self.registered(app)
8-
app.get LootServlet.api_path, &get_credentials
9-
app.post LootServlet.api_path, &create_credential
8+
app.get CredentialServlet.api_path, &get_credentials
9+
app.post CredentialServlet.api_path, &create_credential
1010
end
1111

1212
#######
@@ -17,8 +17,16 @@ def self.get_credentials
1717
lambda {
1818
begin
1919
opts = parse_json_request(request, false)
20-
data = get_db().credentials(opts)
21-
set_json_response(data)
20+
data = get_db().creds(opts)
21+
includes = [:logins, :public, :private, :origin, :realm]
22+
# Need to append the human attribute into the private sub-object before converting to json
23+
# This is normally pulled from a class method from the MetasploitCredential class
24+
response = []
25+
data.each do |cred|
26+
json = cred.as_json(include: includes).merge('human' => cred.private.class.model_name.human)
27+
response << json
28+
end
29+
set_json_response(response)
2230
rescue Exception => e
2331
set_error_on_response(e)
2432
end
@@ -27,7 +35,7 @@ def self.get_credentials
2735

2836
def self.create_credential
2937
lambda {
30-
job = lambda { |opts| get_db().report_credential(opts) }
38+
job = lambda { |opts| get_db().report_cred(opts) }
3139
exec_report_job(request, &job)
3240
}
3341
end

lib/msf/ui/console/command_dispatcher/creds.rb

Lines changed: 99 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ def creds_search(*args)
290290
port_ranges = []
291291
svcs = []
292292
rhosts = []
293+
opts = {}
293294

294295
set_rhosts = false
295296

@@ -314,6 +315,7 @@ def creds_search(*args)
314315
end
315316
when "-t","--type"
316317
ptype = args.shift
318+
opts[:ptype] = ptype
317319
if (!ptype)
318320
print_error("Argument required for -t")
319321
return
@@ -325,14 +327,17 @@ def creds_search(*args)
325327
return
326328
end
327329
svcs = service.split(/[\s]*,[\s]*/)
330+
opts[:svcs] = svcs
328331
when "-P","--password"
329332
pass = args.shift
333+
opts[:pass] = pass
330334
if (!pass)
331335
print_error("Argument required for -P")
332336
return
333337
end
334338
when "-u","--user"
335339
user = args.shift
340+
opts[:user] = user
336341
if (!user)
337342
print_error("Argument required for -u")
338343
return
@@ -343,13 +348,15 @@ def creds_search(*args)
343348
set_rhosts = true
344349
when '-O', '--origins'
345350
hosts = args.shift
351+
opts[:hosts] = hosts
346352
if !hosts
347353
print_error("Argument required for -O")
348354
return
349355
end
350356
arg_host_range(hosts, origin_ranges)
351357
when '-S', '--search-term'
352358
search_term = args.shift
359+
opts[:search_term] = search_term
353360
else
354361
# Anything that wasn't an option is a host to search for
355362
unless (arg_host_range(arg, host_ranges))
@@ -374,6 +381,8 @@ def creds_search(*args)
374381
end
375382
end
376383

384+
opts[:type] = type if type
385+
377386
# normalize
378387
ports = port_ranges.flatten.uniq
379388
svcs.flatten!
@@ -384,120 +393,117 @@ def creds_search(*args)
384393
}
385394

386395
tbl = Rex::Text::Table.new(tbl_opts)
396+
opts = {}
397+
opts[:wspace] = framework.db.workspace
398+
query = framework.db.creds(opts)
387399

388-
::ActiveRecord::Base.connection_pool.with_connection {
389-
query = Metasploit::Credential::Core.where( workspace_id: framework.db.workspace )
390-
query = query.includes(:private, :public, :logins).references(:private, :public, :logins)
391-
query = query.includes(logins: [ :service, { service: :host } ])
392-
393-
if type.present?
394-
query = query.where(metasploit_credential_privates: { type: type })
395-
end
396-
397-
if svcs.present?
398-
query = query.where(Mdm::Service[:name].in(svcs))
399-
end
400+
query.each do |core|
400401

401-
if ports.present?
402-
query = query.where(Mdm::Service[:port].in(ports))
402+
# Exclude non-blank username creds if that's what we're after
403+
if user == "" && core.public && !(core.public.username.blank?)
404+
next
403405
end
404406

405-
if user.present?
406-
# If we have a user regex, only include those that match
407-
query = query.where('"metasploit_credential_publics"."username" ~* ?', user)
407+
# Exclude non-blank password creds if that's what we're after
408+
if pass == "" && core.private && !(core.private.data.blank?)
409+
next
408410
end
409411

410-
if pass.present?
411-
# If we have a password regex, only include those that match
412-
query = query.where('"metasploit_credential_privates"."data" ~* ?', pass)
412+
origin = ''
413+
if core.origin.kind_of?(Metasploit::Credential::Origin::Service)
414+
origin = core.origin.service.host.address
415+
elsif core.origin.kind_of?(Metasploit::Credential::Origin::Session)
416+
origin = core.origin.session.host.address
413417
end
414418

415-
if host_ranges.any? || ports.any? || svcs.any?
416-
# Only find Cores that have non-zero Logins if the user specified a
417-
# filter based on host, port, or service name
418-
query = query.where(Metasploit::Credential::Login[:id].not_eq(nil))
419+
if !origin.empty? && origin_ranges.present? && !origin_ranges.any? {|range| range.include?(origin) }
420+
next
419421
end
420422

421-
query.find_each do |core|
422-
423-
# Exclude non-blank username creds if that's what we're after
424-
if user == "" && core.public && !(core.public.username.blank?)
425-
next
426-
end
427-
428-
# Exclude non-blank password creds if that's what we're after
429-
if pass == "" && core.private && !(core.private.data.blank?)
430-
next
431-
end
432-
433-
origin = ''
434-
if core.origin.kind_of?(Metasploit::Credential::Origin::Service)
435-
origin = core.origin.service.host.address
436-
elsif core.origin.kind_of?(Metasploit::Credential::Origin::Session)
437-
origin = core.origin.session.host.address
423+
if core.logins.empty? && origin_ranges.empty?
424+
public_val = core.public ? core.public.username : ""
425+
private_val = core.private ? core.private.data : ""
426+
realm_val = core.realm ? core.realm.value : ""
427+
human_val = ""
428+
# TODO: We shouldn't have separate code paths depending on the model we're working with
429+
# This should always expect an OpenStruct.
430+
if core.private
431+
if core.private.is_a?(OpenStruct)
432+
human_val = core.human
433+
else
434+
human_val = core.private.class.model_name.human
435+
end
438436
end
439437

440-
if !origin.empty? && origin_ranges.present? && !origin_ranges.any? {|range| range.include?(origin) }
441-
next
442-
end
438+
tbl << [
439+
"", # host
440+
"", # cred
441+
"", # service
442+
public_val,
443+
private_val,
444+
realm_val,
445+
human_val,
446+
]
447+
else
448+
core.logins.each do |login|
449+
# If none of this Core's associated Logins is for a host within
450+
# the user-supplied RangeWalker, then we don't have any reason to
451+
# print it out. However, we treat the absence of ranges as meaning
452+
# all hosts.
453+
if host_ranges.present? && !host_ranges.any? { |range| range.include?(login.service.host.address) }
454+
next
455+
end
443456

444-
if core.logins.empty? && origin_ranges.empty?
445-
tbl << [
446-
"", # host
447-
"", # cred
448-
"", # service
449-
core.public,
450-
core.private,
451-
core.realm,
452-
core.private ? core.private.class.model_name.human : "",
453-
]
454-
else
455-
core.logins.each do |login|
456-
# If none of this Core's associated Logins is for a host within
457-
# the user-supplied RangeWalker, then we don't have any reason to
458-
# print it out. However, we treat the absence of ranges as meaning
459-
# all hosts.
460-
if host_ranges.present? && !host_ranges.any? { |range| range.include?(login.service.host.address) }
461-
next
462-
end
457+
row = [ login.service.host.address ]
458+
row << origin
459+
rhosts << login.service.host.address
460+
if login.service.name.present?
461+
row << "#{login.service.port}/#{login.service.proto} (#{login.service.name})"
462+
else
463+
row << "#{login.service.port}/#{login.service.proto}"
464+
end
463465

464-
row = [ login.service.host.address ]
465-
row << origin
466-
rhosts << login.service.host.address
467-
if login.service.name.present?
468-
row << "#{login.service.port}/#{login.service.proto} (#{login.service.name})"
466+
public_val = core.public ? core.public.username : ""
467+
private_val = core.private ? core.private.data : ""
468+
realm_val = core.realm ? core.realm.value : ""
469+
human_val = ""
470+
# TODO: We shouldn't have separate code paths depending on the model we're working with
471+
# This should always expect an OpenStruct.
472+
if core.private
473+
if core.private.is_a?(OpenStruct)
474+
human_val = core.human
469475
else
470-
row << "#{login.service.port}/#{login.service.proto}"
476+
human_val = core.private.class.model_name.human
471477
end
472-
473-
row += [
474-
core.public,
475-
core.private,
476-
core.realm,
477-
core.private ? core.private.class.model_name.human : "",
478-
]
479-
tbl << row
480478
end
481-
end
482-
if mode == :delete
483-
core.destroy
484-
delete_count += 1
479+
480+
row += [
481+
public_val,
482+
private_val,
483+
realm_val,
484+
human_val,
485+
]
486+
tbl << row
485487
end
486488
end
487-
488-
if output_file.nil?
489-
print_line(tbl.to_s)
490-
else
491-
# create the output file
492-
::File.open(output_file, "wb") { |f| f.write(tbl.to_csv) }
493-
print_status("Wrote creds to #{output_file}")
489+
if mode == :delete
490+
core.destroy
491+
delete_count += 1
494492
end
493+
end
495494

496-
# Finally, handle the case where the user wants the resulting list
497-
# of hosts to go into RHOSTS.
498-
set_rhosts_from_addrs(rhosts.uniq) if set_rhosts
499-
print_status("Deleted #{delete_count} creds") if delete_count > 0
500-
}
495+
if output_file.nil?
496+
print_line(tbl.to_s)
497+
else
498+
# create the output file
499+
::File.open(output_file, "wb") { |f| f.write(tbl.to_csv) }
500+
print_status("Wrote creds to #{output_file}")
501+
end
502+
503+
# Finally, handle the case where the user wants the resulting list
504+
# of hosts to go into RHOSTS.
505+
set_rhosts_from_addrs(rhosts.uniq) if set_rhosts
506+
print_status("Deleted #{delete_count} creds") if delete_count > 0
501507
end
502508

503509
def cmd_creds_tabs(str, words)

0 commit comments

Comments
 (0)