Add keyword searching to msfconsole

git-svn-id: file:///home/svn/framework3/trunk@12752 4d416f70-5f16-0410-b530-b9f4589650da
unstable
HD Moore 2011-05-28 02:26:04 +00:00
parent 96e6e22795
commit 52f7863890
2 changed files with 118 additions and 57 deletions

View File

@ -573,6 +573,86 @@ class Module
(datastore['DEBUG'] || '') =~ /^(1|t|y)/i
end
#
# This provides a standard set of search filters for every module.
# The search terms are in the form of:
# {
# "text" => [ [ "include_term1", "include_term2", ...], [ "exclude_term1", "exclude_term2"], ... ],
# "cve" => [ [ "include_term1", "include_term2", ...], [ "exclude_term1", "exclude_term2"], ... ]
# }
#
# Returns true on no match, false on match
#
def search_filter(k)
refs = self.references.map{|x| [x.ctx_id, x.ctx_val].join("-") }
is_exploit = (self.type == "exploit")
is_auxiliary = (self.type == "auxiliary")
is_post = (self.type == "post")
is_server = (self.respond_to?(:stance) and self.stance == "aggressive")
is_client = (self.respond_to?(:stance) and self.stance == "passive")
[0,1].each do |mode|
match = false
k.keys.each do |t|
next if k[t][mode].length == 0
k[t][mode].each do |w|
# Reset the match flag for each keyword for inclusive search
match = false if mode == 0
# Convert into a case-insensitive regex
r = Regexp.new(Regexp.escape(w), true)
case t
when 'text'
terms = [self.name, self.fullname, self.description] + refs + self.author.map{|x| x.to_s}
if self.respond_to?(:targets) and self.targets
terms = terms + self.targets.map{|x| x.name}
end
match = [t,w] if terms.any? { |x| x =~ r }
when 'name'
match = [t,w] if self.name =~ r
when 'path'
match = [t,w] if self.fullname =~ r
when 'author'
match = [t,w] if self.author.map{|x| x.to_s}.any? { |a| a =~ r }
when 'os', 'platform'
match = [t,w] if self.platform_to_s =~ r or self.arch_to_s =~ r
if not match and self.respond_to?(:targets) and self.targets
match = [t,w] if self.targets.map{|x| x.name}.any? { |t| t =~ r }
end
when 'type'
match = [t,w] if (w == "exploit" and is_exploit)
match = [t,w] if (w == "auxiliary" and is_auxiliary)
match = [t,w] if (w == "post" and is_post)
when 'app'
match = [t,w] if (w == "server" and is_server_exploit)
match = [t,w] if (w == "client" and is_client_exploit)
when 'cve'
match = [t,w] if refs.any? { |ref| ref =~ /^cve\-/i and ref =~ r }
when 'bid'
match = [t,w] if refs.any? { |ref| ref =~ /^bid\-/i and ref =~ r }
when 'osvdb'
match = [t,w] if refs.any? { |ref| ref =~ /^osvdb\-/i and ref =~ r }
end
break if match
end
# Filter this module if no matches for a given keyword type
if mode == 0 and not match
return true
end
end
# Filter this module if we matched an exlusion keyword (-value)
if mode == 1 and match
return true
end
end
false
end
##
#
# Just some handy quick checks

View File

@ -64,9 +64,6 @@ class Core
"-z" => [ false, "Just try to connect, then return." ])
@@search_opts = Rex::Parser::Arguments.new(
"-t" => [ true, "Type of module to search for (all|auxiliary|encoder|exploit|nop|payload)" ],
"-r" => [ true, "Minimum rank to return (#{RankingName.sort.map{|r|r[1]}.join("|")})" ],
"-o" => [ true, "Options or default values to search for (e.g. URIPATH,RPORT=80)" ],
"-h" => [ false, "Help banner." ])
@ -1124,73 +1121,57 @@ class Core
end
#
# Searches modules (name and description) for specified regex
# Searches modules for specific keywords
#
def cmd_search(*args)
section = 'all'
rank = 'manual'
match = nil
opts = {}
match = ''
@@search_opts.parse(args) { |opt, idx, val|
case opt
when "-h"
cmd_search_help
return
when "-t"
section = val
when "-r"
rank = val
when "-o"
val.split(',').each do |optstring|
opt, val = optstring.split('=',2)
opts[opt] = val
end
else
match = val
match += val + " "
end
}
match = '.*' if match.nil?
begin
regex = Regexp.new(match, true, 'n')
rescue RegexpError => e
print_error("Invalid regular expression: #{match} (hint: try .*)")
return
end
match += " "
# Split search terms by space, but allow quoted strings
terms = match.split(/\"/).collect{|t| t.strip==t ? t : t.split(' ')}.flatten
terms.delete('')
print_status("Searching loaded modules for pattern '#{match}'...")
# All terms are either included or excluded
res = {}
if rank.to_i != 0
rank = rank.to_i
elsif RankingName.has_value? rank
rank = RankingName.invert[rank]
else
print_error("Invalid rank, should be one of: " + RankingName.sort.map{|r| r[1]}.join(", "))
return
end
case section
when 'all'
show_auxiliary(regex, rank, opts)
show_encoders(regex, rank, opts)
show_exploits(regex, rank, opts)
show_nops(regex, rank, opts)
show_payloads(regex, rank, opts)
show_post(regex, rank, opts)
when 'auxiliary'
show_auxiliary(regex, rank, opts)
when 'encoder'
show_encoders(regex, rank, opts)
when 'exploit'
show_exploits(regex, rank, opts)
when 'nop'
show_nops(regex, rank, opts)
when 'payload'
show_payloads(regex, rank, opts)
when 'post'
show_post(regex, rank, opts)
else
print_error("Unknown type '#{section}'")
terms.each do |t|
f,v = t.split(":", 2)
if not v
v = f
f = 'text'
end
next if v.length == 0
f.downcase!
v.downcase!
res[f] ||=[ [], [] ]
if v[0,1] == "-"
next if v.length == 1
res[f][1] << v[1,v.length-1]
else
res[f][0] << v
end
end
tbl = generate_module_table("Matching Modules")
framework.modules.each do |m|
o = framework.modules.create(m[0])
if not o.search_filter(res)
tbl << [ o.fullname, o.disclosure_date.to_s, o.rank_to_s, o.name ]
end
end
print_line(tbl.to_s)
end
def cmd_search_tabs(str, words)
@ -1209,7 +1190,7 @@ class Core
end
def cmd_search_help
print_line "Usage: search [options] [regex]"
print_line "Usage: search [keywords]"
print_line
print @@search_opts.usage
end