Adds rhost url support behind a feature flag
Tidy up test Return a string instead of a URI object Code review comments Rubcocop
This commit is contained in:
parent
3fcdbd9402
commit
e7061439ef
|
@ -43,14 +43,23 @@ class DataStore < Hash
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
super(k,v)
|
if v.is_a? Hash
|
||||||
|
v.each { |key, value| self[key] = value }
|
||||||
|
else
|
||||||
|
super(k,v)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Case-insensitive wrapper around hash lookup
|
# Case-insensitive wrapper around hash lookup
|
||||||
#
|
#
|
||||||
def [](k)
|
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
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -30,6 +30,10 @@ module Exploit::Remote::HttpClient
|
||||||
], self.class
|
], self.class
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if framework.features.enabled?("RHOST_HTTP_URL")
|
||||||
|
register_options([Opt::RHOST_HTTP_URL])
|
||||||
|
end
|
||||||
|
|
||||||
register_advanced_options(
|
register_advanced_options(
|
||||||
[
|
[
|
||||||
OptString.new('UserAgent', [false, 'The User-Agent header to use for all requests',
|
OptString.new('UserAgent', [false, 'The User-Agent header to use for all requests',
|
||||||
|
|
|
@ -20,6 +20,11 @@ module Msf
|
||||||
name: 'wrapped_tables',
|
name: 'wrapped_tables',
|
||||||
description: 'When enabled Metasploit will wordwrap all tables to fit into the available terminal width',
|
description: 'When enabled Metasploit will wordwrap all tables to fit into the available terminal width',
|
||||||
default_value: false
|
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
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
|
|
|
@ -14,88 +14,83 @@ module Msf
|
||||||
# register_advanced_options([Opt::Proxies])
|
# register_advanced_options([Opt::Proxies])
|
||||||
#
|
#
|
||||||
module Opt
|
module Opt
|
||||||
|
|
||||||
# @return [OptAddress]
|
# @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 ])
|
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [OptPort]
|
# @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 ])
|
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [OptAddressLocal]
|
# @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 ])
|
Msf::OptAddressLocal.new(__method__.to_s, [ required, desc, default ])
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [OptPort]
|
# @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 ])
|
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [OptString]
|
# @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 ])
|
Msf::OptString.new(__method__.to_s, [ required, desc, default ])
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [OptAddressRange]
|
# @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 ])
|
Msf::OptAddressRange.new('RHOSTS', [ required, desc, default ])
|
||||||
end
|
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' ])
|
Msf::OptAddressRange.new('RHOSTS', [ required, desc, default ], aliases: [ 'RHOST' ])
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [OptPort]
|
# @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 ])
|
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [OptEnum]
|
# @return [OptEnum]
|
||||||
def self.SSLVersion
|
def self.SSLVersion
|
||||||
Msf::OptEnum.new('SSLVersion',
|
Msf::OptEnum.new('SSLVersion',
|
||||||
'Specify the version of SSL/TLS to be used (Auto, TLS and SSL23 are auto-negotiate)',
|
'Specify the version of SSL/TLS to be used (Auto, TLS and SSL23 are auto-negotiate)',
|
||||||
enums: Rex::Socket::SslTcp.supported_ssl_methods
|
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
|
end
|
||||||
|
|
||||||
def self.stager_retry_options
|
def self.stager_retry_options
|
||||||
[
|
[
|
||||||
OptInt.new('StagerRetryCount',
|
OptInt.new('StagerRetryCount',
|
||||||
'The number of times the stager should retry if the first connect fails',
|
'The number of times the stager should retry if the first connect fails',
|
||||||
default: 10,
|
default: 10,
|
||||||
aliases: ['ReverseConnectRetries']
|
aliases: ['ReverseConnectRetries']),
|
||||||
),
|
|
||||||
OptInt.new('StagerRetryWait',
|
OptInt.new('StagerRetryWait',
|
||||||
'Number of seconds to wait for the stager between reconnect attempts',
|
'Number of seconds to wait for the stager between reconnect attempts',
|
||||||
default: 5
|
default: 5)
|
||||||
)
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.http_proxy_options
|
def self.http_proxy_options
|
||||||
[
|
[
|
||||||
OptString.new('HttpProxyHost', 'An optional proxy server IP address or hostname',
|
OptString.new('HttpProxyHost', 'An optional proxy server IP address or hostname',
|
||||||
aliases: ['PayloadProxyHost']
|
aliases: ['PayloadProxyHost']),
|
||||||
),
|
|
||||||
OptPort.new('HttpProxyPort', 'An optional proxy server port',
|
OptPort.new('HttpProxyPort', 'An optional proxy server port',
|
||||||
aliases: ['PayloadProxyPort']
|
aliases: ['PayloadProxyPort']),
|
||||||
),
|
|
||||||
OptString.new('HttpProxyUser', 'An optional proxy server username',
|
OptString.new('HttpProxyUser', 'An optional proxy server username',
|
||||||
aliases: ['PayloadProxyUser'],
|
aliases: ['PayloadProxyUser'],
|
||||||
max_length: Rex::Payloads::Meterpreter::Config::PROXY_USER_SIZE - 1
|
max_length: Rex::Payloads::Meterpreter::Config::PROXY_USER_SIZE - 1),
|
||||||
),
|
|
||||||
OptString.new('HttpProxyPass', 'An optional proxy server password',
|
OptString.new('HttpProxyPass', 'An optional proxy server password',
|
||||||
aliases: ['PayloadProxyPass'],
|
aliases: ['PayloadProxyPass'],
|
||||||
max_length: Rex::Payloads::Meterpreter::Config::PROXY_PASS_SIZE - 1
|
max_length: Rex::Payloads::Meterpreter::Config::PROXY_PASS_SIZE - 1),
|
||||||
),
|
|
||||||
OptEnum.new('HttpProxyType', 'The type of HTTP proxy',
|
OptEnum.new('HttpProxyType', 'The type of HTTP proxy',
|
||||||
enums: ['HTTP', 'SOCKS'],
|
enums: ['HTTP', 'SOCKS'],
|
||||||
aliases: ['PayloadProxyType']
|
aliases: ['PayloadProxyType'])
|
||||||
)
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -114,6 +109,7 @@ module Msf
|
||||||
Proxies = Proxies()
|
Proxies = Proxies()
|
||||||
RHOST = RHOST()
|
RHOST = RHOST()
|
||||||
RHOSTS = RHOSTS()
|
RHOSTS = RHOSTS()
|
||||||
|
RHOST_HTTP_URL = RHOST_HTTP_URL()
|
||||||
RPORT = RPORT()
|
RPORT = RPORT()
|
||||||
SSLVersion = SSLVersion()
|
SSLVersion = SSLVersion()
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
# -*- 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['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?://})
|
||||||
|
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
|
|
@ -28,7 +28,7 @@ class OptRaw < OptBase
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid?(value=self.value)
|
def valid?(value=self.value, check_empty: true)
|
||||||
value = normalize(value)
|
value = normalize(value)
|
||||||
return false if empty_required_value?(value)
|
return false if empty_required_value?(value)
|
||||||
return super
|
return super
|
||||||
|
|
|
@ -18,6 +18,7 @@ module Msf
|
||||||
autoload :OptRaw, 'msf/core/opt_raw'
|
autoload :OptRaw, 'msf/core/opt_raw'
|
||||||
autoload :OptRegexp, 'msf/core/opt_regexp'
|
autoload :OptRegexp, 'msf/core/opt_regexp'
|
||||||
autoload :OptString, 'msf/core/opt_string'
|
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
|
# The options purpose in life is to associate named options with arbitrary
|
||||||
|
|
|
@ -4,9 +4,12 @@ require 'metasploit/framework/aws/client'
|
||||||
RSpec.describe Metasploit::Framework::Aws::Client do
|
RSpec.describe Metasploit::Framework::Aws::Client do
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
s = Class.new(Msf::Auxiliary) do
|
mod_klass = Class.new(Msf::Auxiliary) do
|
||||||
include Metasploit::Framework::Aws::Client
|
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['Region'] = 'us-east-1'
|
||||||
s.datastore['RHOST'] = '127.0.0.1'
|
s.datastore['RHOST'] = '127.0.0.1'
|
||||||
s
|
s
|
||||||
|
|
|
@ -6,10 +6,12 @@ require 'msf/core/exploit/http/jboss'
|
||||||
|
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::Base do
|
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::Base do
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend Msf::Exploit::Remote::HTTP::JBoss
|
include Msf::Exploit::Remote::HTTP::JBoss
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#deploy" do
|
describe "#deploy" do
|
||||||
|
|
|
@ -6,10 +6,12 @@ require 'msf/core/exploit/http/jboss'
|
||||||
|
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::BeanShellScripts do
|
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::BeanShellScripts do
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend Msf::Exploit::Remote::HTTP::JBoss
|
include Msf::Exploit::Remote::HTTP::JBoss
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#generate_bsh" do
|
describe "#generate_bsh" do
|
||||||
|
|
|
@ -7,10 +7,12 @@ require 'msf/core/exploit/http/jboss'
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::BeanShell do
|
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::BeanShell do
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend Msf::Exploit::Remote::HTTP::JBoss
|
include Msf::Exploit::Remote::HTTP::JBoss
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
before :example do
|
before :example do
|
||||||
|
|
|
@ -6,10 +6,12 @@ require 'msf/core/exploit/http/jboss'
|
||||||
|
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::DeploymentFileRepositoryScripts do
|
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::DeploymentFileRepositoryScripts do
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend Msf::Exploit::Remote::HTTP::JBoss
|
include Msf::Exploit::Remote::HTTP::JBoss
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#stager_jsp_with_payload" do
|
describe "#stager_jsp_with_payload" do
|
||||||
|
|
|
@ -6,10 +6,12 @@ require 'msf/core/exploit/http/jboss'
|
||||||
|
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::DeploymentFileRepository do
|
RSpec.describe Msf::Exploit::Remote::HTTP::JBoss::DeploymentFileRepository do
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend Msf::Exploit::Remote::HTTP::JBoss
|
include Msf::Exploit::Remote::HTTP::JBoss
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
let (:base_name) do
|
let (:base_name) do
|
||||||
|
|
|
@ -6,10 +6,12 @@ require 'msf/core/exploit/http/joomla'
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Base do
|
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Base do
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend ::Msf::Exploit::Remote::HTTP::Joomla
|
include ::Msf::Exploit::Remote::HTTP::Joomla
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:joomla_body) do
|
let(:joomla_body) do
|
||||||
|
|
|
@ -5,10 +5,12 @@ require 'msf/core/exploit/http/joomla'
|
||||||
|
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Version do
|
RSpec.describe Msf::Exploit::Remote::HTTP::Joomla::Version do
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend ::Msf::Exploit::Remote::HTTP::Joomla
|
include ::Msf::Exploit::Remote::HTTP::Joomla
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
# From /joomla/language/en-GB/en-GB.xml
|
# From /joomla/language/en-GB/en-GB.xml
|
||||||
|
|
|
@ -8,10 +8,12 @@ require 'msf/core/exploit/http/wordpress'
|
||||||
|
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Base do
|
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Base do
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend ::Msf::Exploit::Remote::HTTP::Wordpress
|
include ::Msf::Exploit::Remote::HTTP::Wordpress
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#wordpress_and_online?' do
|
describe '#wordpress_and_online?' do
|
||||||
|
|
|
@ -8,10 +8,12 @@ require 'msf/core/exploit/http/wordpress'
|
||||||
|
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Login do
|
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Login do
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend ::Msf::Exploit::Remote::HTTP::Wordpress
|
include ::Msf::Exploit::Remote::HTTP::Wordpress
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#wordpress_login' do
|
describe '#wordpress_login' do
|
||||||
|
|
|
@ -8,10 +8,12 @@ require 'msf/core/exploit/http/wordpress'
|
||||||
|
|
||||||
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Version do
|
RSpec.describe Msf::Exploit::Remote::HTTP::Wordpress::Version do
|
||||||
subject do
|
subject do
|
||||||
mod = ::Msf::Exploit.new
|
mod_klass = Class.new(::Msf::Exploit) do
|
||||||
mod.extend ::Msf::Exploit::Remote::HTTP::Wordpress
|
include ::Msf::Exploit::Remote::HTTP::Wordpress
|
||||||
mod.send(:initialize)
|
end
|
||||||
mod
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#wordpress_version' do
|
describe '#wordpress_version' do
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
require 'msf/core/opt_http_rhost_url'
|
||||||
|
|
||||||
|
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' => '' } }
|
||||||
|
].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
|
||||||
|
end
|
|
@ -55,7 +55,10 @@ RSpec.describe Md5LookupUtility do
|
||||||
end
|
end
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
Md5LookupUtility::Md5Lookup.new
|
mod_klass = Md5LookupUtility::Md5Lookup
|
||||||
|
features = instance_double(Msf::FeatureManager, enabled?: false)
|
||||||
|
mod_klass.framework = instance_double(Msf::Framework, features: features, datastore: {})
|
||||||
|
mod_klass.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_expected_response(body)
|
def set_expected_response(body)
|
||||||
|
@ -253,7 +256,11 @@ RSpec.describe Md5LookupUtility do
|
||||||
describe '#get_hash_results' do
|
describe '#get_hash_results' do
|
||||||
context 'when a hash is found' do
|
context 'when a hash is found' do
|
||||||
it 'yields a result' do
|
it 'yields a result' do
|
||||||
search_engine = Md5LookupUtility::Md5Lookup.new
|
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
|
||||||
|
|
||||||
allow(search_engine).to receive(:lookup).and_return(good_result)
|
allow(search_engine).to receive(:lookup).and_return(good_result)
|
||||||
allow(Md5LookupUtility::Md5Lookup).to receive(:new).and_return(search_engine)
|
allow(Md5LookupUtility::Md5Lookup).to receive(:new).and_return(search_engine)
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,11 @@ RSpec.describe VirusTotalUtility do
|
||||||
let(:vt) do
|
let(:vt) do
|
||||||
file = double(File, read: malware_data)
|
file = double(File, read: malware_data)
|
||||||
allow(File).to receive(:open).with(filename, 'rb') {|&block| block.yield file}
|
allow(File).to receive(:open).with(filename, 'rb') {|&block| block.yield file}
|
||||||
VirusTotalUtility::VirusTotal.new({'api_key'=>api_key, 'sample'=>filename})
|
|
||||||
|
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})
|
||||||
end
|
end
|
||||||
|
|
||||||
context ".Initializer" do
|
context ".Initializer" do
|
||||||
|
|
Loading…
Reference in New Issue