From b121e2c3fd05fb4431930304b70767d8124ae9af Mon Sep 17 00:00:00 2001 From: Sven Vetsch Date: Fri, 2 Jan 2015 12:40:24 +0100 Subject: [PATCH 01/10] adds a get and getg method besides the already existing set/setg and unset/unsetg --- features/commands/help.feature | 2 + lib/msf/core/rpc/v10/rpc_core.rb | 5 ++ lib/msf/ui/console/command_dispatcher/core.rb | 82 +++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/features/commands/help.feature b/features/commands/help.feature index a5395f347a..43af1fb62e 100644 --- a/features/commands/help.feature +++ b/features/commands/help.feature @@ -19,6 +19,8 @@ Feature: Help command connect Communicate with a host edit Edit the current module with $VISUAL or $EDITOR exit Exit the console + get Gets the value of a variable + getg Gets the value of a global variable go_pro Launch Metasploit web GUI grep Grep the output of another command help Help menu diff --git a/lib/msf/core/rpc/v10/rpc_core.rb b/lib/msf/core/rpc/v10/rpc_core.rb index f484deec8d..ebe975d291 100644 --- a/lib/msf/core/rpc/v10/rpc_core.rb +++ b/lib/msf/core/rpc/v10/rpc_core.rb @@ -15,6 +15,11 @@ class RPC_Core < RPC_Base self.service.stop end + def rpc_getg(var) + val = framework.datastore[var] + { var.to_s => val.to_s } + end + def rpc_setg(var, val) framework.datastore[var] = val { "result" => "success" } diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index eb5601fd42..3e237f21b1 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -112,6 +112,8 @@ class Core "color" => "Toggle color", "exit" => "Exit the console", "edit" => "Edit the current module with $VISUAL or $EDITOR", + "get" => "Gets the value of a variable", + "getg" => "Gets the value of a global variable", "go_pro" => "Launch Metasploit web GUI", "grep" => "Grep the output of another command", "help" => "Help menu", @@ -2295,6 +2297,86 @@ class Core return tabs end + def cmd_get_help + print_line "Usage: get var1 var2 var3" + print_line + print_line "The get command is used to get the value of one or more variables." + print_line + end + + # + # Gets a value if it's been set. + # + def cmd_get(*args) + + # Figure out if these are global variables + global = false + + if (args[0] == '-g') + args.shift + global = true + end + + # Determine which data store we're operating on + if (active_module and global == false) + datastore = active_module.datastore + else + datastore = framework.datastore + end + + # No arguments? No cookie. + if (args.length == 0) + cmd_get_help + return false + end + + while ((val = args.shift)) + print_line("#{val} => #{datastore[val]}") + end + end + + # + # Tab completion for the get 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_get_tabs(str, words) + datastore = active_module ? active_module.datastore : self.framework.datastore + datastore.keys + end + + + def cmd_getg_help + print_line "Usage: getg var1 [var2 ...]" + print_line + print_line "Exactly like get -g, get global variables" + print_line + end + + # + # Gets variables in the global data store. + # + def cmd_getg(*args) + args.unshift('-g') + + cmd_get(*args) + end + + # + # Tab completion for the getg 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_getg_tabs(str, words) + self.framework.datastore.keys + end + + alias cmd_getg_help cmd_get_help + def cmd_unset_help print_line "Usage: unset [-g] var1 var2 var3 ..." print_line From 45cef82f6c8df222bc45fbe2d4bffadfe7f17af1 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Thu, 15 Jan 2015 11:35:39 -0800 Subject: [PATCH 02/10] Use appropriate help for get/getg --- lib/msf/ui/console/command_dispatcher/core.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 3e237f21b1..82dc1a9f55 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2326,7 +2326,7 @@ class Core # No arguments? No cookie. if (args.length == 0) - cmd_get_help + global ? cmd_getg_help : cmd_get_help return false end @@ -2347,7 +2347,6 @@ class Core datastore.keys end - def cmd_getg_help print_line "Usage: getg var1 [var2 ...]" print_line @@ -2375,8 +2374,6 @@ class Core self.framework.datastore.keys end - alias cmd_getg_help cmd_get_help - def cmd_unset_help print_line "Usage: unset [-g] var1 var2 var3 ..." print_line From 8aff50aed169b3dc8710fb18495c131672218f70 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Thu, 15 Jan 2015 11:36:32 -0800 Subject: [PATCH 03/10] Make get/getg help more consistent --- 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 82dc1a9f55..b69626519a 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2298,7 +2298,7 @@ class Core end def cmd_get_help - print_line "Usage: get var1 var2 var3" + print_line "Usage: get var1 [var2 ...]" print_line print_line "The get command is used to get the value of one or more variables." print_line From 7a900cc889b7c3d1a1b8425b5e9e4badf44e9be2 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Thu, 15 Jan 2015 11:54:01 -0800 Subject: [PATCH 04/10] More Ruby-ish way for cmd_get --- lib/msf/ui/console/command_dispatcher/core.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index b69626519a..ca45f881ec 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2318,21 +2318,19 @@ class Core end # Determine which data store we're operating on - if (active_module and global == false) + if (active_module && !global) datastore = active_module.datastore else datastore = framework.datastore end # No arguments? No cookie. - if (args.length == 0) + if args.empty? global ? cmd_getg_help : cmd_get_help return false end - while ((val = args.shift)) - print_line("#{val} => #{datastore[val]}") - end + args.each { |var| print_line("#{var} => #{datastore[var]}") } end # From ba2c33132b7508b6abc211b3fc16c35b06f0127d Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Thu, 15 Jan 2015 14:06:29 -0800 Subject: [PATCH 05/10] Put Msf::Ui::Console::CommandDispatcher specs in the right location --- .../lib/msf/ui/{ => console}/command_dispatcher/auxiliary_spec.rb | 0 spec/lib/msf/ui/{ => console}/command_dispatcher/core_spec.rb | 0 spec/lib/msf/ui/{ => console}/command_dispatcher/db_spec.rb | 0 spec/lib/msf/ui/{ => console}/command_dispatcher/exploit_spec.rb | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename spec/lib/msf/ui/{ => console}/command_dispatcher/auxiliary_spec.rb (100%) rename spec/lib/msf/ui/{ => console}/command_dispatcher/core_spec.rb (100%) rename spec/lib/msf/ui/{ => console}/command_dispatcher/db_spec.rb (100%) rename spec/lib/msf/ui/{ => console}/command_dispatcher/exploit_spec.rb (100%) diff --git a/spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/auxiliary_spec.rb similarity index 100% rename from spec/lib/msf/ui/command_dispatcher/auxiliary_spec.rb rename to spec/lib/msf/ui/console/command_dispatcher/auxiliary_spec.rb diff --git a/spec/lib/msf/ui/command_dispatcher/core_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb similarity index 100% rename from spec/lib/msf/ui/command_dispatcher/core_spec.rb rename to spec/lib/msf/ui/console/command_dispatcher/core_spec.rb diff --git a/spec/lib/msf/ui/command_dispatcher/db_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/db_spec.rb similarity index 100% rename from spec/lib/msf/ui/command_dispatcher/db_spec.rb rename to spec/lib/msf/ui/console/command_dispatcher/db_spec.rb diff --git a/spec/lib/msf/ui/command_dispatcher/exploit_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/exploit_spec.rb similarity index 100% rename from spec/lib/msf/ui/command_dispatcher/exploit_spec.rb rename to spec/lib/msf/ui/console/command_dispatcher/exploit_spec.rb From da1c56a65d86e1288c5a477971b6a3e8f217e24d Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Thu, 15 Jan 2015 14:46:12 -0800 Subject: [PATCH 06/10] Add minimal tests for get/getg --- lib/msf/ui/console/command_dispatcher/core.rb | 12 +++++------ .../console/command_dispatcher/core_spec.rb | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index ca45f881ec..cad4f63f97 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -2317,6 +2317,12 @@ class Core global = true end + # No arguments? No cookie. + if args.empty? + global ? cmd_getg_help : cmd_get_help + return false + end + # Determine which data store we're operating on if (active_module && !global) datastore = active_module.datastore @@ -2324,12 +2330,6 @@ class Core datastore = framework.datastore end - # No arguments? No cookie. - if args.empty? - global ? cmd_getg_help : cmd_get_help - return false - end - args.each { |var| print_line("#{var} => #{datastore[var]}") } end diff --git a/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb index f83188c8cf..774b93d57b 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb @@ -95,4 +95,25 @@ describe Msf::Ui::Console::CommandDispatcher::Core do end end end + + it { is_expected.to respond_to :cmd_get } + it { is_expected.to respond_to :cmd_getg } + + describe "#cmd_get" do + describe "without arguments" do + it "should show a help message" do + core.cmd_get + @output.join.should =~ /Usage: get / + end + end + end + + describe "#cmd_getg" do + describe "without arguments" do + it "should show a help message" do + core.cmd_getg + @output.join.should =~ /Usage: getg / + end + end + end end From 7f90b68cceaf378613357e6074b7e7748f86b51a Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Fri, 16 Jan 2015 08:28:32 -0800 Subject: [PATCH 07/10] Add rspec coverage for get (and set/setg, in a way) --- .../console/command_dispatcher/core_spec.rb | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb index 774b93d57b..a56c2921da 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb @@ -106,13 +106,51 @@ describe Msf::Ui::Console::CommandDispatcher::Core do @output.join.should =~ /Usage: get / end end - end - describe "#cmd_getg" do - describe "without arguments" do - it "should show a help message" do - core.cmd_getg - @output.join.should =~ /Usage: getg / + describe "with arguments" do + let(:name) { ::Rex::Text.rand_text_alpha(10).upcase } + + context "with an active module" do + let(:mod) do + mod = ::Msf::Module.new + mod.send(:initialize, {}) + mod + end + + it "should show an empty value if not set in the framework or module" do + allow(core).to receive(:active_module).and_return(mod) + allow(driver).to receive(:on_variable_set).and_return(true) + core.cmd_get(name) + @output.join.should =~ /^#{name} => $/ + end + + it "should show no value when only the framework has this variable" do + allow(core).to receive(:active_module).and_return(mod) + allow(driver).to receive(:on_variable_set).and_return(true) + core.cmd_setg(name, 'FRAMEWORK') + @output = [] + core.cmd_get(name) + @output.join.should =~ /^#{name} => $/ + end + + it "should show the module's value when only the module has this variable" do + allow(core).to receive(:active_module).and_return(mod) + allow(driver).to receive(:on_variable_set).and_return(true) + core.cmd_set(name, 'MODULE') + @output = [] + core.cmd_get(name) + @output.join.should =~ /^#{name} => MODULE$/ + end + + it "should show the module's value when both the module and the framework have this variable" do + allow(core).to receive(:active_module).and_return(mod) + allow(driver).to receive(:on_variable_set).and_return(true) + core.cmd_setg(name, 'FRAMEWORK') + core.cmd_set(name, 'MODULE') + @output = [] + core.cmd_get(name) + @output.join.should =~ /^#{name} => MODULE$/ + end end end end From c6121f0a37cb59e9a4d60d0fcb33ded1f23ebeb8 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Fri, 16 Jan 2015 08:43:14 -0800 Subject: [PATCH 08/10] Add rspec coverage for getg (and set/setg, in a way) --- .../console/command_dispatcher/core_spec.rb | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb index a56c2921da..468761bf50 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb @@ -117,7 +117,7 @@ describe Msf::Ui::Console::CommandDispatcher::Core do mod end - it "should show an empty value if not set in the framework or module" do + it "should show no value if not set in the framework or module" do allow(core).to receive(:active_module).and_return(mod) allow(driver).to receive(:on_variable_set).and_return(true) core.cmd_get(name) @@ -154,4 +154,60 @@ describe Msf::Ui::Console::CommandDispatcher::Core do end end end + + describe "#cmd_getg" do + describe "without arguments" do + it "should show a help message" do + core.cmd_getg + @output.join.should =~ /Usage: getg / + end + end + + describe "with arguments" do + let(:name) { ::Rex::Text.rand_text_alpha(10).upcase } + + context "with an active module" do + let(:mod) do + mod = ::Msf::Module.new + mod.send(:initialize, {}) + mod + end + + it "should show no value if not set in the framework or module" do + allow(core).to receive(:active_module).and_return(mod) + allow(driver).to receive(:on_variable_set).and_return(true) + core.cmd_getg(name) + @output.join.should =~ /^#{name} => $/ + end + + it "should show no value when only the module has this variable" do + allow(core).to receive(:active_module).and_return(mod) + allow(driver).to receive(:on_variable_set).and_return(true) + core.cmd_set(name, 'MODULE') + @output = [] + core.cmd_getg(name) + @output.join.should =~ /^#{name} => $/ + end + + it "should show the framework's value when only the framework has this variable" do + allow(core).to receive(:active_module).and_return(mod) + allow(driver).to receive(:on_variable_set).and_return(true) + core.cmd_setg(name, 'FRAMEWORK') + @output = [] + core.cmd_getg(name) + @output.join.should =~ /^#{name} => FRAMEWORK$/ + end + + it "should show the framework's value when both the module and the framework have this variable" do + allow(core).to receive(:active_module).and_return(mod) + allow(driver).to receive(:on_variable_set).and_return(true) + core.cmd_setg(name, 'FRAMEWORK') + core.cmd_set(name, 'MODULE') + @output = [] + core.cmd_getg(name) + @output.join.should =~ /^#{name} => FRAMEWORK$/ + end + end + end + end end From e7566944df2878a881698b962ea1d721e8f576b5 Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Fri, 16 Jan 2015 09:48:24 -0800 Subject: [PATCH 09/10] Simplify get/getg rspec --- .../console/command_dispatcher/core_spec.rb | 111 +++++------------- 1 file changed, 32 insertions(+), 79 deletions(-) diff --git a/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb index 468761bf50..234ae76c3a 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/core_spec.rb @@ -99,65 +99,37 @@ describe Msf::Ui::Console::CommandDispatcher::Core do it { is_expected.to respond_to :cmd_get } it { is_expected.to respond_to :cmd_getg } - describe "#cmd_get" do - describe "without arguments" do - it "should show a help message" do - core.cmd_get - @output.join.should =~ /Usage: get / - end + def set_and_test_variable(name, framework_value, module_value, framework_re, module_re) + # set the current module + allow(core).to receive(:active_module).and_return(mod) + # always assume set variables validate (largely irrelevant because ours are random) + allow(driver).to receive(:on_variable_set).and_return(true) + # the specified global value + core.cmd_setg(name, framework_value) if framework_value + # set the specified local value + core.cmd_set(name, module_value) if module_value + + # test the global value if specified + if framework_re + @output = [] + core.cmd_getg(name) + @output.join.should =~ framework_re end - describe "with arguments" do - let(:name) { ::Rex::Text.rand_text_alpha(10).upcase } - - context "with an active module" do - let(:mod) do - mod = ::Msf::Module.new - mod.send(:initialize, {}) - mod - end - - it "should show no value if not set in the framework or module" do - allow(core).to receive(:active_module).and_return(mod) - allow(driver).to receive(:on_variable_set).and_return(true) - core.cmd_get(name) - @output.join.should =~ /^#{name} => $/ - end - - it "should show no value when only the framework has this variable" do - allow(core).to receive(:active_module).and_return(mod) - allow(driver).to receive(:on_variable_set).and_return(true) - core.cmd_setg(name, 'FRAMEWORK') - @output = [] - core.cmd_get(name) - @output.join.should =~ /^#{name} => $/ - end - - it "should show the module's value when only the module has this variable" do - allow(core).to receive(:active_module).and_return(mod) - allow(driver).to receive(:on_variable_set).and_return(true) - core.cmd_set(name, 'MODULE') - @output = [] - core.cmd_get(name) - @output.join.should =~ /^#{name} => MODULE$/ - end - - it "should show the module's value when both the module and the framework have this variable" do - allow(core).to receive(:active_module).and_return(mod) - allow(driver).to receive(:on_variable_set).and_return(true) - core.cmd_setg(name, 'FRAMEWORK') - core.cmd_set(name, 'MODULE') - @output = [] - core.cmd_get(name) - @output.join.should =~ /^#{name} => MODULE$/ - end - end + # test the local value if specified + if module_re + @output = [] + core.cmd_get(name) + @output.join.should =~ module_re end end - describe "#cmd_getg" do + describe "#cmd_get and #cmd_getg" do describe "without arguments" do - it "should show a help message" do + it "should show the correct help message" do + core.cmd_get + @output.join.should =~ /Usage: get / + @output = [] core.cmd_getg @output.join.should =~ /Usage: getg / end @@ -174,38 +146,19 @@ describe Msf::Ui::Console::CommandDispatcher::Core do end it "should show no value if not set in the framework or module" do - allow(core).to receive(:active_module).and_return(mod) - allow(driver).to receive(:on_variable_set).and_return(true) - core.cmd_getg(name) - @output.join.should =~ /^#{name} => $/ + set_and_test_variable(name, nil, nil, /^#{name} => $/, /^#{name} => $/) end - it "should show no value when only the module has this variable" do - allow(core).to receive(:active_module).and_return(mod) - allow(driver).to receive(:on_variable_set).and_return(true) - core.cmd_set(name, 'MODULE') - @output = [] - core.cmd_getg(name) - @output.join.should =~ /^#{name} => $/ + it "should show the correct value when only the module has this variable" do + set_and_test_variable(name, nil, 'MODULE', /^#{name} => $/, /^#{name} => MODULE$/) end - it "should show the framework's value when only the framework has this variable" do - allow(core).to receive(:active_module).and_return(mod) - allow(driver).to receive(:on_variable_set).and_return(true) - core.cmd_setg(name, 'FRAMEWORK') - @output = [] - core.cmd_getg(name) - @output.join.should =~ /^#{name} => FRAMEWORK$/ + it "should show the correct value when only the framework has this variable" do + set_and_test_variable(name, 'FRAMEWORK', nil, /^#{name} => FRAMEWORK$/, /^#{name} => $/) end - it "should show the framework's value when both the module and the framework have this variable" do - allow(core).to receive(:active_module).and_return(mod) - allow(driver).to receive(:on_variable_set).and_return(true) - core.cmd_setg(name, 'FRAMEWORK') - core.cmd_set(name, 'MODULE') - @output = [] - core.cmd_getg(name) - @output.join.should =~ /^#{name} => FRAMEWORK$/ + it "should show the correct value when both the module and the framework have this variable" do + set_and_test_variable(name, 'FRAMEWORK', 'MODULE', /^#{name} => FRAMEWORK$/, /^#{name} => MODULE$/) end end end From b2e9e43f3dfcccac6608dc08cf20a353341811df Mon Sep 17 00:00:00 2001 From: Jon Hart Date: Fri, 16 Jan 2015 10:39:05 -0800 Subject: [PATCH 10/10] Add unit tests for RPC's getg --- spec/lib/msf/core/rpc/v10/rpc_core_spec.rb | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 spec/lib/msf/core/rpc/v10/rpc_core_spec.rb diff --git a/spec/lib/msf/core/rpc/v10/rpc_core_spec.rb b/spec/lib/msf/core/rpc/v10/rpc_core_spec.rb new file mode 100644 index 0000000000..06cdd96025 --- /dev/null +++ b/spec/lib/msf/core/rpc/v10/rpc_core_spec.rb @@ -0,0 +1,28 @@ +# -*- coding:binary -*- +require 'spec_helper' + +require 'msf/core/rpc/v10/rpc_base' +require 'msf/core/rpc/v10/rpc_core' +require 'msf/core/rpc/v10/service' + +describe Msf::RPC::RPC_Core do + include_context 'Msf::Simple::Framework' + + let(:service) do + Msf::RPC::Service.new(framework) + end + + let(:core) do + Msf::RPC::RPC_Core.new(service) + end + + describe '#rpc_getg' do + it 'should show an empty value if the variable is unset' do + expect(core.rpc_getg('FOO')).to eq({'FOO' => ''}) + end + it 'should show the correct value if the variable is set' do + core.rpc_setg('FOO', 'BAR') + expect(core.rpc_getg('FOO')).to eq({'FOO' => 'BAR'}) + end + end +end