Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement "set PAYLOAD" by index #12126

Merged
merged 3 commits into from
Jul 31, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/msf/core/evasion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ def target_index
target_idx =
begin
Integer(datastore['TARGET'])
rescue ArgumentError, TypeError
rescue TypeError, ArgumentError
datastore['TARGET']
end

Expand Down
12 changes: 2 additions & 10 deletions lib/msf/core/exploit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ def initialize(info = {})
end

# Initialize exploit datastore with target information
import_target_datastore
import_target_defaults

# All exploits can increase the delay when waiting for a session.
# However, this only applies to aggressive exploits.
Expand Down Expand Up @@ -686,7 +686,7 @@ def target_index
target_idx =
begin
Integer(datastore['TARGET'])
rescue ArgumentError, TypeError
rescue TypeError, ArgumentError
datastore['TARGET']
end

Expand All @@ -701,14 +701,6 @@ def target_index
return (target_idx) ? target_idx.to_i : nil
end

#
# Import the target's DefaultOptions hash into the datastore.
#
def import_target_datastore
return unless target && target.default_options
datastore.import_options_from_hash(target.default_options)
end

#
# Returns the target's platform, or the one assigned to the module itself.
#
Expand Down
11 changes: 10 additions & 1 deletion lib/msf/core/module/data_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ def import_defaults(clear_datastore = true)
end
end

#
# Import the target's DefaultOptions hash into the datastore.
#
def import_target_defaults
return unless target && target.default_options

datastore.import_options_from_hash(target.default_options, true, 'self')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#11529 should be fixed with self.

end

#
# Overrides the class' own datastore with the one supplied. This is used
# to allow modules to share datastores, such as a payload sharing an
Expand All @@ -38,4 +47,4 @@ def share_datastore(ds)
protected

attr_writer :datastore
end
end
15 changes: 15 additions & 0 deletions lib/msf/ui/console/command_dispatcher/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,21 @@ def show_options(mod) # :nodoc:
#print("\nTarget: #{mod.target.name}\n\n")
end

# This is for the "use" and "set" commands
def module_index(list, index, &block)
wvu marked this conversation as resolved.
Show resolved Hide resolved
return unless list.kind_of?(Array) && index

begin
idx = Integer(index)
rescue ArgumentError
return
end

# Don't support negative indices
return if idx < 0

yield list[idx]
end

end

Expand Down
19 changes: 17 additions & 2 deletions lib/msf/ui/console/command_dispatcher/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ module CommandDispatcher
class Core

include Msf::Ui::Console::CommandDispatcher
include Msf::Ui::Console::CommandDispatcher::Common

# Session command options
@@sessions_opts = Rex::Parser::Arguments.new(
Expand Down Expand Up @@ -1574,6 +1575,16 @@ def cmd_set(*args)
name = args[0]
value = args[1, args.length-1].join(' ')

# Set PAYLOAD by index
if name.upcase == 'PAYLOAD' && active_module && (active_module.exploit? || active_module.evasion?)
module_index(payload_show_results, value) do |mod|
return false unless mod && mod.respond_to?(:first)

# [name, class] from payload_show_results
value = mod.first
end
end

# If the driver indicates that the value is not valid, bust out.
if (driver.on_variable_set(global, name, value) == false)
print_error("The value specified for #{name} is not valid.")
Expand All @@ -1592,13 +1603,17 @@ def cmd_set(*args)
end

# Set PAYLOAD from TARGET
if name.upcase == 'TARGET' && active_module && active_module.exploit?
active_module.import_target_datastore
if name.upcase == 'TARGET' && active_module && (active_module.exploit? || active_module.evasion?)
active_module.import_target_defaults
end

print_line("#{name} => #{datastore[name]}")
end

def payload_show_results
Msf::Ui::Console::CommandDispatcher::Modules.class_variable_get(:@@payload_show_results)
end

#
# Tab completion for the set command
#
Expand Down
25 changes: 11 additions & 14 deletions lib/msf/ui/console/command_dispatcher/modules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def initialize(driver)
@previous_module = nil
@module_name_stack = []
@module_search_results = []
@@payload_show_results = []
@dangerzone_map = nil
end

Expand Down Expand Up @@ -641,18 +642,12 @@ def cmd_use(*args)
# Try to create an instance of the supplied module name
mod_name = args[0]

# Try to create an integer out of a supplied module name
mod_index =
begin
Integer(mod_name)
rescue ArgumentError, TypeError
nil
end

# Use a module by search index
if mod_index
return if mod_index < 0 || @module_search_results[mod_index].nil?
mod_name = @module_search_results[mod_index].fullname
module_index(@module_search_results, mod_name) do |mod|
return false unless mod && mod.respond_to?(:fullname)

# Module cache object from @module_search_results
mod_name = mod.fullname
end

# See if the supplied module name has already been resolved
Expand Down Expand Up @@ -1039,10 +1034,12 @@ def show_exploits(regex = nil, minrank = nil, opts = nil) # :nodoc:
def show_payloads(regex = nil, minrank = nil, opts = nil) # :nodoc:
# If an active module has been selected and it's an exploit, get the
# list of compatible payloads and display them
if (active_module and (active_module.exploit? == true or active_module.evasion?))
show_module_set("Compatible Payloads", active_module.compatible_payloads, regex, minrank, opts)
if active_module && (active_module.exploit? || active_module.evasion?)
wvu marked this conversation as resolved.
Show resolved Hide resolved
@@payload_show_results = active_module.compatible_payloads

show_module_set('Compatible Payloads', @@payload_show_results, regex, minrank, opts)
else
show_module_set("Payloads", framework.payloads, regex, minrank, opts)
show_module_set('Payloads', framework.payloads, regex, minrank, opts)
end
end

Expand Down
29 changes: 19 additions & 10 deletions lib/msf/ui/console/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -404,22 +404,14 @@ def on_startup(opts = {})
#
def on_variable_set(glob, var, val)
case var.downcase
when 'payload'
if framework && !framework.payloads.valid?(val)
return false
elsif active_module && active_module.type == 'exploit' && !active_module.is_payload_compatible?(val)
return false
elsif active_module
active_module.datastore.clear_non_user_defined
elsif framework
framework.datastore.clear_non_user_defined
end
when 'sessionlogging'
handle_session_logging(val) if glob
when 'consolelogging'
handle_console_logging(val) if glob
when 'loglevel'
handle_loglevel(val) if glob
when 'payload'
handle_payload(val)
when 'ssh_ident'
handle_ssh_ident(val)
end
Expand Down Expand Up @@ -572,6 +564,23 @@ def handle_loglevel(val)
set_log_level(Msf::LogSource, val)
end

#
# This method handles setting a desired payload
#
# TODO: Move this out of the console driver!
#
def handle_payload(val)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this refactor! 👍

If this also took active_module and maybe even framework as parameters it becomes easier to move somewhere else. Although since it creates side-effects on these params it might need more thought.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, not sure about this one yet, since I simply moved it out of on_variable_set like the others. I don't like how the console driver is doing all these things, but that's another refactor for the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should also be noted that is_payload_compatible? is not the same as checking compatible_payloads, so things like platform and arch aren't matched on.

So, it has been and still is possible to set a Linux payload on ms17_010_eternalblue, for instance. Not a fan of this behavior, so maybe we can leverage compatible_payloads here after #11768.

I'd rather do it in a separate PR.

if framework && !framework.payloads.valid?(val)
return false
elsif active_module && (active_module.exploit? || active_module.evasion?)
return false unless active_module.is_payload_compatible?(val)
elsif active_module
active_module.datastore.clear_non_user_defined
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#11529 happened here.

elsif framework
framework.datastore.clear_non_user_defined
end
end

#
# This method monkeypatches Net::SSH's client identification string
#
Expand Down