Remoce initial rhost http url attempt

This commit is contained in:
Alan Foster 2021-05-24 00:29:29 +01:00
parent 0f433cf2ef
commit c84b651ca6
No known key found for this signature in database
GPG Key ID: 3BD4FA3818818F04
21 changed files with 79 additions and 274 deletions

View File

@ -43,23 +43,14 @@ class DataStore < Hash
end
end
if v.is_a? Hash
v.each { |key, value| self[key] = value }
else
super(k,v)
end
super(k,v)
end
#
# Case-insensitive wrapper around hash lookup
#
def [](k)
k = find_key_case(k)
if options[k].respond_to? :calculate_value
options[k].calculate_value(self)
else
super(k)
end
super(find_key_case(k))
end
#

View File

@ -32,10 +32,6 @@ module Exploit::Remote::HttpClient
], 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',

View File

@ -19,11 +19,6 @@ module Msf
name: WRAPPED_TABLES,
description: 'When enabled Metasploit will wordwrap all tables to fit into the available terminal width',
default_value: true
}.freeze,
{
name: 'RHOST_HTTP_URL',
description: 'When enabled in supported modules you can specify a URL as a target',
default_value: false
}.freeze
].freeze

View File

@ -13,83 +13,88 @@ 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)
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 ])
'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.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
@ -108,7 +113,6 @@ module Msf
Proxies = Proxies()
RHOST = RHOST()
RHOSTS = RHOSTS()
RHOST_HTTP_URL = RHOST_HTTP_URL()
RPORT = RPORT()
SSLVersion = SSLVersion()
end

View File

@ -1,88 +0,0 @@
# -*- coding: binary -*-
module Msf
###
#
# RHOST URL option.
#
###
class OptHTTPRhostURL < OptBase
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['VHOST'] || 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
value = calculate_value(value) if value.is_a? Hash
return unless single_rhost?(value)
value = 'http://' + value unless value.start_with?(%r{https?://})
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

View File

@ -18,7 +18,6 @@ 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'
#
# The options purpose in life is to associate named options with arbitrary

View File

@ -81,7 +81,6 @@ class MsfAutoload
def custom_inflections
{
'opt_http_rhost_url' => 'OptHTTPRhostURL',
'uuid' => 'UUID',
'db_manager' => 'DBManager',
'ci' => 'CI',

View File

@ -4,12 +4,9 @@ require 'metasploit/framework/aws/client'
RSpec.describe Metasploit::Framework::Aws::Client do
subject do
mod_klass = Class.new(Msf::Auxiliary) do
s = Class.new(Msf::Auxiliary) do
include Metasploit::Framework::Aws::Client
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
s = mod_klass.new
end.new
s.datastore['Region'] = 'us-east-1'
s.datastore['RHOST'] = '127.0.0.1'
s

View File

@ -3,12 +3,10 @@ require 'spec_helper'
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::Base do
subject do
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
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
end
describe "#deploy" do

View File

@ -3,12 +3,10 @@ require 'spec_helper'
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::BeanShellScripts do
subject do
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
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
end
describe "#generate_bsh" do

View File

@ -4,12 +4,10 @@ require 'spec_helper'
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::BeanShell do
subject do
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
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
end
before :example do

View File

@ -3,12 +3,10 @@ require 'spec_helper'
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::DeploymentFileRepositoryScripts do
subject do
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
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
end
describe "#stager_jsp_with_payload" do

View File

@ -3,12 +3,10 @@ require 'spec_helper'
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::DeploymentFileRepository do
subject do
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
mod = ::Msf::Exploit.new
mod.extend Msf::Exploit::Remote::HTTP::JBoss
mod.send(:initialize)
mod
end
let (:base_name) do

View File

@ -2,12 +2,10 @@
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Base do
subject do
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
mod = ::Msf::Exploit.new
mod.extend ::Msf::Exploit::Remote::HTTP::Joomla
mod.send(:initialize)
mod
end
let(:joomla_body) do

View File

@ -1,12 +1,10 @@
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Version do
subject do
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
mod = ::Msf::Exploit.new
mod.extend ::Msf::Exploit::Remote::HTTP::Joomla
mod.send(:initialize)
mod
end
# From /joomla/language/en-GB/en-GB.xml

View File

@ -4,12 +4,10 @@ require 'spec_helper'
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Base do
subject do
mod_klass = Class.new(::Msf::Exploit) do
include ::Msf::Exploit::Remote::HTTP::Wordpress
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
mod = ::Msf::Exploit.new
mod.extend ::Msf::Exploit::Remote::HTTP::Wordpress
mod.send(:initialize)
mod
end
describe '#wordpress_and_online?' do

View File

@ -4,12 +4,10 @@ require 'spec_helper'
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Login do
subject do
mod_klass = Class.new(::Msf::Exploit) do
include ::Msf::Exploit::Remote::HTTP::Wordpress
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
mod = ::Msf::Exploit.new
mod.extend ::Msf::Exploit::Remote::HTTP::Wordpress
mod.send(:initialize)
mod
end
describe '#wordpress_login' do

View File

@ -4,12 +4,10 @@ require 'spec_helper'
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Version do
subject do
mod_klass = Class.new(::Msf::Exploit) do
include ::Msf::Exploit::Remote::HTTP::Wordpress
end
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
mod = ::Msf::Exploit.new
mod.extend ::Msf::Exploit::Remote::HTTP::Wordpress
mod.send(:initialize)
mod
end
describe '#wordpress_version' do

View File

@ -1,57 +0,0 @@
RSpec.describe Msf::OptHTTPRhostURL do
subject(:opt) { described_class.new('RHOST_HTTP_URL') }
valid_values = [
{ value: 'http://example.com', normalized: { 'RHOSTS' => 'example.com', 'RPORT' => 80, 'SSL' => false, 'TARGETURI' => '/', 'URI' => '/', 'VHOST' => 'example.com', 'HttpUsername' => '', 'HttpPassword' => '' } },
{ value: 'https://example.com', normalized: { 'RHOSTS' => 'example.com', 'RPORT' => 443, 'SSL' => true, 'TARGETURI' => '/', 'URI' => '/', 'VHOST' => 'example.com', 'HttpUsername' => '', 'HttpPassword' => '' } },
{ value: 'example.com', normalized: { 'RHOSTS' => 'example.com', 'RPORT' => 80, 'SSL' => false, 'TARGETURI' => '/', 'URI' => '/', 'VHOST' => 'example.com', 'HttpUsername' => '', 'HttpPassword' => '' } },
{ value: 'http://user:pass@example.com:1234/somePath', normalized: { 'RHOSTS' => 'example.com', 'RPORT' => 1234, 'SSL' => false, 'TARGETURI' => '/somePath', 'URI' => '/somePath', 'VHOST' => 'example.com', 'HttpUsername' => 'user', 'HttpPassword' => 'pass' } },
{ value: 'http://127.0.0.1', normalized: { 'RHOSTS' => '127.0.0.1', 'RPORT' => 80, 'SSL' => false, 'TARGETURI' => '/', 'URI' => '/', 'VHOST' => nil, 'HttpUsername' => '', 'HttpPassword' => '' } }
]
invalid_values = [
{ value: '192.0.2.0/24' },
{ value: '192.0.2.0-255' },
{ value: '192.0.2.0,1-255' },
{ value: '192.0.2.*' },
{ value: '192.0.2.0-192.0.2.255' }
]
it_behaves_like 'an option', valid_values, invalid_values, 'rhost http url'
describe '#calculate_value' do
[
{ expected_url: 'http://example.com', datastore: { 'RHOSTS' => 'example.com', 'RPORT' => 80, 'SSL' => false, 'TARGETURI' => '', 'URI' => '', 'VHOST' => 'example.com', 'HttpUsername' => '', 'HttpPassword' => '' } },
{ expected_url: 'https://example.com', datastore: { 'RHOSTS' => 'example.com', 'RPORT' => 443, 'SSL' => true, 'TARGETURI' => '', 'URI' => '', 'VHOST' => 'example.com', 'HttpUsername' => '', 'HttpPassword' => '' } },
{ expected_url: 'http://user:pass@example.com:1234/somePath', datastore: { 'RHOSTS' => 'example.com', 'RPORT' => 1234, 'SSL' => false, 'TARGETURI' => '/somePath', 'URI' => '/somePath', 'VHOST' => 'example.com', 'HttpUsername' => 'user', 'HttpPassword' => 'pass' } },
{ expected_url: 'http://127.0.0.1', datastore: { 'RHOSTS' => '127.0.0.1', 'RPORT' => 80, 'SSL' => false, 'TARGETURI' => '', 'URI' => '', 'VHOST' => nil, 'HttpUsername' => '', 'HttpPassword' => '' } },
{ expected_url: 'http://example.com', datastore: { 'RHOSTS' => '127.0.0.1', 'RPORT' => 80, 'SSL' => false, 'TARGETURI' => '', 'URI' => '', 'VHOST' => 'example.com', 'HttpUsername' => '', 'HttpPassword' => '' } } ].each do |test|
context test[:datastore].to_s do
it "should return #{test[:expected_url]}" do
expect(subject.calculate_value(test[:datastore])).to eq(test[:expected_url])
end
end
end
end
describe '#valid?' do
[
{ expected: true, value: { 'VHOST' => nil, 'RHOSTS' => '127.0.0.1', 'RPORT' => 80, 'SSL' => false, 'TARGETURI' => '/', 'URI' => '/', 'HttpUsername' => '', 'HttpPassword' => '' } },
{ expected: true, value: 'google.com' },
{ expected: true, value: 'https://google.com' },
{ expected: true, value: '127.0.0.1' },
{ expected: true, value: 'http://127.0.0.1/' },
{ expected: true, value: nil }, # RHOST_HTTP_URL does not have to be set, so nil should return true.
{ expected: false, value: { 'VHOST' => nil, 'RHOSTS' => '', 'RPORT' => 80, 'SSL' => false, 'TARGETURI' => '/', 'URI' => '/', 'HttpUsername' => '', 'HttpPassword' => '' } },
{ expected: false, value: {} },
{ expected: false, value: '' }
].each do |test|
context test[:value].to_s do
it "should return #{test[:expected]}" do
expect(subject.valid?(test[:value])).to eq(test[:expected])
end
end
end
end
end

View File

@ -54,10 +54,7 @@ RSpec.describe Md5LookupUtility do
end
subject do
mod_klass = Md5LookupUtility::Md5Lookup
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new
Md5LookupUtility::Md5Lookup.new
end
def set_expected_response(body)
@ -255,11 +252,7 @@ RSpec.describe Md5LookupUtility do
describe '#get_hash_results' do
context 'when a hash is found' do
it 'yields a result' do
mod_klass = Md5LookupUtility::Md5Lookup
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
search_engine = mod_klass.new
search_engine = Md5LookupUtility::Md5Lookup.new
allow(search_engine).to receive(:lookup).and_return(good_result)
allow(Md5LookupUtility::Md5Lookup).to receive(:new).and_return(search_engine)

View File

@ -86,11 +86,7 @@ RSpec.describe VirusTotalUtility do
let(:vt) do
file = double(File, read: malware_data)
allow(File).to receive(:open).with(filename, 'rb') {|&block| block.yield file}
mod_klass = VirusTotalUtility::VirusTotal
features = instance_double(Msf::FeatureManager, enabled?: false)
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
mod_klass.new({'api_key'=>api_key, 'sample'=>filename})
VirusTotalUtility::VirusTotal.new({'api_key'=>api_key, 'sample'=>filename})
end
context ".Initializer" do