CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/ui/console/module_argument_parsing.rb
Views: 11783
1
# -*- coding: binary -*-
2
3
require 'addressable'
4
require 'msf/ui/console/command_dispatcher'
5
6
module Msf
7
module Ui
8
module Console
9
10
###
11
#
12
# A centralized mixin to ensure that options are consistently parsed across all module types
13
# when running a module's cmd_run/cmd_check/cmd_exploit arguments
14
#
15
###
16
module ModuleArgumentParsing
17
18
# Options which are standard and predictable across all modules
19
@@module_opts = Rex::Parser::Arguments.new(
20
['-h', '--help'] => [ false, 'Help banner.' ],
21
['-j', '--job'] => [ false, 'Run in the context of a job.' ],
22
['-J', '--foreground'] => [ false, 'Force running in the foreground, even if passive.' ],
23
['-o', '--options'] => [ true, 'A comma separated list of options in VAR=VAL format.', '<options>' ],
24
['-q', '--quiet'] => [ false, 'Run the module in quiet mode with no output' ],
25
['-r', '--reload-libs'] => [ false, 'Reload all libraries before running.' ]
26
)
27
28
@@module_opts_with_action_support = @@module_opts.merge(
29
['-a', '--action'] => [ true, 'The action to use. If none is specified, ACTION is used.', '<action>']
30
)
31
32
@@exploit_opts = @@module_opts.merge(
33
['-e', '--encoder'] => [ true, 'The payload encoder to use. If none is specified, ENCODER is used.', '<encoder>' ],
34
['-f', '--force-run'] => [ false, 'Force the exploit to run regardless of the value of MinimumRank.' ],
35
['-n', '--nop-generator'] => [ true, 'The NOP generator to use. If none is specified, NOP is used.', '<generator>' ],
36
['-p', '--payload'] => [ true, 'The payload to use. If none is specified, PAYLOAD is used.', '<payload>' ],
37
['-t', '--target'] => [ true, 'The target index to use. If none is specified, TARGET is used.', '<target>' ],
38
['-z', '--no-interact'] => [ false, 'Do not interact with the session after successful exploitation.' ]
39
)
40
41
def parse_check_opts(args)
42
help_cmd = proc do |_result|
43
cmd_check_help
44
end
45
parse_opts(@@module_opts_with_action_support, args, help_cmd: help_cmd)&.slice(:datastore_options, :reload_libs)
46
end
47
48
def parse_run_opts(args, action: nil)
49
help_cmd = proc do |result|
50
if result[:action].nil?
51
cmd_run_help
52
else
53
cmd_action_help(action)
54
end
55
end
56
57
parse_opts(@@module_opts_with_action_support, args, help_cmd: help_cmd, action: action)
58
end
59
60
def parse_exploit_opts(args)
61
help_cmd = proc do |_result|
62
cmd_exploit_help
63
end
64
parse_opts(@@exploit_opts, args, help_cmd: help_cmd)&.except(:action)
65
end
66
67
def print_module_run_or_check_usage(command:, description: nil, options: @@module_opts)
68
description ||= command == :check ? 'Check if the target is vulnerable' : "Run the current #{name.downcase} module"
69
70
is_http_mod = mod.is_a?(Msf::Exploit::Remote::HttpClient)
71
is_smb_mod = mod.is_a?(Msf::Exploit::Remote::SMB::Client) || mod.options.include?('SMBUser')
72
is_mysql_mod = mod.is_a?(Msf::Exploit::Remote::MYSQL)
73
74
print_line("Usage: #{command} [options] [RHOSTS]")
75
print_line('')
76
print_line(description)
77
print_line(options.usage)
78
print_line('Examples:')
79
print_line('')
80
print_line(" #{command} 192.168.1.123")
81
print_line(" #{command} 192.168.1.1-192.168.1.254")
82
print_line(" #{command} file:///tmp/rhost_list.txt")
83
print_line(" #{command} http://192.168.1.123/foo") if is_http_mod
84
print_line(" #{command} http://user:[email protected]/foo") if is_http_mod
85
print_line(" #{command} HttpTrace=true http://192.168.1.123/foo") if is_http_mod
86
print_line(" #{command} mysql://user:[email protected]") if is_mysql_mod
87
print_line(" #{command} SQL='select version()' mysql://user:[email protected]") if is_mysql_mod && mod.options.include?('SQL')
88
print_line(" #{command} smb://192.168.1.123") if is_smb_mod
89
print_line(" #{command} smb://user:[email protected]") if is_smb_mod
90
print_line(" #{command} LPATH=/tmp/foo.txt smb://user:[email protected]/share_name/foo.txt") if is_smb_mod && mod.options.include?('RPATH')
91
print_line('')
92
print_line('Learn more at https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html')
93
print_line('')
94
end
95
96
protected
97
98
def parse_opts(opts, args, help_cmd:, action: nil)
99
result = {
100
jobify: false,
101
quiet: false,
102
datastore_options: {},
103
action: action || mod.datastore['ACTION']
104
}
105
datastore_options = result[:datastore_options]
106
opts.parse(args) do |opt, _idx, val|
107
case opt
108
when '-e'
109
result[:encoder] = val
110
when '-f'
111
result[:force] = true
112
when '-j'
113
result[:jobify] = true
114
when '-J'
115
result[:jobify] = false
116
when '-n'
117
result[:nop] = val
118
when '-o'
119
if val.nil?
120
print_error('Missing OptionStr value')
121
help_cmd.call result
122
return
123
end
124
val << '=' unless val.include?('=')
125
val.split(',').each do |opt|
126
name, value = opt.split('=', 2)
127
append_datastore_option(datastore_options, name, value)
128
end
129
when '-p'
130
result[:payload] = val
131
when '-r'
132
result[:reload_libs] = true
133
when '-t'
134
result[:target] = val.to_i
135
when '-z'
136
result[:background] = true
137
when '-a'
138
result[:action] = val
139
when '-q'
140
result[:quiet] = true
141
when '-h'
142
help_cmd.call result
143
return
144
else
145
if val && val[0] == '-'
146
help_cmd.call result
147
return
148
end
149
150
if resembles_datastore_assignment?(val)
151
name, val = val.split('=', 2)
152
append_datastore_option(datastore_options, name, val)
153
elsif resembles_rhost_value?(val)
154
append_datastore_option(datastore_options, 'RHOSTS', val)
155
else
156
print_error("Invalid argument #{val}")
157
help_cmd.call result
158
return
159
end
160
end
161
end
162
163
result
164
end
165
166
def resembles_datastore_assignment?(val)
167
return false unless val
168
169
valid_option_regex = /^(\w|::)+=.*/
170
valid_option_regex.match?(val)
171
end
172
173
def resembles_rhost_value?(val)
174
return false unless val
175
176
::Addressable::URI.parse(val)
177
true
178
rescue ::Addressable::URI::InvalidURIError => _e
179
false
180
end
181
182
def append_datastore_option(datastore_options, name, value)
183
if name.casecmp?('RHOST') || name.casecmp?('RHOSTS')
184
new_value = quote_whitespaced_value(value)
185
if !datastore_options['RHOSTS']
186
datastore_options['RHOSTS'] = new_value
187
else
188
datastore_options['RHOSTS'] = "#{datastore_options['RHOSTS']} #{new_value}"
189
end
190
else
191
datastore_options[name.upcase] = value
192
end
193
datastore_options
194
end
195
196
# Wraps values which contain spaces in quotes to ensure it's handled correctly later
197
def quote_whitespaced_value(val)
198
val.include?(' ') ? "\"#{val}\"" : val
199
end
200
end
201
end
202
end
203
end
204
205