-
Notifications
You must be signed in to change notification settings - Fork 14.2k
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
Adding an option to specify a target URL #13766
Conversation
lib/msf/core/opt_rhost_url.rb
Outdated
# The datastore uses both `TARGETURI` and `URI` to denote the path of a URL, we try both here and fall back to `/` | ||
uri.path=(datastore['TARGETURI'] || datastore['URI'] || '/') | ||
uri.user=datastore['HttpUsername'] | ||
uri.password=datastore['HttpPassword'] if uri.user |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the protocol's getting dropped at the minute, can we slice in the scheme too? 👀
> set RHOST_URL http://google.com
RHOST_URL => //google.com:80
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not in a reliable way that I was able to come up with no
see we aren't actually storing the scheme on our end, we'd have to sort of reverse engineer the scheme from the provided options but there's no guarantee we'd get it right, we could end up having the user set a url then immediately be showing them one with a different scheme
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what I could do though is slice off the leading //
that would solve the problem I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's an interesting discussion point. The original RFC made notes about different schemes too - such as ftp
, postgres
, smb
, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless you think it'll cause more confusion not having those formats work from the get go I still think this current iteration would benefit a majority of users and we can always add mroe use cases down the line when it becomes apparent they are warranted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just keeping an open mind for now, discussing how the different implementations could impact various workflows. We want to reduce the possibility of confusing users in the initial release, and avoiding potential confusion for when we do support more use cases in the future. Finding the right balance between usable, shippable, and iterable will be good 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if things would become easier if this was renamed from the current generic name to something more specific like OptHttpUrl
, that way the correct protocol scheme is easy to infer 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alan fights for the users.
@dwelch-r7 Are there any additional edge cases to consider? For instance not having a module loaded, setting RHOST_URL, then loading the module and running it? Would that cause any bugs or unexpected weirdness?
Or running a module without setting any url information:
Which I think will cause a crash with the current implementation |
uhhh yea looks like you're right, I did not realise it was trying to validate it on setting and when running the module, I'll figure something out |
Spotted another edgecase:
Looks like it doesn't work well for modules that require a |
Pushed up a fix for that, I had that in there at one point and removed it for some reason |
@dwelch-r7 I think I was expecting semantics similar to curl which handles that scenario gracefully:
|
We should double check the ability to check this works with |
# Sets a name to a value in a context aware environment. | ||
# | ||
def set_option(name, value, global: false, append: false) | ||
|
||
# Determine which data store we're operating on | ||
if active_module and !global | ||
datastore = active_module.datastore | ||
else | ||
global = true | ||
datastore = self.framework.datastore | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Sets a name to a value in a context aware environment. | |
# | |
def set_option(name, value, global: false, append: false) | |
# Determine which data store we're operating on | |
if active_module and !global | |
datastore = active_module.datastore | |
else | |
global = true | |
datastore = self.framework.datastore | |
end | |
# Sets a name to a value in a context-aware environment. | |
# | |
def set_option(name, value, global: false, append: false) | |
# Determine which datastore we're operating on | |
if active_module && !global | |
datastore = active_module.datastore | |
else | |
global = true | |
datastore = framework.datastore | |
end |
elsif (args.length == 1) | ||
if (not datastore[args[0]].nil?) | ||
elsif args.length == 1 | ||
if not datastore[args[0]].nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not datastore[args[0]].nil? | |
if datastore[args[0]] |
Msf::Serializer::ReadableText.dump_datastore( | ||
(global) ? "Global" : "Module: #{active_module.refname}", | ||
datastore) + "\n") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't tell what this formatting change is for.
Msf::Serializer::ReadableText.dump_datastore( | ||
"Global", framework.datastore)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't tell what this formatting change is for.
lib/msf/core/option_container.rb
Outdated
@@ -18,6 +18,7 @@ module Msf | |||
autoload :OptRaw, 'msf/core/opt_raw' | |||
autoload :OptRegexp, 'msf/core/opt_regexp' | |||
autoload :OptString, 'msf/core/opt_string' | |||
autoload :OptRhostUrl, 'msf/core/opt_rhost_url' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
autoload :OptRhostUrl, 'msf/core/opt_rhost_url' | |
autoload :OptRhostURL, 'msf/core/opt_rhost_url' |
https://github.com/rubocop-hq/ruby-style-guide#camelcase-for-classes
if v.is_a? Hash | ||
v.each { |key, value| self[key] = value } | ||
else | ||
super(k,v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super(k,v) | |
super(k, v) |
end | ||
|
||
# | ||
# Case-insensitive wrapper around hash lookup | ||
# | ||
def [](k) | ||
super(find_key_case(k)) | ||
k = find_key_case(k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k = find_key_case(k) | |
k = find_key_case(k) | |
lib/msf/core/opt.rb
Outdated
@@ -62,6 +62,10 @@ def self.SSLVersion | |||
) | |||
end | |||
|
|||
def self.RHOST_URL(default=nil, required=false, desc="The target URL, only applicable if there is a single URL") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def self.RHOST_URL(default=nil, required=false, desc="The target URL, only applicable if there is a single URL") | |
def self.RHOST_URL(default = nil, required = false, desc = 'The target URL, only applicable if there is a single URL') |
https://github.com/rubocop-hq/ruby-style-guide#spaces-around-equals
lib/msf/core/opt.rb
Outdated
@@ -62,6 +62,10 @@ def self.SSLVersion | |||
) | |||
end | |||
|
|||
def self.RHOST_URL(default=nil, required=false, desc="The target URL, only applicable if there is a single URL") | |||
Msf::OptRhostUrl.new(__method__.to_s, [ required, desc, default ]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/msf/core/opt_rhost_url.rb
Outdated
# RHOST URL option. | ||
# | ||
### | ||
class OptRhostUrl < OptBase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/msf/core/opt_rhost_url.rb
Outdated
'rhost url' | ||
end | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/msf/core/opt_rhost_url.rb
Outdated
|
||
option_hash['RHOSTS'] = uri.hostname | ||
option_hash['RPORT'] = uri.port | ||
option_hash['SSL'] = %w{ssl https}.include?(uri.scheme) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
option_hash['SSL'] = %w{ssl https}.include?(uri.scheme) | |
option_hash['SSL'] = %w[ssl https].include?(uri.scheme) |
https://github.com/rubocop-hq/ruby-style-guide#percent-literal-braces
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
During module hacking we discussed just running Rubocop over this file in its entirety, there's a things to lint such as a few unneeded uses of and
etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good.
lib/msf/core/opt_rhost_url.rb
Outdated
option_hash['TARGETURI'] = uri.path || '/' | ||
option_hash['URI'] = uri.path || '/' | ||
|
||
if uri.scheme and %{http https}.include?(uri.scheme) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if uri.scheme and %{http https}.include?(uri.scheme) | |
if uri.scheme && %[http https].include?(uri.scheme) |
https://github.com/rapid7/metasploit-framework/pull/13766/files#r451880965
lib/msf/core/opt_rhost_url.rb
Outdated
def valid?(value, check_empty: false) | ||
return true unless value | ||
uri = get_uri(value) | ||
false unless uri.host != nil and uri.port != nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
false unless uri.host != nil and uri.port != nil | |
return false if uri.host.nil? && uri.port.nil? |
Did you mean to return
? What happens with the super
?
lib/msf/core/opt_rhost_url.rb
Outdated
def get_uri(value) | ||
begin | ||
uri = URI(value) | ||
rescue URI::InvalidURIError | ||
uri = URI('//' + value) | ||
end | ||
uri | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def get_uri(value) | |
begin | |
uri = URI(value) | |
rescue URI::InvalidURIError | |
uri = URI('//' + value) | |
end | |
uri | |
end | |
def get_uri(value) | |
URI(value) | |
rescue URI::InvalidURIError | |
URI("//#{value}") | |
end |
https://github.com/rubocop-hq/ruby-style-guide#implicit-begin
lib/msf/core/opt_rhost_url.rb
Outdated
end | ||
|
||
def calculate_value(datastore) | ||
if datastore['RHOSTS'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard clause, please?
https://github.com/rubocop-hq/ruby-style-guide#nested-conditionals
lib/msf/core/opt_rhost_url.rb
Outdated
|
||
def calculate_value(datastore) | ||
if datastore['RHOSTS'] | ||
begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be a good idea to use RuboCop.
6578c63
to
1a61308
Compare
# | ||
# Sets a name to a value in a context aware environment. | ||
# | ||
def set_option(name, value, global: false, append: false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we revert this file now? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dwelch-r7 Just confirming if this file can be reverted or not 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not needed for these changes no, but I think they're good changes, I can revert and put it up as a separate PR, that probably makes more sense
lib/msf/core/exploit/http/client.rb
Outdated
@@ -22,6 +22,7 @@ def initialize(info = {}) | |||
|
|||
register_options( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a feature flag for this by modifying the feature manager's DEFAULTS
and conditionally checking the include:
if framework.features.enabled?("feature_name")
register_options([OPT::RHOST_URL])
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind pulling the latest of the #13833 PR to get the latest features API
cdea10f
to
fbd0d0e
Compare
This PR is for an additional option that allows users to specify a URL (i.e.
set RHOST_URL https://example.com/path
) rather than setting each individual option that is the current workflow (i.e.set RHOSTS example.com
,set RPORT 443
,set SSL true
... etc)For full details on the various approaches to adding URL Support to Metasploit:
https://github.com/rapid7/metasploit-framework/wiki/RFC---Metasploit-URL-support
I currently only have the new option added to
Exploit::Remote::HttpClient
but I'm sure there are a few other places it also belongs, more than happy to add it in anywhere that makes senseHere is an example of the setting in action:

The reverse is also true, i.e. you can set multiple individual values and the RHOST_URL will be displayed for you:

Http username and password is also supported:

Some things to note with the current implementation:
Only single URLs may be specified (In other words you are not able specify an address range in the new option), attempting this will blank out the RHOST_URL option, but any previous options that were set will remain.

URLs of the format

example.com
notably missing the scheme are not supported, you must do//example.com:<port>
or even betterhttps://example.com
To sum up, if this feature is to be added users would be able to simply input
set RHOST_URL
and paste in their target rather than having to set anywhere up to 6 different options depending on what that particular module cared about.