From 77a6469291e0082d668c31a5d802743bf434f40d Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Fri, 21 Sep 2012 15:55:43 -0500 Subject: [PATCH 01/19] implements a simple grep feature for msfconsole example usage: grep https show payloads grep -i iPhone show exploits grep -i sp3 show targets grep '^[\s]*generic/c' show payloads Usage: grep [options] pattern cmd Grep the results of a console command (similar to Linux grep command) OPTIONS: -c Only print a count of matching lines. -h Help banner. -i Ignore case. -m Stop after arg matches. -v Invert match. --- lib/msf/ui/console/command_dispatcher/core.rb | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 2d1c80ee0a..1572ab5664 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -65,6 +65,13 @@ class Core "-w" => [ true, "Specify connect timeout." ], "-z" => [ false, "Just try to connect, then return." ]) + @@grep_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help banner." ], + "-i" => [ false, "Ignore case." ], + "-m" => [ true, "Stop after arg matches." ], + "-v" => [ false, "Invert match." ], + "-c" => [ false, "Only print a count of matching lines." ]) + @@search_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help banner." ]) @@ -82,6 +89,7 @@ class Core "connect" => "Communicate with a host", "color" => "Toggle color", "exit" => "Exit the console", + "grep" => "Grep the output of another command", "help" => "Help menu", "info" => "Displays information about one or more module", "irb" => "Drop into irb scripting mode", @@ -2330,6 +2338,99 @@ class Core return true end + def cmd_grep_help + print_line "Usage: grep [options] pattern cmd" + print_line + print_line "Grep the results of a console command (similar to Linux grep command)" + print(@@grep_opts.usage()) + end + + ## + # Greps the output of another console command + # + # :call-seq: + # cmd_grep(grep_opt1, grep_opt2, pattern, command, cmd_arg) + # cmd_grep(pattern, command) + def cmd_grep(*args) + return cmd_grep_help if args.length < 2 + match_mods = {:insensitive => false} + output_mods = {:count => false, :invert => false} + @@grep_opts.parse(args.dup) do |opt, idx, val| + case opt + when "-h" + return cmd_grep_help + when "-m" + # limit to arg matches + match_mods[:max] = val.to_i + # delete opt and val from args list + args.shift + args.shift + when "-v" + # invert match + match_mods[:invert] = true + # delete opt from args list + args.shift + when "-i" + # case insensitive + match_mods[:insensitive] = true + args.shift + when "-c" + # just count matches + output_mods[:count] = true + args.shift + end + end + # after deleting parsed options, the only args left should be the pattern, the cmd to run, and cmd args + pattern = args.shift + if match_mods[:insensitive] + rx = Regexp.new(pattern, true) + else + rx = Regexp.new(pattern) + end + cmd = args.join(" ") + + # redirect stdout (only) temporarily + # we use a rex buffer but add a write method to the instance, which is required in order to be valid $stdout + orig_stdout = $stdout + buf = Rex::Ui::Text::Output::Buffer.new + def buf.write(msg = '') + self.print_raw(msg) + end + $stdout = buf # stdout is now redirected to buf + driver.run_single(cmd) + $stdout = orig_stdout # stdout is now restored + cmd_output = buf.dump_buffer + # control matching based on remaining match_mods (:insensitive was already handled) + statement = 'line =~ rx' + statement = 'not line =~ rx' if match_mods[:invert] + our_lines = [] + if match_mods[:max] + count = 0 + cmd_output.lines do |line| + # we don't wan't to keep processing if we've reached :max already + break if count >= match_mods[:max] + if (eval statement) + count += 1 + our_lines << line + end + end + else + our_lines = cmd_output.lines.select {|line| line if (eval statement)} + end + + # now control output based on output_mods such as :count + if output_mods[:count] + return print_status our_lines.length.to_s + else + our_lines.each {|line| print line} + end + end + + def cmd_grep_tabs(str, words) + # TODO, make sure this works, just guessed to start + tab_complete(words.join(" ")) + end + # # Tab complete module names # From 267221bca745d7efca711f94e9aae098edfc4cfb Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Mon, 24 Sep 2012 13:40:12 -0500 Subject: [PATCH 02/19] adds the -A and -B options to grep -A arg, show arg lines after match -B arg, show arg lines before match example: grep -i -A 2 Reverse show advanced --- lib/msf/ui/console/command_dispatcher/core.rb | 57 +++++++++++++------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 1572ab5664..058cc74439 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -70,6 +70,8 @@ class Core "-i" => [ false, "Ignore case." ], "-m" => [ true, "Stop after arg matches." ], "-v" => [ false, "Invert match." ], + "-A" => [ true, "Show arg lines after a match." ], + "-B" => [ true, "Show arg lines before a match." ], "-c" => [ false, "Only print a count of matching lines." ]) @@search_opts = Rex::Parser::Arguments.new( @@ -2365,6 +2367,18 @@ class Core # delete opt and val from args list args.shift args.shift + when "-A" + # also return arg lines after a match + output_mods[:also] = val.to_i + # delete opt and val from args list + args.shift + args.shift + when "-B" + # also return arg lines before a match + output_mods[:also] = (val.to_i * -1) + # delete opt and val from args list + args.shift + args.shift when "-v" # invert match match_mods[:invert] = true @@ -2400,30 +2414,25 @@ class Core driver.run_single(cmd) $stdout = orig_stdout # stdout is now restored cmd_output = buf.dump_buffer + # put lines into an array so we can access them more easily and split('\n') doesn't work + all_lines = cmd_output.lines.select {|line| line} # control matching based on remaining match_mods (:insensitive was already handled) statement = 'line =~ rx' statement = 'not line =~ rx' if match_mods[:invert] our_lines = [] - if match_mods[:max] - count = 0 - cmd_output.lines do |line| - # we don't wan't to keep processing if we've reached :max already - break if count >= match_mods[:max] - if (eval statement) - count += 1 - our_lines << line - end + count = 0 + all_lines.each_with_index do |line, line_num| + # we don't wan't to keep processing if we have a :max and we've reached it already + break if ( match_mods[:max] and count >= match_mods[:max] ) + if (eval statement) + count += 1 + our_lines += get_grep_lines(all_lines,line_num,output_mods[:also]) end - else - our_lines = cmd_output.lines.select {|line| line if (eval statement)} end - # now control output based on output_mods such as :count - if output_mods[:count] - return print_status our_lines.length.to_s - else - our_lines.each {|line| print line} - end + # now control output based on remaining output_mods such as :count + return print_status(count.to_s) if output_mods[:count] + our_lines.each {|line| print line} end def cmd_grep_tabs(str, words) @@ -2893,6 +2902,20 @@ protected 'Columns' => [ 'Name', 'Disclosure Date', 'Rank', 'Description' ] ) end + # + # Returns array of matched line at +line_num+ plus any after/before lines requested as + # integer +also+ from the lines specified as +all_lines+. +also+ is positive for "after" + # and negative for "before" lines + # + def get_grep_lines(all_lines,line_num, also=nil) + return [all_lines[line_num]] unless also + also = also.to_i + if also < 0 + return all_lines.slice(line_num + also, also.abs + 1) + else + return all_lines.slice(line_num, also + 1) + end + end end From e7372250d2ee8c3a7b048b47162c6ff31d65c423 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Sun, 13 Jan 2013 02:18:45 -0600 Subject: [PATCH 03/19] added -k keep and -s skip --- lib/msf/ui/console/command_dispatcher/core.rb | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 058cc74439..cb1b1a0d60 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -70,8 +70,11 @@ class Core "-i" => [ false, "Ignore case." ], "-m" => [ true, "Stop after arg matches." ], "-v" => [ false, "Invert match." ], - "-A" => [ true, "Show arg lines after a match." ], - "-B" => [ true, "Show arg lines before a match." ], + "-A" => [ true, "Show arg lines after a match." ], + "-B" => [ true, "Show arg lines before a match." ], + "-s" => [ true, "Skip arg lines before attempting match." ], + "-k" => [ true, "Keep (include) arg lines at start of file." ], + "-c" => [ false, "Only print a count of matching lines." ]) @@search_opts = Rex::Parser::Arguments.new( @@ -2347,12 +2350,13 @@ class Core print(@@grep_opts.usage()) end - ## - # Greps the output of another console command + # + # Greps the output of another console command, usage is similar the shell grep command + # grep [options] pattern other_cmd [other command's args], similar to grep [options] pattern file # - # :call-seq: - # cmd_grep(grep_opt1, grep_opt2, pattern, command, cmd_arg) - # cmd_grep(pattern, command) + # @param [Array] Args to the grep command minimally including a pattern & a command to search + # @return [String,nil] Results matching the regular expression given + def cmd_grep(*args) return cmd_grep_help if args.length < 2 match_mods = {:insensitive => false} @@ -2365,20 +2369,17 @@ class Core # limit to arg matches match_mods[:max] = val.to_i # delete opt and val from args list - args.shift - args.shift + args.shift(s) when "-A" # also return arg lines after a match output_mods[:also] = val.to_i # delete opt and val from args list - args.shift - args.shift + args.shift(2) when "-B" # also return arg lines before a match output_mods[:also] = (val.to_i * -1) # delete opt and val from args list - args.shift - args.shift + args.shift(2) when "-v" # invert match match_mods[:invert] = true @@ -2392,6 +2393,14 @@ class Core # just count matches output_mods[:count] = true args.shift + when "-k" + # keep arg number of lines at the top of the output, useful for commands with table headers in output + output_mods[:keep] = val.to_i + args.shift(2) + when "-s" + # skip arg number of lines at the top of the output, useful for avoiding undesirable matches + output_mods[:skip] = val.to_i + args.shift(2) end end # after deleting parsed options, the only args left should be the pattern, the cmd to run, and cmd args @@ -2406,7 +2415,7 @@ class Core # redirect stdout (only) temporarily # we use a rex buffer but add a write method to the instance, which is required in order to be valid $stdout orig_stdout = $stdout - buf = Rex::Ui::Text::Output::Buffer.new + buf = Rex::Ui::Text::Output::Buffer.new # @todo, change per scriptjunkie's comments? def buf.write(msg = '') self.print_raw(msg) end @@ -2417,14 +2426,20 @@ class Core # put lines into an array so we can access them more easily and split('\n') doesn't work all_lines = cmd_output.lines.select {|line| line} # control matching based on remaining match_mods (:insensitive was already handled) - statement = 'line =~ rx' - statement = 'not line =~ rx' if match_mods[:invert] + if match_mods[:invert] + statement = 'not line =~ rx' + else + statement = 'line =~ rx' + end + our_lines = [] count = 0 - all_lines.each_with_index do |line, line_num| - # we don't wan't to keep processing if we have a :max and we've reached it already - break if ( match_mods[:max] and count >= match_mods[:max] ) - if (eval statement) + all_lines.each_with_index do |line, line_num| # @todo switch to map? + next if output_mods[:skip] and line_num < output_mods[:skip] + our_lines += line if output_mods[:keep] and line_num < output_mods[:keep] + # we don't wan't to keep processing if we have a :max and we've reached it already (not counting skips/keeps) + break if match_mods[:max] and count >= match_mods[:max] + if eval statement or count += 1 our_lines += get_grep_lines(all_lines,line_num,output_mods[:also]) end @@ -2908,8 +2923,8 @@ protected # and negative for "before" lines # def get_grep_lines(all_lines,line_num, also=nil) - return [all_lines[line_num]] unless also also = also.to_i + return [all_lines[line_num]] unless also or also == 0 if also < 0 return all_lines.slice(line_num + also, also.abs + 1) else From d9990829d90f9dbf3827a56145c7ab8fc26714de Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Sun, 13 Jan 2013 02:39:56 -0600 Subject: [PATCH 04/19] fixes some issues with -k and -s --- lib/msf/ui/console/command_dispatcher/core.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 8b55c908f8..c63420c013 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -74,7 +74,6 @@ class Core "-B" => [ true, "Show arg lines before a match." ], "-s" => [ true, "Skip arg lines before attempting match." ], "-k" => [ true, "Keep (include) arg lines at start of file." ], - "-c" => [ false, "Only print a count of matching lines." ]) @@search_opts = Rex::Parser::Arguments.new( @@ -2436,11 +2435,11 @@ class Core our_lines = [] count = 0 all_lines.each_with_index do |line, line_num| # @todo switch to map? - next if output_mods[:skip] and line_num < output_mods[:skip] - our_lines += line if output_mods[:keep] and line_num < output_mods[:keep] + next if (output_mods[:skip] and line_num < output_mods[:skip]) + our_lines << line if (output_mods[:keep] and line_num < output_mods[:keep]) # we don't wan't to keep processing if we have a :max and we've reached it already (not counting skips/keeps) break if match_mods[:max] and count >= match_mods[:max] - if eval statement or + if eval statement count += 1 our_lines += get_grep_lines(all_lines,line_num,output_mods[:also]) end From 7f90082bec7c1543536883acf4912c8cb09414ed Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Sun, 13 Jan 2013 03:06:56 -0600 Subject: [PATCH 05/19] grep tab complete is working, but not fully options tab complete, but not the commands at the end --- lib/msf/ui/console/command_dispatcher/core.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index c63420c013..1d79333e4a 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2451,8 +2451,11 @@ class Core end def cmd_grep_tabs(str, words) - # TODO, make sure this works, just guessed to start - tab_complete(words.join(" ")) + # @todo, make sure this works, just guessed to start + tabs = [] + tabs += @@grep_opts.fmt.keys if (str and not str =~ /\w/) + #tabs += driver.tab_complete_stub(str) + tabs end # From 3c44769bd884257afa79441580a27e0cfa48353f Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Mon, 14 Jan 2013 14:15:13 -0600 Subject: [PATCH 06/19] attempt to add nested tab completion --- lib/msf/ui/console/command_dispatcher/core.rb | 5 ++--- lib/rex/ui/text/dispatcher_shell.rb | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 1d79333e4a..e6916164d9 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2452,9 +2452,8 @@ class Core def cmd_grep_tabs(str, words) # @todo, make sure this works, just guessed to start - tabs = [] - tabs += @@grep_opts.fmt.keys if (str and not str =~ /\w/) - #tabs += driver.tab_complete_stub(str) + tabs = @@grep_opts.fmt.keys || [] # default to use grep's options + tabs = driver.tab_complete(str, words) if (str and str =~ /\w/) # if not an opt, use normal tab comp. tabs end diff --git a/lib/rex/ui/text/dispatcher_shell.rb b/lib/rex/ui/text/dispatcher_shell.rb index ea103a180c..8fceba4e5b 100644 --- a/lib/rex/ui/text/dispatcher_shell.rb +++ b/lib/rex/ui/text/dispatcher_shell.rb @@ -273,7 +273,7 @@ module DispatcherShell # a design problem in the Readline module and depends on the # Readline.basic_word_break_characters variable being set to \x00 # - def tab_complete(str) + def tab_complete(str, previous_words = []) # Check trailing whitespace so we can tell 'x' from 'x ' str_match = str.match(/\s+$/) str_trail = (str_match.nil?) ? '' : str_match[0] @@ -288,13 +288,13 @@ module DispatcherShell self.tab_words = str_words # Pop the last word and pass it to the real method - tab_complete_stub(self.tab_words.pop) + tab_complete_stub(self.tab_words.pop, previous_words) end # Performs tab completion of a command, if supported # Current words can be found in self.tab_words # - def tab_complete_stub(str) + def tab_complete_stub(str, previous_words = []) items = [] return nil if not str @@ -333,15 +333,15 @@ module DispatcherShell str = Regexp.escape(str) end - # XXX - This still doesn't fix some Regexp warnings: + # @todo - This still doesn't fix some Regexp warnings: # ./lib/rex/ui/text/dispatcher_shell.rb:171: warning: regexp has `]' without escape # Match based on the partial word items.find_all { |e| e =~ /^#{str}/ - # Prepend the rest of the command (or it gets replaced!) + # Prepend the rest of the command, and all previous_words (or it all gets replaced!) }.map { |e| - tab_words.dup.push(e).join(' ') + (previous_words + tab_words).push(e).join(' ') } end From 9bb2dddf993b0d58fd4fa09388f4aefa04ae0ee9 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Mon, 14 Jan 2013 14:53:31 -0600 Subject: [PATCH 07/19] adds @todo for when tab_comp norm is completed tab_completion normalization is RM7649 --- lib/msf/ui/console/command_dispatcher/core.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index e6916164d9..59ee1b5093 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2453,7 +2453,9 @@ class Core def cmd_grep_tabs(str, words) # @todo, make sure this works, just guessed to start tabs = @@grep_opts.fmt.keys || [] # default to use grep's options - tabs = driver.tab_complete(str, words) if (str and str =~ /\w/) # if not an opt, use normal tab comp. + # if not an opt, use normal tab comp. + # @todo uncomment out next line when tab_completion normalization is complete RM7649 + # tabs = driver.get_all_commands if (str and str =~ /\w/) tabs end From 9ad726167e4bd54e867cd331331bcac1d3a87422 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Mon, 14 Jan 2013 17:14:48 -0600 Subject: [PATCH 08/19] changes to address scriptjunkie's rpc concerns as described in https://github.com/rapid7/metasploit-framework/pull/820 --- lib/msf/ui/console/command_dispatcher/core.rb | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 59ee1b5093..64f35854cb 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2412,18 +2412,26 @@ class Core end cmd = args.join(" ") - # redirect stdout (only) temporarily + # get a ref to the current console driver + orig_driver = self.driver + # redirect output after saving the old ones and getting a new output buffer to use for redirect + orig_driver_output = orig_driver.output + orig_driver_input = orig_driver.input # we use a rex buffer but add a write method to the instance, which is required in order to be valid $stdout - orig_stdout = $stdout - buf = Rex::Ui::Text::Output::Buffer.new # @todo, change per scriptjunkie's comments? - def buf.write(msg = '') + temp_output = Rex::Ui::Text::Output::Buffer.new + def temp_output.write(msg = '') self.print_raw(msg) end - $stdout = buf # stdout is now redirected to buf - driver.run_single(cmd) - $stdout = orig_stdout # stdout is now restored - cmd_output = buf.dump_buffer - # put lines into an array so we can access them more easily and split('\n') doesn't work + orig_driver.init_ui(orig_driver_input,temp_output) + # run the desired command to be grepped + orig_driver.run_single(cmd) + # restore original output + orig_driver.init_ui(orig_driver_input,orig_driver_output) + # @todo fix the prompt so we don't get "msf > >". I've tried everything to fix this... nada + # orig_driver.update_prompt(orig_prompt,orig_prompt_char,true) # <-- dependent on other code I added & removed + # dump the command's output so we can grep it + cmd_output = temp_output.dump_buffer + # put lines into an array so we can access them more easily and split('\n') doesn't work on the output obj. all_lines = cmd_output.lines.select {|line| line} # control matching based on remaining match_mods (:insensitive was already handled) if match_mods[:invert] @@ -2434,7 +2442,7 @@ class Core our_lines = [] count = 0 - all_lines.each_with_index do |line, line_num| # @todo switch to map? + all_lines.each_with_index do |line, line_num| next if (output_mods[:skip] and line_num < output_mods[:skip]) our_lines << line if (output_mods[:keep] and line_num < output_mods[:keep]) # we don't wan't to keep processing if we have a :max and we've reached it already (not counting skips/keeps) @@ -2451,10 +2459,10 @@ class Core end def cmd_grep_tabs(str, words) - # @todo, make sure this works, just guessed to start tabs = @@grep_opts.fmt.keys || [] # default to use grep's options # if not an opt, use normal tab comp. - # @todo uncomment out next line when tab_completion normalization is complete RM7649 + # @todo uncomment out next line when tab_completion normalization is complete RM7649 or + # replace with new code that permits "nested" tab completion # tabs = driver.get_all_commands if (str and str =~ /\w/) tabs end From c60556389f80c55bc1754077ec9bb8ae163992d9 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Tue, 15 Jan 2013 16:22:04 -0600 Subject: [PATCH 09/19] add yard doc and allow for -A and -B at same time --- lib/msf/ui/console/command_dispatcher/core.rb | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 64f35854cb..204e0317a9 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -70,10 +70,10 @@ class Core "-i" => [ false, "Ignore case." ], "-m" => [ true, "Stop after arg matches." ], "-v" => [ false, "Invert match." ], - "-A" => [ true, "Show arg lines after a match." ], - "-B" => [ true, "Show arg lines before a match." ], - "-s" => [ true, "Skip arg lines before attempting match." ], - "-k" => [ true, "Keep (include) arg lines at start of file." ], + "-A" => [ true, "Show arg lines of output After a match." ], + "-B" => [ true, "Show arg lines of output Before a match." ], + "-s" => [ true, "Skip arg lines of output before attempting match."], + "-k" => [ true, "Keep (include) arg lines at start of output." ], "-c" => [ false, "Only print a count of matching lines." ]) @@search_opts = Rex::Parser::Arguments.new( @@ -2372,12 +2372,12 @@ class Core args.shift(s) when "-A" # also return arg lines after a match - output_mods[:also] = val.to_i + output_mods[:after] = val.to_i # delete opt and val from args list args.shift(2) when "-B" # also return arg lines before a match - output_mods[:also] = (val.to_i * -1) + output_mods[:before] = (val.to_i * -1) # delete opt and val from args list args.shift(2) when "-v" @@ -2449,7 +2449,8 @@ class Core break if match_mods[:max] and count >= match_mods[:max] if eval statement count += 1 - our_lines += get_grep_lines(all_lines,line_num,output_mods[:also]) + # we might get a -A/after and a -B/before at the same time + our_lines += retrieve_grep_lines(all_lines,line_num,output_mods[:before], output_mods[:after]) end end @@ -2930,18 +2931,22 @@ protected ) end # - # Returns array of matched line at +line_num+ plus any after/before lines requested as - # integer +also+ from the lines specified as +all_lines+. +also+ is positive for "after" - # and negative for "before" lines + # Returns an array of lines at the provided line number plus any after/before lines requested from + # all_lines by supplying the 'after' and/or 'before' parameters which are always positive # - def get_grep_lines(all_lines,line_num, also=nil) - also = also.to_i - return [all_lines[line_num]] unless also or also == 0 - if also < 0 - return all_lines.slice(line_num + also, also.abs + 1) - else - return all_lines.slice(line_num, also + 1) - end + # @param all_lines [Array] An array of all lines being considered for matching + # @param line_num [Integer] The line number in all_lines which has satisifed the match + # @param after [Integer] The number of lines after the match line to include (should always be positive) + # @param before [Integer] The number of lines before the match line to include (should always be positive) + # @return [Array] Array of lines including the line at line_num and any before and after + + def retrieve_grep_lines(all_lines,line_num, before = nil, after = nil) + after = after.to_i.abs + before = before.to_i.abs + start = line_num - before + start = 0 if start < 0 + finish = line_num + before + return all_lines.slice(start..finish) end end From 86e4bb2db5f0ad9bacaa0553f1349c06b0aa5fd1 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Tue, 15 Jan 2013 16:42:02 -0600 Subject: [PATCH 10/19] yard doc fixed and added for all _tabs methods --- lib/msf/ui/console/command_dispatcher/core.rb | 102 +++++++++++++++++- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 204e0317a9..4a624f1c6b 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -233,6 +233,10 @@ class Core # # Tab completion for the resource command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_resource_tabs(str, words) tabs = [] #return tabs if words.length > 1 @@ -653,6 +657,10 @@ class Core # # Tab completion for the info command (same as use) # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_info_tabs(str, words) cmd_use_tabs(str, words) end @@ -769,6 +777,10 @@ class Core # # Tab completion for the jobs command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_jobs_tabs(str, words) if words.length == 1 return @@jobs_opts.fmt.keys @@ -792,6 +804,13 @@ class Core cmd_jobs("-k", *args) end + # + # Tab completion for the kill command + # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_kill_tabs(str, words) return [] if words.length > 1 framework.jobs.keys @@ -910,6 +929,10 @@ class Core # # Tab completion for the threads command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_threads_tabs(str, words) if words.length == 1 return @@threads_opts.fmt.keys @@ -986,6 +1009,10 @@ class Core # # Tab completion for the load command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_load_tabs(str, words) tabs = [] @@ -1145,6 +1172,10 @@ class Core # # Tab completion for the route command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_route_tabs(str, words) if words.length == 1 return %w{add remove get flush print} @@ -1267,6 +1298,13 @@ class Core print(added) end + # + # Tab completion for the loadpath command + # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_loadpath_tabs(str, words) return [] if words.length > 1 @@ -1385,6 +1423,13 @@ class Core print_line(tbl.to_s) end + # + # Tab completion for the search command + # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_search_tabs(str, words) if words.length == 1 return @@search_opts.fmt.keys @@ -1700,6 +1745,10 @@ class Core # # Tab completion for the sessions command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_sessions_tabs(str, words) if words.length == 1 return @@sessions_opts.fmt.keys @@ -1821,6 +1870,10 @@ class Core # # Tab completion for the set command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_set_tabs(str, words) # A value has already been specified @@ -1902,6 +1955,10 @@ class Core # # Tab completion for the setg command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_setg_tabs(str, words) cmd_set_tabs(str, words) end @@ -1998,6 +2055,10 @@ class Core # # Tab completion for the show command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_show_tabs(str, words) return [] if words.length > 1 @@ -2043,6 +2104,10 @@ class Core # # Tab completion for the unload command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_unload_tabs(str, words) return [] if words.length > 1 @@ -2116,6 +2181,10 @@ class Core # # Tab completion for the unset command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_unset_tabs(str, words) datastore = active_module ? active_module.datastore : self.framework.datastore datastore.keys @@ -2140,6 +2209,10 @@ class Core # # Tab completion for the unsetg command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_unsetg_tabs(str, words) self.framework.datastore.keys end @@ -2268,6 +2341,13 @@ class Core end end + # + # Tab completion for the pushm command + # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_pushm_tabs(str, words) tab_complete_module(str, words) end @@ -2321,6 +2401,10 @@ class Core # # Tab completion for the use command # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_use_tabs(str, words) return [] if words.length > 1 @@ -2352,9 +2436,10 @@ class Core # # Greps the output of another console command, usage is similar the shell grep command - # grep [options] pattern other_cmd [other command's args], similar to grep [options] pattern file + # grep [options] pattern other_cmd [other command's args], similar to the shell's grep [options] pattern file + # however it also includes -k to keep lines and -s to skip lines. grep -k 5 is useful for keeping table headers # - # @param [Array] Args to the grep command minimally including a pattern & a command to search + # @param args [Array] Args to the grep command minimally including a pattern & a command to search # @return [String,nil] Results matching the regular expression given def cmd_grep(*args) @@ -2459,6 +2544,13 @@ class Core our_lines.each {|line| print line} end + # + # Tab completion for the grep command + # + # @param str [String] the string currently being typed before tab was hit + # @param words [Array] the previously completed words on the command line. words is always + # at least 1 when tab completion has reached this stage since the command itself has been completed + def cmd_grep_tabs(str, words) tabs = @@grep_opts.fmt.keys || [] # default to use grep's options # if not an opt, use normal tab comp. @@ -2931,14 +3023,14 @@ protected ) end # - # Returns an array of lines at the provided line number plus any after/before lines requested from - # all_lines by supplying the 'after' and/or 'before' parameters which are always positive + # Returns an array of lines at the provided line number plus any before and/or after lines requested + # from all_lines by supplying the +before+ and/or +after+ parameters which are always positive # # @param all_lines [Array] An array of all lines being considered for matching # @param line_num [Integer] The line number in all_lines which has satisifed the match # @param after [Integer] The number of lines after the match line to include (should always be positive) # @param before [Integer] The number of lines before the match line to include (should always be positive) - # @return [Array] Array of lines including the line at line_num and any before and after + # @return [Array] Array of lines including the line at line_num and any +before+ and/or +after+ def retrieve_grep_lines(all_lines,line_num, before = nil, after = nil) after = after.to_i.abs From 4d33742482400b67c75479155049c16a4ac3070b Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Tue, 15 Jan 2013 17:35:57 -0600 Subject: [PATCH 11/19] fixed bug with -A --- lib/msf/ui/console/command_dispatcher/core.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 4a624f1c6b..1d96bd52da 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2462,7 +2462,7 @@ class Core args.shift(2) when "-B" # also return arg lines before a match - output_mods[:before] = (val.to_i * -1) + output_mods[:before] = val.to_i # delete opt and val from args list args.shift(2) when "-v" @@ -3037,7 +3037,7 @@ protected before = before.to_i.abs start = line_num - before start = 0 if start < 0 - finish = line_num + before + finish = line_num + after return all_lines.slice(start..finish) end end From ad8516eacf0eb96cca5f4b3ceca5863c547b543a Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Tue, 15 Jan 2013 17:57:28 -0600 Subject: [PATCH 12/19] fixed prompt issue, still need to restore context see line 2519 area. msf exploit(psexec) > grep -i -A 2 encoding show msf> --- lib/msf/ui/console/command_dispatcher/core.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 1d96bd52da..fed326023a 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -315,8 +315,8 @@ class Core driver.destack_dispatcher # Restore the prompt - prompt = framework.datastore['Prompt'] || "%undmsf%clr " - prompt_char = framework.datastore['PromptChar'] || ">" + prompt = framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt + prompt_char = framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar driver.update_prompt("#{prompt}", prompt_char, true) end end @@ -2512,8 +2512,11 @@ class Core orig_driver.run_single(cmd) # restore original output orig_driver.init_ui(orig_driver_input,orig_driver_output) - # @todo fix the prompt so we don't get "msf > >". I've tried everything to fix this... nada - # orig_driver.update_prompt(orig_prompt,orig_prompt_char,true) # <-- dependent on other code I added & removed + # restore the prompt so we don't get "msf > >". + prompt = framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt + prompt_char = framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar + driver.update_prompt("#{prompt}", prompt_char, true) + #@todo restore the prompt context # dump the command's output so we can grep it cmd_output = temp_output.dump_buffer # put lines into an array so we can access them more easily and split('\n') doesn't work on the output obj. From 2a6a833931800924bdcb7a1c3a3316f6b03b3d00 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Tue, 15 Jan 2013 22:24:36 -0600 Subject: [PATCH 13/19] prompt fixes (restores prompt context) & normalization Msf::Ui::Console::Driver::DefaultPrompt and Msf::Ui::Console::Driver::Default should be used when default is desired --- lib/msf/ui/console/command_dispatcher/core.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index fed326023a..48d70be045 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2294,8 +2294,8 @@ class Core mod.init_ui(driver.input, driver.output) # Update the command prompt - prompt = framework.datastore['Prompt'] || "%undmsf%clr " - prompt_char = framework.datastore['PromptChar'] || ">" + prompt = framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt + prompt_char = framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar driver.update_prompt("#{prompt} #{mod.type}(%bld%red#{mod.shortname}%clr) ", prompt_char, true) end @@ -2515,8 +2515,13 @@ class Core # restore the prompt so we don't get "msf > >". prompt = framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt prompt_char = framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar - driver.update_prompt("#{prompt}", prompt_char, true) - #@todo restore the prompt context + mod = active_module + if mod # if there is an active module, give them the fanciness they have come to expect + driver.update_prompt("#{prompt} #{mod.type}(%bld%red#{mod.shortname}%clr) ", prompt_char, true) + else + driver.update_prompt("#{prompt}", prompt_char, true) + end + # dump the command's output so we can grep it cmd_output = temp_output.dump_buffer # put lines into an array so we can access them more easily and split('\n') doesn't work on the output obj. @@ -2895,7 +2900,7 @@ protected [ 'MinimumRank', framework.datastore['MinimumRank'] || '', 'The minimum rank of exploits that will run without explicit confirmation' ], [ 'SessionLogging', framework.datastore['SessionLogging'] || '', 'Log all input and output for sessions' ], [ 'TimestampOutput', framework.datastore['TimestampOutput'] || '', 'Prefix all console output with a timestamp' ], - [ 'Prompt', framework.datastore['Prompt'] || '', 'The prompt string, defaults to "%undmsf%clr"' ], + [ 'Prompt', framework.datastore['Prompt'] || '', 'The prompt string, defaults to "#{Msf::Ui::Console::Driver::DefaultPrompt}"' ], [ 'PromptChar', framework.datastore['PromptChar'] || '', 'The prompt character, defaults to ">"' ], [ 'PromptTimeFormat', framework.datastore['PromptTimeFormat'] || '', 'A format for timestamp escapes in the prompt, see ruby\'s strftime docs' ], ].each { |r| tbl << r } From 204b43b0d34ba5b889499f00d50491179bc5cf14 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Tue, 15 Jan 2013 22:44:55 -0600 Subject: [PATCH 14/19] fix typo in args.shift --- lib/msf/ui/console/command_dispatcher/core.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 48d70be045..25c37ab1ba 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2454,7 +2454,7 @@ class Core # limit to arg matches match_mods[:max] = val.to_i # delete opt and val from args list - args.shift(s) + args.shift(2) when "-A" # also return arg lines after a match output_mods[:after] = val.to_i From f7195fb5b5caf7100cb43418a58eb636607c3fda Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Wed, 16 Jan 2013 00:39:22 -0600 Subject: [PATCH 15/19] handle unknown commands more informatively before it just returned nothing, now it prints the familiar "Unkown command: " message --- lib/msf/ui/console/command_dispatcher/core.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 25c37ab1ba..e72b1ec845 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2524,6 +2524,12 @@ class Core # dump the command's output so we can grep it cmd_output = temp_output.dump_buffer + + # Bail if the command failed + if cmd_output =~ /Unknown command:/ + print_error("Unknown command: #{args[0]}.") + return false + end # put lines into an array so we can access them more easily and split('\n') doesn't work on the output obj. all_lines = cmd_output.lines.select {|line| line} # control matching based on remaining match_mods (:insensitive was already handled) From 3210c5382e3971bda08386127f1950da77dde838 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Wed, 16 Jan 2013 00:49:54 -0600 Subject: [PATCH 16/19] undo vestiges of attempt to add tab_complete nesting return code to original state before I started editing --- lib/rex/ui/text/dispatcher_shell.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rex/ui/text/dispatcher_shell.rb b/lib/rex/ui/text/dispatcher_shell.rb index 8fceba4e5b..f505bd443e 100644 --- a/lib/rex/ui/text/dispatcher_shell.rb +++ b/lib/rex/ui/text/dispatcher_shell.rb @@ -273,7 +273,7 @@ module DispatcherShell # a design problem in the Readline module and depends on the # Readline.basic_word_break_characters variable being set to \x00 # - def tab_complete(str, previous_words = []) + def tab_complete(str) # Check trailing whitespace so we can tell 'x' from 'x ' str_match = str.match(/\s+$/) str_trail = (str_match.nil?) ? '' : str_match[0] @@ -288,13 +288,13 @@ module DispatcherShell self.tab_words = str_words # Pop the last word and pass it to the real method - tab_complete_stub(self.tab_words.pop, previous_words) + tab_complete_stub(self.tab_words.pop) end # Performs tab completion of a command, if supported # Current words can be found in self.tab_words # - def tab_complete_stub(str, previous_words = []) + def tab_complete_stub(str) items = [] return nil if not str @@ -339,9 +339,9 @@ module DispatcherShell # Match based on the partial word items.find_all { |e| e =~ /^#{str}/ - # Prepend the rest of the command, and all previous_words (or it all gets replaced!) + # Prepend the rest of the command (or it all gets replaced!) }.map { |e| - (previous_words + tab_words).push(e).join(' ') + tab_words.dup.push(e).join(' ') } end From b1dbbe3baacd7b9ba9afb7ce4cf07a89d48854c1 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Wed, 16 Jan 2013 00:59:45 -0600 Subject: [PATCH 17/19] msftidy eol fixes --- lib/msf/ui/console/command_dispatcher/core.rb | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index e72b1ec845..e823f75bf0 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -780,7 +780,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_jobs_tabs(str, words) if words.length == 1 return @@jobs_opts.fmt.keys @@ -810,7 +810,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_kill_tabs(str, words) return [] if words.length > 1 framework.jobs.keys @@ -932,7 +932,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_threads_tabs(str, words) if words.length == 1 return @@threads_opts.fmt.keys @@ -1012,7 +1012,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_load_tabs(str, words) tabs = [] @@ -1175,7 +1175,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_route_tabs(str, words) if words.length == 1 return %w{add remove get flush print} @@ -1304,7 +1304,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_loadpath_tabs(str, words) return [] if words.length > 1 @@ -1429,7 +1429,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_search_tabs(str, words) if words.length == 1 return @@search_opts.fmt.keys @@ -1748,7 +1748,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_sessions_tabs(str, words) if words.length == 1 return @@sessions_opts.fmt.keys @@ -1873,7 +1873,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_set_tabs(str, words) # A value has already been specified @@ -1958,7 +1958,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_setg_tabs(str, words) cmd_set_tabs(str, words) end @@ -2058,7 +2058,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_show_tabs(str, words) return [] if words.length > 1 @@ -2107,7 +2107,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_unload_tabs(str, words) return [] if words.length > 1 @@ -2184,7 +2184,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_unset_tabs(str, words) datastore = active_module ? active_module.datastore : self.framework.datastore datastore.keys @@ -2212,7 +2212,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_unsetg_tabs(str, words) self.framework.datastore.keys end @@ -2347,7 +2347,7 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_pushm_tabs(str, words) tab_complete_module(str, words) end @@ -2403,8 +2403,8 @@ class Core # # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always - # at least 1 when tab completion has reached this stage since the command itself has been completed - + # at least 1 when tab completion has reached this stage since the command itself has been completd + def cmd_use_tabs(str, words) return [] if words.length > 1 @@ -2438,9 +2438,9 @@ class Core # Greps the output of another console command, usage is similar the shell grep command # grep [options] pattern other_cmd [other command's args], similar to the shell's grep [options] pattern file # however it also includes -k to keep lines and -s to skip lines. grep -k 5 is useful for keeping table headers - # + # # @param args [Array] Args to the grep command minimally including a pattern & a command to search - # @return [String,nil] Results matching the regular expression given + # @return [String,nil] Results matching the regular expression given def cmd_grep(*args) return cmd_grep_help if args.length < 2 @@ -2507,7 +2507,7 @@ class Core def temp_output.write(msg = '') self.print_raw(msg) end - orig_driver.init_ui(orig_driver_input,temp_output) + orig_driver.init_ui(orig_driver_input,temp_output) # run the desired command to be grepped orig_driver.run_single(cmd) # restore original output @@ -2534,11 +2534,11 @@ class Core all_lines = cmd_output.lines.select {|line| line} # control matching based on remaining match_mods (:insensitive was already handled) if match_mods[:invert] - statement = 'not line =~ rx' + statement = 'not line =~ rx' else statement = 'line =~ rx' end - + our_lines = [] count = 0 all_lines.each_with_index do |line, line_num| @@ -2546,7 +2546,7 @@ class Core our_lines << line if (output_mods[:keep] and line_num < output_mods[:keep]) # we don't wan't to keep processing if we have a :max and we've reached it already (not counting skips/keeps) break if match_mods[:max] and count >= match_mods[:max] - if eval statement + if eval statement count += 1 # we might get a -A/after and a -B/before at the same time our_lines += retrieve_grep_lines(all_lines,line_num,output_mods[:before], output_mods[:after]) @@ -2564,13 +2564,13 @@ class Core # @param str [String] the string currently being typed before tab was hit # @param words [Array] the previously completed words on the command line. words is always # at least 1 when tab completion has reached this stage since the command itself has been completed - + def cmd_grep_tabs(str, words) tabs = @@grep_opts.fmt.keys || [] # default to use grep's options # if not an opt, use normal tab comp. # @todo uncomment out next line when tab_completion normalization is complete RM7649 or # replace with new code that permits "nested" tab completion - # tabs = driver.get_all_commands if (str and str =~ /\w/) + # tabs = driver.get_all_commands if (str and str =~ /\w/) tabs end From 52596ae3b43decc5cbd9343fdb3a4be86b4295a9 Mon Sep 17 00:00:00 2001 From: kernelsmith Date: Mon, 21 Jan 2013 18:17:28 -0600 Subject: [PATCH 18/19] add -R capability like hosts -R moves the set_rhosts method def out into a separate file so it can be included by both db.rb cmd_hosts and core.rb cmd_grep --- lib/msf/core/set_rhosts.rb | 36 ++++++++++++++++ lib/msf/ui/console/command_dispatcher/core.rb | 29 ++++++++++++- lib/msf/ui/console/command_dispatcher/db.rb | 41 +------------------ 3 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 lib/msf/core/set_rhosts.rb diff --git a/lib/msf/core/set_rhosts.rb b/lib/msf/core/set_rhosts.rb new file mode 100644 index 0000000000..39c3fb82e8 --- /dev/null +++ b/lib/msf/core/set_rhosts.rb @@ -0,0 +1,36 @@ +# +# Set RHOSTS in the +active_module+'s (or global if none) datastore from an array of addresses +# +# This stores all the addresses to a temporary file and utilizes the +#
file:/tmp/filename
syntax to confer the addrs. +rhosts+ +# should be an Array. NOTE: the temporary file is *not* deleted +# automatically. +# +def set_rhosts_from_addrs(rhosts) + if rhosts.empty? + print_status "The list is empty, cowardly refusing to set RHOSTS" + return + end + if active_module + mydatastore = active_module.datastore + else + # if there is no module in use set the list to the global variable + mydatastore = self.framework.datastore + end + + if rhosts.length > 5 + # Lots of hosts makes 'show options' wrap which is difficult to + # read, store to a temp file + rhosts_file = Rex::Quickfile.new("msf-db-rhosts-") + mydatastore['RHOSTS'] = 'file:'+rhosts_file.path + # create the output file and assign it to the RHOSTS variable + rhosts_file.write(rhosts.join("\n")+"\n") + rhosts_file.close + else + # For short lists, just set it directly + mydatastore['RHOSTS'] = rhosts.join(" ") + end + + print_line "RHOSTS => #{mydatastore['RHOSTS']}" + print_line +end \ No newline at end of file diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index e823f75bf0..40a254b5f1 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -20,6 +20,7 @@ module CommandDispatcher class Core include Msf::Ui::Console::CommandDispatcher + require 'msf/core/set_rhosts' # Session command options @@sessions_opts = Rex::Parser::Arguments.new( @@ -74,7 +75,8 @@ class Core "-B" => [ true, "Show arg lines of output Before a match." ], "-s" => [ true, "Skip arg lines of output before attempting match."], "-k" => [ true, "Keep (include) arg lines at start of output." ], - "-c" => [ false, "Only print a count of matching lines." ]) + "-c" => [ false, "Only print a count of matching lines." ], + "-R" => [ false, "Set RHOSTS with the results, can only be used when command output has 'addresses' as the first column" ]) @@search_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help banner." ]) @@ -2486,6 +2488,11 @@ class Core # skip arg number of lines at the top of the output, useful for avoiding undesirable matches output_mods[:skip] = val.to_i args.shift(2) + when "-R" + output_mods[:set_rhosts] = true + args.shift + output_mods[:keep] = 6 unless output_mods[:keep].to_i > 6 # we need the column headers + rhosts = [] end end # after deleting parsed options, the only args left should be the pattern, the cmd to run, and cmd args @@ -2555,7 +2562,25 @@ class Core # now control output based on remaining output_mods such as :count return print_status(count.to_s) if output_mods[:count] - our_lines.each {|line| print line} + + if output_mods[:set_rhosts] + # we don't use the db to confirm these hosts because we want to keep grep independent of db status so we + # require the first column to be the addresses otherwise there's no good way to parse the table.to_s output + col_names_row_index = nil + our_lines.each_with_index do |line,idx| + if line =~ /^address/ # this is assumed to be the line w/the column names + col_names_row_index = idx + break + end + end + rhosts_row_start = col_names_row_index + 1 + rhosts_row_start += 1 if our_lines[rhosts_row_start] =~ /^-+/ # skip the "-----" separator if present + row_of_interest = our_lines.slice(rhosts_row_start..-1) + rhosts = row_of_interest.map {|row| row.split(/\s/).first} + set_rhosts_from_addrs(rhosts) + else + our_lines.each {|line| print line} + end end # diff --git a/lib/msf/ui/console/command_dispatcher/db.rb b/lib/msf/ui/console/command_dispatcher/db.rb index c590424829..d95cda3eec 100644 --- a/lib/msf/ui/console/command_dispatcher/db.rb +++ b/lib/msf/ui/console/command_dispatcher/db.rb @@ -11,6 +11,7 @@ module CommandDispatcher class Db require 'tempfile' + require 'msf/core/set_rhosts' include Msf::Ui::Console::CommandDispatcher @@ -1476,7 +1477,7 @@ class Db print_error("The database is not connected") return end - + print_status("Purging and rebuilding the module cache in the background...") framework.threads.spawn("ModuleCacheRebuild", true) do framework.db.purge_all_module_details @@ -1491,44 +1492,6 @@ class Db print_line end - # - # Set RHOSTS in the +active_module+'s (or global if none) datastore from an array of addresses - # - # This stores all the addresses to a temporary file and utilizes the - #
file:/tmp/filename
syntax to confer the addrs. +rhosts+ - # should be an Array. NOTE: the temporary file is *not* deleted - # automatically. - # - def set_rhosts_from_addrs(rhosts) - if rhosts.empty? - print_status "The list is empty, cowardly refusing to set RHOSTS" - return - end - if active_module - mydatastore = active_module.datastore - else - # if there is no module in use set the list to the global variable - mydatastore = self.framework.datastore - end - - if rhosts.length > 5 - # Lots of hosts makes 'show options' wrap which is difficult to - # read, store to a temp file - rhosts_file = Rex::Quickfile.new("msf-db-rhosts-") - mydatastore['RHOSTS'] = 'file:'+rhosts_file.path - # create the output file and assign it to the RHOSTS variable - rhosts_file.write(rhosts.join("\n")+"\n") - rhosts_file.close - else - # For short lists, just set it directly - mydatastore['RHOSTS'] = rhosts.join(" ") - end - - print_line "RHOSTS => #{mydatastore['RHOSTS']}" - print_line - end - - def db_find_tools(tools) found = true missed = [] From afcbaffa2b48a6795bfa3f862de7f8b7a427c1ec Mon Sep 17 00:00:00 2001 From: Tod Beardsley Date: Mon, 18 Mar 2013 15:28:19 -0500 Subject: [PATCH 19/19] Revert "add -R capability like hosts -R" Pulling out the set_rhosts_from_addrs -- that's not required for grep-like functionality, and adding this method to the global namespace is undesirable. This reverts commit 52596ae3b43decc5cbd9343fdb3a4be86b4295a9. --- lib/msf/core/set_rhosts.rb | 36 ---------------- lib/msf/ui/console/command_dispatcher/core.rb | 29 +------------ lib/msf/ui/console/command_dispatcher/db.rb | 41 ++++++++++++++++++- 3 files changed, 41 insertions(+), 65 deletions(-) delete mode 100644 lib/msf/core/set_rhosts.rb diff --git a/lib/msf/core/set_rhosts.rb b/lib/msf/core/set_rhosts.rb deleted file mode 100644 index 39c3fb82e8..0000000000 --- a/lib/msf/core/set_rhosts.rb +++ /dev/null @@ -1,36 +0,0 @@ -# -# Set RHOSTS in the +active_module+'s (or global if none) datastore from an array of addresses -# -# This stores all the addresses to a temporary file and utilizes the -#
file:/tmp/filename
syntax to confer the addrs. +rhosts+ -# should be an Array. NOTE: the temporary file is *not* deleted -# automatically. -# -def set_rhosts_from_addrs(rhosts) - if rhosts.empty? - print_status "The list is empty, cowardly refusing to set RHOSTS" - return - end - if active_module - mydatastore = active_module.datastore - else - # if there is no module in use set the list to the global variable - mydatastore = self.framework.datastore - end - - if rhosts.length > 5 - # Lots of hosts makes 'show options' wrap which is difficult to - # read, store to a temp file - rhosts_file = Rex::Quickfile.new("msf-db-rhosts-") - mydatastore['RHOSTS'] = 'file:'+rhosts_file.path - # create the output file and assign it to the RHOSTS variable - rhosts_file.write(rhosts.join("\n")+"\n") - rhosts_file.close - else - # For short lists, just set it directly - mydatastore['RHOSTS'] = rhosts.join(" ") - end - - print_line "RHOSTS => #{mydatastore['RHOSTS']}" - print_line -end \ No newline at end of file diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 8dbbfea8cf..58d21445a7 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -20,7 +20,6 @@ module CommandDispatcher class Core include Msf::Ui::Console::CommandDispatcher - require 'msf/core/set_rhosts' # Session command options @@sessions_opts = Rex::Parser::Arguments.new( @@ -75,8 +74,7 @@ class Core "-B" => [ true, "Show arg lines of output Before a match." ], "-s" => [ true, "Skip arg lines of output before attempting match."], "-k" => [ true, "Keep (include) arg lines at start of output." ], - "-c" => [ false, "Only print a count of matching lines." ], - "-R" => [ false, "Set RHOSTS with the results, can only be used when command output has 'addresses' as the first column" ]) + "-c" => [ false, "Only print a count of matching lines." ]) @@search_opts = Rex::Parser::Arguments.new( "-h" => [ false, "Help banner." ]) @@ -2518,11 +2516,6 @@ class Core # skip arg number of lines at the top of the output, useful for avoiding undesirable matches output_mods[:skip] = val.to_i args.shift(2) - when "-R" - output_mods[:set_rhosts] = true - args.shift - output_mods[:keep] = 6 unless output_mods[:keep].to_i > 6 # we need the column headers - rhosts = [] end end # after deleting parsed options, the only args left should be the pattern, the cmd to run, and cmd args @@ -2592,25 +2585,7 @@ class Core # now control output based on remaining output_mods such as :count return print_status(count.to_s) if output_mods[:count] - - if output_mods[:set_rhosts] - # we don't use the db to confirm these hosts because we want to keep grep independent of db status so we - # require the first column to be the addresses otherwise there's no good way to parse the table.to_s output - col_names_row_index = nil - our_lines.each_with_index do |line,idx| - if line =~ /^address/ # this is assumed to be the line w/the column names - col_names_row_index = idx - break - end - end - rhosts_row_start = col_names_row_index + 1 - rhosts_row_start += 1 if our_lines[rhosts_row_start] =~ /^-+/ # skip the "-----" separator if present - row_of_interest = our_lines.slice(rhosts_row_start..-1) - rhosts = row_of_interest.map {|row| row.split(/\s/).first} - set_rhosts_from_addrs(rhosts) - else - our_lines.each {|line| print line} - end + our_lines.each {|line| print line} end # diff --git a/lib/msf/ui/console/command_dispatcher/db.rb b/lib/msf/ui/console/command_dispatcher/db.rb index d967398adb..88f5f6504f 100644 --- a/lib/msf/ui/console/command_dispatcher/db.rb +++ b/lib/msf/ui/console/command_dispatcher/db.rb @@ -11,7 +11,6 @@ module CommandDispatcher class Db require 'tempfile' - require 'msf/core/set_rhosts' include Msf::Ui::Console::CommandDispatcher @@ -1528,7 +1527,7 @@ class Db print_error("The database is not connected") return end - + print_status("Purging and rebuilding the module cache in the background...") framework.threads.spawn("ModuleCacheRebuild", true) do framework.db.purge_all_module_details @@ -1543,6 +1542,44 @@ class Db print_line end + # + # Set RHOSTS in the +active_module+'s (or global if none) datastore from an array of addresses + # + # This stores all the addresses to a temporary file and utilizes the + #
file:/tmp/filename
syntax to confer the addrs. +rhosts+ + # should be an Array. NOTE: the temporary file is *not* deleted + # automatically. + # + def set_rhosts_from_addrs(rhosts) + if rhosts.empty? + print_status "The list is empty, cowardly refusing to set RHOSTS" + return + end + if active_module + mydatastore = active_module.datastore + else + # if there is no module in use set the list to the global variable + mydatastore = self.framework.datastore + end + + if rhosts.length > 5 + # Lots of hosts makes 'show options' wrap which is difficult to + # read, store to a temp file + rhosts_file = Rex::Quickfile.new("msf-db-rhosts-") + mydatastore['RHOSTS'] = 'file:'+rhosts_file.path + # create the output file and assign it to the RHOSTS variable + rhosts_file.write(rhosts.join("\n")+"\n") + rhosts_file.close + else + # For short lists, just set it directly + mydatastore['RHOSTS'] = rhosts.join(" ") + end + + print_line "RHOSTS => #{mydatastore['RHOSTS']}" + print_line + end + + def db_find_tools(tools) found = true missed = []