refactor search to work with existing console search function, and expand console keyword options

GSoC/Meterpreter_Web_Console
Erin Bleiweiss 2018-07-23 16:37:11 -05:00
parent 6c7650eec3
commit 2215cab7df
2 changed files with 98 additions and 117 deletions

View File

@ -4,9 +4,10 @@ module Msf::DBManager::Module
raise ::ArgumentError, "At least one search parameter must be provided." if opts.dup.except!(:fields).empty? raise ::ArgumentError, "At least one search parameter must be provided." if opts.dup.except!(:fields).empty?
search_results = [] search_results = []
metadata = Msf::Modules::Metadata::Cache.instance.get_metadata metadata = Msf::Modules::Metadata::Cache.instance.get_metadata
params = parse_params(opts)
metadata.each { |module_metadata| metadata.each { |module_metadata|
if is_match(module_metadata, opts) if Msf::Modules::Metadata::Cache.instance.matches(params, module_metadata)
search_results << get_fields(module_metadata, opts) search_results << get_fields(opts, module_metadata)
end end
} }
search_results search_results
@ -16,95 +17,29 @@ module Msf::DBManager::Module
private private
####### #######
def is_match(metadata, params) def parse_params(opts)
match = true # Parse the query params and format the hash to match what the console search `is_match` function expects
params.each do |param, value| # A param prefixed with '-' indicates "not", and will omit results matching that keyword
#
r = Regexp.new(Regexp.escape(value), true) # Resulting Hash Example:
# {"platform"=>[["android"], []]} will match modules targeting the android platform
case param # {"platform"=>[[], ["android"]]} will exclude modules targeting the android platform
when :app params = {}
(match = match && value == 'client') if metadata.is_client opts.each do |k, v|
(match = match && value == 'server') if metadata.is_server key = k.to_s
when :author, :authors unless key == "fields"
match = match && metadata.author.any? { |a| a =~ r} params[key] = [ [], [] ]
when :arch if v[0, 1] == '-'
match = match && metadata.arch =~ r params[key][1] << v[1,v.length-1]
when :bid else
match = match && metadata.references.any? { |ref| ref =~ /^bid\-/i and ref =~ r } params[key][0] << v
when :cve
match = match && metadata.references.any? { |ref| ref =~ /^cve\-/i and ref =~ r }
when :edb
match = match && metadata.references.any? { |ref| ref =~ /^edb\-/i and ref =~ r }
when :description
match = match && metadata.description =~ r
when :date, :disclosure_date
match = match && metadata.disclosure_date.to_s =~ r
when :full_name, :fullname
match = match && metadata.full_name =~ r
when :is_client
match = match && value == (metadata.is_client).to_s
when :is_server
match = match && value == (metadata.is_server).to_s
when :is_install_path
match = match && value == (metadata.is_install_path).to_s
when :mod_time
match = match && metadata.mod_time.to_s =~ r
when :name
match = match && metadata.name =~ r
when :os, :platform
terms = [metadata.platform, metadata.arch]
if metadata.targets
terms = terms + metadata.targets
end end
match = match && terms.any? { |term| term =~ r }
when :path
match = match && metadata.path =~ r
when :port, :rport
match = match && metadata.rport =~ r
when :rank
# Determine if param was prepended with gt, lt, gte, lte, or eq
# Ex: "lte300" should match all ranks <= 300
query_rank = value.dup
operator = query_rank[0, 3].tr("0-9", "")
matches_rank = metadata.rank == value.to_i
unless operator.empty?
query_rank.slice! operator
query_rank = query_rank.to_i
case operator
when 'gt'
matches_rank = metadata.rank.to_i > query_rank
when 'lt'
matches_rank = metadata.rank.to_i < query_rank
when 'gte'
matches_rank = metadata.rank.to_i >= query_rank
when 'lte'
matches_rank = metadata.rank.to_i <= query_rank
when 'eq'
matches_rank = metadata.rank.to_i == query_rank
end
end
match = match && matches_rank
when :ref, :ref_name
match = match && metadata.ref_name =~ r
when :reference, :references
match = match && metadata.references.any? { |ref| ref =~ r }
when :target, :targets
match = match && metadata.targets.any? { |target| target =~ r }
when :text
terms = [metadata.name, metadata.full_name, metadata.description] + metadata.references + metadata.author
if metadata.targets
terms = terms + metadata.targets
end
match = match && terms.any? { |term| term =~ r}
when :type
match = match && (Msf::MODULE_TYPES.any? { |type| value == type and metadata.type == type })
end end
end end
match params
end end
def get_fields(module_metadata, opts) def get_fields(opts, module_metadata)
selected_fields = {} selected_fields = {}
aliases = { aliases = {

View File

@ -10,8 +10,10 @@ module Msf::Modules::Metadata::Search
def find(search_string) def find(search_string)
search_results = [] search_results = []
params = parse_search_string(search_string)
get_metadata.each { |module_metadata| get_metadata.each { |module_metadata|
if is_match(search_string, module_metadata) if is_match(params, module_metadata)
search_results << module_metadata search_results << module_metadata
end end
} }
@ -19,15 +21,16 @@ module Msf::Modules::Metadata::Search
return search_results return search_results
end end
# Helper method for private `is_match`
def matches(params, module_metadata)
is_match(params, module_metadata)
end
####### #######
private private
####### #######
def is_match(search_string, module_metadata) def parse_search_string(search_string)
return false if not search_string
search_string += ' '
# Split search terms by space, but allow quoted strings # Split search terms by space, but allow quoted strings
terms = search_string.split(/\"/).collect{|t| t.strip==t ? t : t.split(' ')}.flatten terms = search_string.split(/\"/).collect{|t| t.strip==t ? t : t.split(' ')}.flatten
terms.delete('') terms.delete('')
@ -37,7 +40,7 @@ module Msf::Modules::Metadata::Search
terms.each do |t| terms.each do |t|
f,v = t.split(":", 2) f,v = t.split(":", 2)
if not v unless v
v = f v = f
f = 'text' f = 'text'
end end
@ -52,8 +55,13 @@ module Msf::Modules::Metadata::Search
res[f][0] << v res[f][0] << v
end end
end end
res
end
k = res def is_match(params, module_metadata)
return false if params.empty?
k = params
[0,1].each do |mode| [0,1].each do |mode|
match = false match = false
@ -68,6 +76,67 @@ module Msf::Modules::Metadata::Search
r = Regexp.new(Regexp.escape(w), true) r = Regexp.new(Regexp.escape(w), true)
case t case t
when 'app'
match = [t,w] if (w == "server" and module_metadata.is_server)
match = [t,w] if (w == "client" and module_metadata.is_client)
when 'author', 'authors'
match = [t,w] if module_metadata.author.any? { |a| a =~ r }
when 'arch'
match = [t,w,] if module_metadata.arch =~ r
when 'cve'
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^cve\-/i and ref =~ r }
when 'bid'
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^bid\-/i and ref =~ r }
when 'edb'
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^edb\-/i and ref =~ r }
when 'date', 'disclosure_date'
match = [t,w] if module_metadata.disclosure_date.to_s =~ r
when 'description'
match = [t,w] if module_metadata.description =~ r
when 'full_name', 'fullname'
match = [t,w] if module_metadata.full_name =~ r
when 'mod_time'
match = [t,w] if module_metadata.mod_time.to_s =~ r
when 'name'
match = [t,w] if module_metadata.name =~ r
when 'os', 'platform'
match = [t,w] if module_metadata.platform =~ r or module_metadata.arch =~ r
if module_metadata.targets
match = [t,w] if module_metadata.targets.any? { |t| t =~ r }
end
when 'path'
match = [t,w] if module_metadata.full_name =~ r
when 'port', 'rport'
match = [t,w] if module_metadata.rport =~ r
when 'rank'
# Determine if param was prepended with gt, lt, gte, lte, or eq
# Ex: "lte300" should match all ranks <= 300
query_rank = w.dup
operator = query_rank[0,3].tr('0-9', '')
matches_rank = module_metadata.rank == w.to_i
unless operator.empty?
query_rank.slice! operator
query_rank = query_rank.to_i
case operator
when 'gt'
matches_rank = module_metadata.rank.to_i > query_rank
when 'lt'
matches_rank = module_metadata.rank.to_i < query_rank
when 'gte'
matches_rank = module_metadata.rank.to_i >= query_rank
when 'lte'
matches_rank = module_metadata.rank.to_i <= query_rank
when 'eq'
matches_rank = module_metadata.rank.to_i == query_rank
end
end
match = [t,w] if matches_rank
when 'ref', 'ref_name'
match = [t,w] if module_metadata.ref_name =~ r
when 'reference', 'references'
match = [t,w] if module_metadata.references.any? { |ref| ref =~ r }
when 'target', 'targets'
match = [t,w] if module_metadata.targets.any? { |target| target =~ r }
when 'text' when 'text'
terms = [module_metadata.name, module_metadata.full_name, module_metadata.description] + module_metadata.references + module_metadata.author terms = [module_metadata.name, module_metadata.full_name, module_metadata.description] + module_metadata.references + module_metadata.author
@ -75,31 +144,8 @@ module Msf::Modules::Metadata::Search
terms = terms + module_metadata.targets terms = terms + module_metadata.targets
end end
match = [t,w] if terms.any? { |x| x =~ r } match = [t,w] if terms.any? { |x| x =~ r }
when 'name'
match = [t,w] if module_metadata.name =~ r
when 'path'
match = [t,w] if module_metadata.full_name =~ r
when 'author'
match = [t,w] if module_metadata.author.any? { |a| a =~ r }
when 'os', 'platform'
match = [t,w] if module_metadata.platform =~ r or module_metadata.arch =~ r
if module_metadata.targets
match = [t,w] if module_metadata.targets.any? { |t| t =~ r }
end
when 'port'
match = [t,w] if module_metadata.rport =~ r
when 'type' when 'type'
match = [t,w] if Msf::MODULE_TYPES.any? { |modt| w == modt and module_metadata.type == modt } match = [t,w] if Msf::MODULE_TYPES.any? { |modt| w == modt and module_metadata.type == modt }
when 'app'
match = [t,w] if (w == "server" and module_metadata.is_server)
match = [t,w] if (w == "client" and module_metadata.is_client)
when 'cve'
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^cve\-/i and ref =~ r }
when 'bid'
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^bid\-/i and ref =~ r }
when 'edb'
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^edb\-/i and ref =~ r }
end end
break if match break if match
end end