Skip to content

Commit 24b9713

Browse files
committed
Msf::DBManager Mdm::Module* specs
[#47979793]
1 parent 3325b17 commit 24b9713

File tree

9 files changed

+1378
-106
lines changed

9 files changed

+1378
-106
lines changed

lib/msf/core/db_manager.rb

Lines changed: 118 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -314,17 +314,28 @@ def workspace
314314
end
315315

316316

317+
# @note Does nothing unless {#migrated} is +true+ and {#modules_caching} is
318+
# +false+.
319+
#
320+
# Destroys all Mdm::ModuleDetails in the database.
321+
#
322+
# @return [void]
317323
def purge_all_module_details
318324
return if not self.migrated
319325
return if self.modules_caching
320326

321327
::ActiveRecord::Base.connection_pool.with_connection do
322328
Mdm::ModuleDetail.destroy_all
323329
end
324-
325-
true
326330
end
327331

332+
# Destroys the old Mdm::ModuleDetail and creates a new Mdm::ModuleDetail for
333+
# any module with an Mdm::ModuleDetail where the modification time of the
334+
# Mdm::ModuleDetail#file differs from the Mdm::ModuleDetail#mtime. If the
335+
# Mdm::ModuleDetail#file no only exists on disk, then the Mdm::ModuleDetail
336+
# is just destroyed without a new one being created.
337+
#
338+
# @return [void]
328339
def update_all_module_details
329340
return if not self.migrated
330341
return if self.modules_caching
@@ -334,106 +345,110 @@ def update_all_module_details
334345
self.modules_cached = false
335346
self.modules_caching = true
336347

337-
::ActiveRecord::Base.connection_pool.with_connection {
348+
ActiveRecord::Base.connection_pool.with_connection do
338349

339-
refresh = []
340-
skipped = []
350+
refresh = []
351+
skipped = []
341352

342-
Mdm::ModuleDetail.find_each do |md|
353+
Mdm::ModuleDetail.find_each do |md|
343354

344-
unless md.ready
345-
refresh << md
346-
next
347-
end
355+
unless md.ready
356+
refresh << md
357+
next
358+
end
348359

349-
unless md.file and ::File.exists?(md.file)
350-
refresh << md
351-
next
352-
end
360+
unless md.file and ::File.exists?(md.file)
361+
refresh << md
362+
next
363+
end
353364

354-
if ::File.mtime(md.file).to_i != md.mtime.to_i
355-
refresh << md
356-
next
357-
end
365+
if ::File.mtime(md.file).to_i != md.mtime.to_i
366+
refresh << md
367+
next
368+
end
358369

359-
skipped << [md.mtype, md.refname]
360-
end
370+
skipped << [md.mtype, md.refname]
371+
end
361372

362-
refresh.each {|md| md.destroy }
363-
refresh = nil
364-
365-
[
366-
[ 'exploit', framework.exploits ],
367-
[ 'auxiliary', framework.auxiliary ],
368-
[ 'post', framework.post ],
369-
[ 'payload', framework.payloads ],
370-
[ 'encoder', framework.encoders ],
371-
[ 'nop', framework.nops ]
372-
].each do |mt|
373-
mt[1].keys.sort.each do |mn|
374-
next if skipped.include?( [ mt[0], mn ] )
375-
obj = mt[1].create(mn)
376-
next if not obj
377-
begin
378-
update_module_details(obj)
379-
rescue ::Exception
380-
elog("Error updating module details for #{obj.fullname}: #{$!.class} #{$!}")
373+
refresh.each { |md| md.destroy }
374+
375+
[
376+
['exploit', framework.exploits],
377+
['auxiliary', framework.auxiliary],
378+
['post', framework.post],
379+
['payload', framework.payloads],
380+
['encoder', framework.encoders],
381+
['nop', framework.nops]
382+
].each do |mt|
383+
mt[1].keys.sort.each do |mn|
384+
next if skipped.include?([mt[0], mn])
385+
obj = mt[1].create(mn)
386+
next if not obj
387+
begin
388+
update_module_details(obj)
389+
rescue ::Exception
390+
elog("Error updating module details for #{obj.fullname}: #{$!.class} #{$!}")
391+
end
381392
end
382393
end
383-
end
384394

385-
self.framework.cache_initialized = true
386-
self.framework.cache_thread = nil
395+
self.framework.cache_initialized = true
396+
end
387397

388-
self.modules_cached = true
398+
# in reverse order of section before with_connection block
389399
self.modules_caching = false
390-
391-
nil
392-
393-
}
400+
self.modules_cached = true
401+
self.framework.cache_thread = nil
394402
end
395403

396-
def update_module_details(obj)
404+
# Creates an Mdm::ModuleDetail from a module instance.
405+
#
406+
# @param module_instance [Msf::Module] a metasploit module instance.
407+
# @return [void]
408+
def update_module_details(module_instance)
397409
return if not self.migrated
398410

399-
::ActiveRecord::Base.connection_pool.with_connection {
400-
info = module_to_details_hash(obj)
401-
bits = info.delete(:bits) || []
402-
403-
md = Mdm::ModuleDetail.create(info)
404-
bits.each do |args|
405-
otype, vals = args
406-
case otype
407-
when :author
408-
md.add_author(vals[:name], vals[:email])
409-
when :action
410-
md.add_action(vals[:name])
411-
when :arch
412-
md.add_arch(vals[:name])
413-
when :platform
414-
md.add_platform(vals[:name])
415-
when :target
416-
md.add_target(vals[:index], vals[:name])
417-
when :ref
418-
md.add_ref(vals[:name])
419-
when :mixin
420-
# md.add_mixin(vals[:name])
411+
ActiveRecord::Base.connection_pool.with_connection do
412+
info = module_to_details_hash(module_instance)
413+
bits = info.delete(:bits) || []
414+
module_detail = Mdm::ModuleDetail.create(info)
415+
416+
bits.each do |args|
417+
otype, vals = args
418+
419+
case otype
420+
when :action
421+
module_detail.add_action(vals[:name])
422+
when :arch
423+
module_detail.add_arch(vals[:name])
424+
when :author
425+
module_detail.add_author(vals[:name], vals[:email])
426+
when :platform
427+
module_detail.add_platform(vals[:name])
428+
when :ref
429+
module_detail.add_ref(vals[:name])
430+
when :target
431+
module_detail.add_target(vals[:index], vals[:name])
432+
end
421433
end
422-
end
423-
424-
md.ready = true
425-
md.save
426-
md.id
427434

428-
}
435+
module_detail.ready = true
436+
module_detail.save
437+
end
429438
end
430439

440+
# Destroys Mdm::ModuleDetail if one exists for the given
441+
# Mdm::ModuleDetail#mtype and Mdm::ModuleDetail#refname.
442+
#
443+
# @param mtype [String] module type.
444+
# @param refname [String] module reference name.
445+
# @return [void]
431446
def remove_module_details(mtype, refname)
432447
return if not self.migrated
433-
::ActiveRecord::Base.connection_pool.with_connection {
434-
md = Mdm::ModuleDetail.find(:conditions => [ 'mtype = ? and refname = ?', mtype, refname])
435-
md.destroy if md
436-
}
448+
449+
ActiveRecord::Base.connection_pool.with_connection do
450+
Mdm::ModuleDetail.where(:mtype => mtype, :refname => refname).destroy_all
451+
end
437452
end
438453

439454
def module_to_details_hash(m)
@@ -525,17 +540,31 @@ def module_to_details_hash(m)
525540

526541

527542

528-
#
529543
# This provides a standard set of search filters for every module.
530-
# The search terms are in the form of:
531-
# {
532-
# "text" => [ [ "include_term1", "include_term2", ...], [ "exclude_term1", "exclude_term2"], ... ],
533-
# "cve" => [ [ "include_term1", "include_term2", ...], [ "exclude_term1", "exclude_term2"], ... ]
534-
# }
535544
#
536-
# Returns true on no match, false on match
545+
# Supported keywords with the format <keyword>:<search_value>:
546+
# +app+:: If +client+ then matches +'passive'+ stance modules, otherwise matches +'active' stance modules.
547+
# +author+:: Matches modules with the given author email or name.
548+
# +bid+:: Matches modules with the given Bugtraq ID.
549+
# +cve+:: Matches modules with the given CVE ID.
550+
# +edb+:: Matches modules with the given Exploit-DB ID.
551+
# +name+:: Matches modules with the given full name or name.
552+
# +os+, +platform+:: Matches modules with the given platform or target name.
553+
# +osvdb+:: Matches modules with the given OSVDB ID.
554+
# +ref+:: Matches modules with the given reference ID.
555+
# +type+:: Matches modules with the given type.
556+
#
557+
# Any text not associated with a keyword is matched against the description,
558+
# the full name, and the name of the module; the name of the module actions;
559+
# the name of the module archs; the name of the module authors; the name of
560+
# module platform; the module refs; or the module target.
537561
#
538-
def search_modules(search_string, inclusive=false)
562+
# @param search_string [String] a string of space separated keyword pairs or
563+
# free form text.
564+
# @return [false] if search_string is +nil+
565+
# @return [Array<Mdm::ModuleDetail>] module details that matched
566+
# +search_string+
567+
def search_modules(search_string)
539568
return false if not search_string
540569

541570
search_string += " "
@@ -589,8 +618,6 @@ def search_modules(search_string, inclusive=false)
589618
xv = "%#{kv}%"
590619
where_q << ' ( module_platforms.name ILIKE ? OR module_targets.name ILIKE ? ) '
591620
where_v << [ xv, xv ]
592-
when 'port'
593-
# TODO
594621
when 'type'
595622
where_q << ' ( module_details.mtype = ? ) '
596623
where_v << [ kv ]
@@ -617,7 +644,7 @@ def search_modules(search_string, inclusive=false)
617644
"LEFT OUTER JOIN module_targets ON module_details.id = module_targets.module_detail_id " +
618645
"LEFT OUTER JOIN module_platforms ON module_details.id = module_platforms.module_detail_id "
619646
).
620-
where(where_q.join(inclusive ? " OR " : " AND "), *(where_v.flatten)).
647+
where(where_q.join(" AND "), *(where_v.flatten)).
621648
# Compatibility for Postgres installations prior to 9.1 - doesn't have support for wildcard group by clauses
622649
group("module_details.id, module_details.mtime, module_details.file, module_details.mtype, module_details.refname, module_details.fullname, module_details.name, module_details.rank, module_details.description, module_details.license, module_details.privileged, module_details.disclosure_date, module_details.default_target, module_details.default_action, module_details.stance, module_details.ready")
623650

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

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,17 +1386,16 @@ def cmd_search_help
13861386
print_line
13871387
print_line "Keywords:"
13881388
{
1389-
"name" => "Modules with a matching descriptive name",
1390-
"path" => "Modules with a matching path or reference name",
1391-
"platform" => "Modules affecting this platform",
1392-
"port" => "Modules with a matching remote port",
1393-
"type" => "Modules of a specific type (exploit, auxiliary, or post)",
1394-
"app" => "Modules that are client or server attacks",
1395-
"author" => "Modules written by this author",
1396-
"cve" => "Modules with a matching CVE ID",
1397-
"bid" => "Modules with a matching Bugtraq ID",
1398-
"osvdb" => "Modules with a matching OSVDB ID",
1399-
"edb" => "Modules with a matching Exploit-DB ID"
1389+
'app' => 'Modules that are client or server attacks',
1390+
'author' => 'Modules written by this author',
1391+
'bid' => 'Modules with a matching Bugtraq ID',
1392+
'cve' => 'Modules with a matching CVE ID',
1393+
'edb' => 'Modules with a matching Exploit-DB ID',
1394+
'name' => 'Modules with a matching descriptive name',
1395+
'osvdb' => 'Modules with a matching OSVDB ID',
1396+
'platform' => 'Modules affecting this platform',
1397+
'ref' => 'Modules with a matching ref',
1398+
'type' => 'Modules of a specific type (exploit, auxiliary, or post)',
14001399
}.each_pair do |keyword, description|
14011400
print_line " #{keyword.ljust 10}: #{description}"
14021401
end

spec/factories/mdm/module_details.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
sequence :mdm_module_detail_rank do |n|
4747
(100 * n)
4848
end
49+
50+
stances = ['active', 'passive']
51+
sequence :mdm_module_detail_stance, stances.cycle
4952
end
5053

5154
modules_pathname = Metasploit::Framework.root.join('modules')
@@ -70,6 +73,7 @@
7073
rank { generate :mdm_module_detail_rank }
7174
refname { generate :mdm_module_detail_refname }
7275
fullname { "#{mtype}/#{refname}" }
76+
stance { generate :mdm_module_detail_stance }
7377

7478
file {
7579
type_directory = type_directory_by_type[mtype]

0 commit comments

Comments
 (0)