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

Adds rhost url support behind a feature flag #13961

Merged
merged 1 commit into from
Aug 19, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 11 additions & 2 deletions lib/msf/core/data_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,23 @@ def []=(k, v)
end
end

super(k,v)
if v.is_a? Hash
v.each { |key, value| self[key] = value }
else
super(k,v)
end
end

#
# Case-insensitive wrapper around hash lookup
#
def [](k)
super(find_key_case(k))
k = find_key_case(k)
if options[k].respond_to? :calculate_value
options[k].calculate_value(self)
else
super(k)
end
end

#
Expand Down
4 changes: 4 additions & 0 deletions lib/msf/core/exploit/http/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def initialize(info = {})
], self.class
)

if framework.features.enabled?("RHOST_HTTP_URL")
register_options([Opt::RHOST_HTTP_URL])
end

register_advanced_options(
[
OptString.new('UserAgent', [false, 'The User-Agent header to use for all requests',
Expand Down
5 changes: 5 additions & 0 deletions lib/msf/core/feature_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ class FeatureManager
name: 'wrapped_tables',
description: 'When enabled Metasploit will wordwrap all tables to fit into the available terminal width',
default_value: false
}.freeze,
{
name: 'RHOST_HTTP_URL',
description: 'When enabled in supported modules you can specify a URL as a target',
default_value: false
}.freeze
].freeze

Expand Down
60 changes: 28 additions & 32 deletions lib/msf/core/opt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,88 +14,83 @@ module Msf
# register_advanced_options([Opt::Proxies])
#
module Opt

# @return [OptAddress]
def self.CHOST(default=nil, required=false, desc="The local client address")
def self.CHOST(default = nil, required = false, desc = 'The local client address')
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
end

# @return [OptPort]
def self.CPORT(default=nil, required=false, desc="The local client port")
def self.CPORT(default = nil, required = false, desc = 'The local client port')
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
end

# @return [OptAddressLocal]
def self.LHOST(default=nil, required=true, desc="The listen address (an interface may be specified)")
def self.LHOST(default = nil, required = true, desc = 'The listen address (an interface may be specified)')
Msf::OptAddressLocal.new(__method__.to_s, [ required, desc, default ])
end

# @return [OptPort]
def self.LPORT(default=nil, required=true, desc="The listen port")
def self.LPORT(default = nil, required = true, desc = 'The listen port')
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
end

# @return [OptString]
def self.Proxies(default=nil, required=false, desc="A proxy chain of format type:host:port[,type:host:port][...]")
def self.Proxies(default = nil, required = false, desc = 'A proxy chain of format type:host:port[,type:host:port][...]')
Msf::OptString.new(__method__.to_s, [ required, desc, default ])
end

# @return [OptAddressRange]
def self.RHOSTS(default=nil, required=true, desc="The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'")
def self.RHOSTS(default = nil, required = true, desc = "The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'")
Msf::OptAddressRange.new('RHOSTS', [ required, desc, default ])
end

def self.RHOST(default=nil, required=true, desc="The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'")
def self.RHOST(default = nil, required = true, desc = "The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'")
Msf::OptAddressRange.new('RHOSTS', [ required, desc, default ], aliases: [ 'RHOST' ])
end

# @return [OptPort]
def self.RPORT(default=nil, required=true, desc="The target port")
def self.RPORT(default = nil, required = true, desc = 'The target port')
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
end

# @return [OptEnum]
def self.SSLVersion
Msf::OptEnum.new('SSLVersion',
'Specify the version of SSL/TLS to be used (Auto, TLS and SSL23 are auto-negotiate)',
enums: Rex::Socket::SslTcp.supported_ssl_methods
)
'Specify the version of SSL/TLS to be used (Auto, TLS and SSL23 are auto-negotiate)',
enums: Rex::Socket::SslTcp.supported_ssl_methods)
end

def self.RHOST_HTTP_URL(default = nil, required = false, desc = 'The target URL, only applicable if there is a single URL')
Msf::OptHTTPRhostURL.new(__method__.to_s, [required, desc, default ])
end

def self.stager_retry_options
[
OptInt.new('StagerRetryCount',
'The number of times the stager should retry if the first connect fails',
default: 10,
aliases: ['ReverseConnectRetries']
),
'The number of times the stager should retry if the first connect fails',
default: 10,
aliases: ['ReverseConnectRetries']),
OptInt.new('StagerRetryWait',
'Number of seconds to wait for the stager between reconnect attempts',
default: 5
)
'Number of seconds to wait for the stager between reconnect attempts',
default: 5)
]
end

def self.http_proxy_options
[
OptString.new('HttpProxyHost', 'An optional proxy server IP address or hostname',
aliases: ['PayloadProxyHost']
),
aliases: ['PayloadProxyHost']),
OptPort.new('HttpProxyPort', 'An optional proxy server port',
aliases: ['PayloadProxyPort']
),
aliases: ['PayloadProxyPort']),
OptString.new('HttpProxyUser', 'An optional proxy server username',
aliases: ['PayloadProxyUser'],
max_length: Rex::Payloads::Meterpreter::Config::PROXY_USER_SIZE - 1
),
aliases: ['PayloadProxyUser'],
max_length: Rex::Payloads::Meterpreter::Config::PROXY_USER_SIZE - 1),
OptString.new('HttpProxyPass', 'An optional proxy server password',
aliases: ['PayloadProxyPass'],
max_length: Rex::Payloads::Meterpreter::Config::PROXY_PASS_SIZE - 1
),
aliases: ['PayloadProxyPass'],
max_length: Rex::Payloads::Meterpreter::Config::PROXY_PASS_SIZE - 1),
OptEnum.new('HttpProxyType', 'The type of HTTP proxy',
enums: ['HTTP', 'SOCKS'],
aliases: ['PayloadProxyType']
)
enums: ['HTTP', 'SOCKS'],
aliases: ['PayloadProxyType'])
]
end

Expand All @@ -114,6 +109,7 @@ def self.http_header_options
Proxies = Proxies()
RHOST = RHOST()
RHOSTS = RHOSTS()
RHOST_HTTP_URL = RHOST_HTTP_URL()
RPORT = RPORT()
SSLVersion = SSLVersion()
end
Expand Down
87 changes: 87 additions & 0 deletions lib/msf/core/opt_http_rhost_url.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# -*- coding: binary -*-

module Msf
###
#
# RHOST URL option.
#
###
class OptHTTPRhostURL < OptBase
adfoster-r7 marked this conversation as resolved.
Show resolved Hide resolved
def type
'rhost http url'
end

def normalize(value)
return unless value

uri = get_uri(value)
return unless uri

option_hash = {}
# Blank this out since we don't know if this new value will have a `VHOST` to ensure we remove the old value
option_hash['VHOST'] = nil

option_hash['RHOSTS'] = uri.hostname
option_hash['RPORT'] = uri.port
option_hash['SSL'] = %w[ssl https].include?(uri.scheme)

# Both `TARGETURI` and `URI` are used as datastore options to denote the path on a uri
option_hash['TARGETURI'] = uri.path.present? ? uri.path : '/'
option_hash['URI'] = option_hash['TARGETURI']

if uri.scheme && %(http https).include?(uri.scheme)
option_hash['VHOST'] = uri.hostname unless Rex::Socket.is_ip_addr?(uri.hostname)
option_hash['HttpUsername'] = uri.user.to_s
option_hash['HttpPassword'] = uri.password.to_s
end

option_hash
end

def valid?(value, check_empty: false)
return true unless value || required

uri = get_uri(value)
return false unless uri && !uri.host.nil? && !uri.port.nil?

super
end

def calculate_value(datastore)
return unless datastore['RHOSTS']
begin
uri_type = datastore['SSL'] ? URI::HTTPS : URI::HTTP
uri = uri_type.build(host: datastore['RHOSTS'])
uri.port = datastore['RPORT']
# 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
uri.to_s
rescue URI::InvalidComponentError
nil
end
end

protected

def get_uri(value)
return unless value
return unless single_rhost?(value)

value = 'http://' + value unless value.start_with?(%r{https?://})
adfoster-r7 marked this conversation as resolved.
Show resolved Hide resolved
URI(value)
rescue URI::InvalidURIError
nil
end

def single_rhost?(value)
return true if value =~ /[^-0-9,.*\/]/
walker = Rex::Socket::RangeWalker.new(value)
return false unless walker.valid?
# if there is only a single ip then it's not a range
walker.length == 1
end

end
end
2 changes: 1 addition & 1 deletion lib/msf/core/opt_raw.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def normalize(value)
value
end

def valid?(value=self.value)
def valid?(value=self.value, check_empty: true)
dwelch-r7 marked this conversation as resolved.
Show resolved Hide resolved
value = normalize(value)
return false if empty_required_value?(value)
return super
Expand Down
1 change: 1 addition & 0 deletions lib/msf/core/option_container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 :OptHTTPRhostURL, 'msf/core/opt_http_rhost_url'
adfoster-r7 marked this conversation as resolved.
Show resolved Hide resolved

#
# The options purpose in life is to associate named options with arbitrary
Expand Down
7 changes: 5 additions & 2 deletions spec/lib/metasploit/framework/aws/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
RSpec.describe Metasploit::Framework::Aws::Client do

subject do
s = Class.new(Msf::Auxiliary) do
mod_klass = Class.new(Msf::Auxiliary) do
include Metasploit::Framework::Aws::Client
end.new
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
s = mod_klass.new
s.datastore['Region'] = 'us-east-1'
s.datastore['RHOST'] = '127.0.0.1'
s
Expand Down
10 changes: 6 additions & 4 deletions spec/lib/msf/core/exploit/http/jboss/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::Base do
subject do
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
mod_klass = Class.new(::Msf::Exploit) do
include Msf::Exploit::Remote::HTTP::JBoss
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
end

describe "#deploy" do
Expand Down
10 changes: 6 additions & 4 deletions spec/lib/msf/core/exploit/http/jboss/bean_shell_scripts_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::BeanShellScripts do
subject do
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
mod_klass = Class.new(::Msf::Exploit) do
include Msf::Exploit::Remote::HTTP::JBoss
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
end

describe "#generate_bsh" do
Expand Down
10 changes: 6 additions & 4 deletions spec/lib/msf/core/exploit/http/jboss/bean_shell_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::BeanShell do

subject do
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
mod_klass = Class.new(::Msf::Exploit) do
include Msf::Exploit::Remote::HTTP::JBoss
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
end

before :example do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::DeploymentFileRepositoryScripts do
subject do
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
mod_klass = Class.new(::Msf::Exploit) do
include Msf::Exploit::Remote::HTTP::JBoss
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
end

describe "#stager_jsp_with_payload" do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::DeploymentFileRepository do
subject do
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
mod_klass = Class.new(::Msf::Exploit) do
include Msf::Exploit::Remote::HTTP::JBoss
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
end

let (:base_name) do
Expand Down
10 changes: 6 additions & 4 deletions spec/lib/msf/core/exploit/http/joomla/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Base do

subject do
mod = ::Msf::Exploit.new
mod.extend ::Msf::Exploit::Remote::HTTP::Joomla
mod.send(:initialize)
mod
mod_klass = Class.new(::Msf::Exploit) do
include ::Msf::Exploit::Remote::HTTP::Joomla
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
end

let(:joomla_body) do
Expand Down
Loading