Module: Msf::Ui::Console::ModuleArgumentParsing

Included in:
CommandDispatcher::Exploit, CommandDispatcher::Post, ModuleActionCommands, ModuleCommandDispatcher
Defined in:
lib/msf/ui/console/module_argument_parsing.rb

Overview

A centralized mixin to ensure that options are consistently parsed across all module types when running a module's cmd_run/cmd_check/cmd_exploit arguments

Constant Summary collapse

@@module_opts =

Options which are standard and predictable across all modules

Rex::Parser::Arguments.new(
  ['-h', '--help']        => [ false, 'Help banner.'                                                       ],
  ['-j', '--job']         => [ false, 'Run in the context of a job.'                                       ],
  ['-J', '--foreground']  => [ false, 'Force running in the foreground, even if passive.'                  ],
  ['-o', '--options']     => [ true,  'A comma separated list of options in VAR=VAL format.', '<options>'  ],
  ['-q', '--quiet']       => [ false, 'Run the module in quiet mode with no output'                        ],
  ['-r', '--reload-libs'] => [ false, 'Reload all libraries before running.'                               ]
)
@@module_opts_with_action_support =
@@module_opts.merge(
  ['-a', '--action'] => [ true, 'The action to use. If none is specified, ACTION is used.', '<action>']
)
@@exploit_opts =
@@module_opts.merge(
  ['-e', '--encoder']       => [ true,  'The payload encoder to use.  If none is specified, ENCODER is used.', '<encoder>'   ],
  ['-f', '--force-run']     => [ false, 'Force the exploit to run regardless of the value of MinimumRank.'                   ],
  ['-n', '--nop-generator'] => [ true,  'The NOP generator to use.  If none is specified, NOP is used.', '<generator>'       ],
  ['-p', '--payload']       => [ true,  'The payload to use.  If none is specified, PAYLOAD is used.', '<payload>'           ],
  ['-t', '--target']        => [ true,  'The target index to use.  If none is specified, TARGET is used.', '<target>'        ],
  ['-z', '--no-interact']   => [ false, 'Do not interact with the session after successful exploitation.'                    ]
)

Instance Method Summary collapse

Instance Method Details

#append_datastore_option(datastore_options, name, value) ⇒ Object (protected)



182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 182

def append_datastore_option(datastore_options, name, value)
  if name.casecmp?('RHOST') || name.casecmp?('RHOSTS')
    new_value = quote_whitespaced_value(value)
    if !datastore_options['RHOSTS']
      datastore_options['RHOSTS'] = new_value
    else
      datastore_options['RHOSTS'] = "#{datastore_options['RHOSTS']} #{new_value}"
    end
  else
    datastore_options[name.upcase] = value
  end
  datastore_options
end

#parse_check_opts(args) ⇒ Object



41
42
43
44
45
46
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 41

def parse_check_opts(args)
  help_cmd = proc do |_result|
    cmd_check_help
  end
  parse_opts(@@module_opts_with_action_support, args, help_cmd: help_cmd)&.slice(:datastore_options, :reload_libs)
end

#parse_exploit_opts(args) ⇒ Object



60
61
62
63
64
65
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 60

def parse_exploit_opts(args)
  help_cmd = proc do |_result|
    cmd_exploit_help
  end
  parse_opts(@@exploit_opts, args, help_cmd: help_cmd)&.except(:action)
end

#parse_opts(opts, args, help_cmd:, action: nil) ⇒ Object (protected)



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 98

def parse_opts(opts, args, help_cmd:, action: nil)
  result = {
    jobify: false,
    quiet: false,
    datastore_options: {},
    action: action || mod.datastore['ACTION']
  }
  datastore_options = result[:datastore_options]
  opts.parse(args) do |opt, _idx, val|
    case opt
    when '-e'
      result[:encoder] = val
    when '-f'
      result[:force] = true
    when '-j'
      result[:jobify] = true
    when '-J'
      result[:jobify] = false
    when '-n'
      result[:nop] = val
    when '-o'
      if val.nil?
        print_error('Missing OptionStr value')
        help_cmd.call result
        return
      end
      val << '=' unless val.include?('=')
      val.split(',').each do |opt|
        name, value = opt.split('=', 2)
        append_datastore_option(datastore_options, name, value)
      end
    when '-p'
      result[:payload] = val
    when '-r'
      result[:reload_libs] = true
    when '-t'
      result[:target] = val.to_i
    when '-z'
      result[:background] = true
    when '-a'
      result[:action] = val
    when '-q'
      result[:quiet] = true
    when '-h'
      help_cmd.call result
      return
    else
      if val && val[0] == '-'
        help_cmd.call result
        return
      end

      if resembles_datastore_assignment?(val)
        name, val = val.split('=', 2)
        append_datastore_option(datastore_options, name, val)
      elsif resembles_rhost_value?(val)
        append_datastore_option(datastore_options, 'RHOSTS', val)
      else
        print_error("Invalid argument #{val}")
        help_cmd.call result
        return
      end
    end
  end

  result
end

#parse_run_opts(args, action: nil) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 48

def parse_run_opts(args, action: nil)
  help_cmd = proc do |result|
    if result[:action].nil?
      cmd_run_help
    else
      cmd_action_help(action)
    end
  end

  parse_opts(@@module_opts_with_action_support, args, help_cmd: help_cmd, action: action)
end


67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 67

def print_module_run_or_check_usage(command:, description: nil, options: @@module_opts)
  description ||= command == :check ? 'Check if the target is vulnerable' : "Run the current #{name.downcase} module"

  is_http_mod = mod.is_a?(Msf::Exploit::Remote::HttpClient)
  is_smb_mod = mod.is_a?(Msf::Exploit::Remote::SMB::Client) || mod.options.include?('SMBUser')
  is_mysql_mod = mod.is_a?(Msf::Exploit::Remote::MYSQL)

  print_line("Usage: #{command} [options] [RHOSTS]")
  print_line('')
  print_line(description)
  print_line(options.usage)
  print_line('Examples:')
  print_line('')
  print_line("    #{command} 192.168.1.123")
  print_line("    #{command} 192.168.1.1-192.168.1.254")
  print_line("    #{command} file:///tmp/rhost_list.txt")
  print_line("    #{command} http://192.168.1.123/foo") if is_http_mod
  print_line("    #{command} http://user:pass@192.168.1.123/foo") if is_http_mod
  print_line("    #{command} HttpTrace=true http://192.168.1.123/foo") if is_http_mod
  print_line("    #{command} mysql://user:pass@192.168.1.123") if is_mysql_mod
  print_line("    #{command} SQL='select version()' mysql://user:pass@192.168.1.123") if is_mysql_mod && mod.options.include?('SQL')
  print_line("    #{command} smb://192.168.1.123") if is_smb_mod
  print_line("    #{command} smb://user:pass@192.168.1.123") if is_smb_mod
  print_line("    #{command} LPATH=/tmp/foo.txt smb://user:pass@192.168.1.123/share_name/foo.txt") if is_smb_mod && mod.options.include?('RPATH')
  print_line('')
  print_line('Learn more at https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html')
  print_line('')
end

#quote_whitespaced_value(val) ⇒ Object (protected)

Wraps values which contain spaces in quotes to ensure it's handled correctly later



197
198
199
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 197

def quote_whitespaced_value(val)
  val.include?(' ') ? "\"#{val}\"" : val
end

#resembles_datastore_assignment?(val) ⇒ Boolean (protected)

Returns:

  • (Boolean)


166
167
168
169
170
171
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 166

def resembles_datastore_assignment?(val)
  return false unless val

  valid_option_regex = /^(\w|::)+=.*/
  valid_option_regex.match?(val)
end

#resembles_rhost_value?(val) ⇒ Boolean (protected)

Returns:

  • (Boolean)


173
174
175
176
177
178
179
180
# File 'lib/msf/ui/console/module_argument_parsing.rb', line 173

def resembles_rhost_value?(val)
  return false unless val

  ::Addressable::URI.parse(val)
  true
rescue ::Addressable::URI::InvalidURIError => _e
  false
end