Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/ui/console/module_argument_parsing.rb
19566 views
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)
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
if name.upcase == 'ACTION'
153
result[:action] = val
154
else
155
append_datastore_option(datastore_options, name, val)
156
end
157
elsif resembles_rhost_value?(val)
158
append_datastore_option(datastore_options, 'RHOSTS', val)
159
else
160
print_error("Invalid argument #{val}")
161
help_cmd.call result
162
return
163
end
164
end
165
end
166
167
result
168
end
169
170
def resembles_datastore_assignment?(val)
171
return false unless val
172
173
valid_option_regex = /^(\w|::)+=.*/
174
valid_option_regex.match?(val)
175
end
176
177
def resembles_rhost_value?(val)
178
return false unless val
179
180
::Addressable::URI.parse(val)
181
true
182
rescue ::Addressable::URI::InvalidURIError => _e
183
false
184
end
185
186
def append_datastore_option(datastore_options, name, value)
187
if name.casecmp?('RHOST') || name.casecmp?('RHOSTS')
188
new_value = quote_whitespaced_value(value)
189
if !datastore_options['RHOSTS']
190
datastore_options['RHOSTS'] = new_value
191
else
192
datastore_options['RHOSTS'] = "#{datastore_options['RHOSTS']} #{new_value}"
193
end
194
else
195
datastore_options[name.upcase] = value
196
end
197
datastore_options
198
end
199
200
# Wraps values which contain spaces in quotes to ensure it's handled correctly later
201
def quote_whitespaced_value(val)
202
val.include?(' ') ? "\"#{val}\"" : val
203
end
204
end
205
end
206
end
207
end
208
209