From 52f78638902078e7d2092ecb7f13096d0bbda0ce Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sat, 28 May 2011 02:26:04 +0000 Subject: [PATCH] Add keyword searching to msfconsole git-svn-id: file:///home/svn/framework3/trunk@12752 4d416f70-5f16-0410-b530-b9f4589650da --- lib/msf/core/module.rb | 80 ++++++++++++++++ lib/msf/ui/console/command_dispatcher/core.rb | 95 ++++++++----------- 2 files changed, 118 insertions(+), 57 deletions(-) diff --git a/lib/msf/core/module.rb b/lib/msf/core/module.rb index e1de1ac0c8..943e33dce9 100644 --- a/lib/msf/core/module.rb +++ b/lib/msf/core/module.rb @@ -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 diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index ef68a2cd49..38b7bc055b 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -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