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

Adding an option to specify a target URL #13766

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 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
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ source 'https://rubygems.org'
gemspec name: 'metasploit-framework'

gem 'sqlite3', '~>1.3.0'
# gem 'rex-text', path: '../rex-text'
gem 'rex-text', git: 'https://github.com/adfoster-r7/rex-text', branch: 'add-word-wrapping-to-rex-tables'

# separate from test as simplecov is not run on travis-ci
group :coverage do
Expand Down
9 changes: 8 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
GIT
remote: https://github.com/adfoster-r7/rex-text
revision: f7990ab39b8a347b0a16d2db9c52ea227b332457
branch: add-word-wrapping-to-rex-tables
specs:
rex-text (0.2.27)

PATH
remote: .
specs:
Expand Down Expand Up @@ -347,7 +354,6 @@ GEM
rex-socket
rex-text
rex-struct2 (0.1.2)
rex-text (0.2.26)
rex-zip (0.1.3)
rex-text
rexml (3.2.4)
Expand Down Expand Up @@ -448,6 +454,7 @@ DEPENDENCIES
pry-byebug
rake
redcarpet
rex-text!
rspec-rails
rspec-rerun
rubocop (= 0.86.0)
Expand Down
1 change: 1 addition & 0 deletions lib/msf/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module Msf

# Framework context and core classes
require 'msf/core/framework'
require 'msf/core/feature_manager'
require 'msf/core/db_manager'
require 'msf/core/event_dispatcher'
require 'msf/core/module_manager'
Expand Down
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
super(k,v)
super(k, v)

end
end

#
# Case-insensitive wrapper around hash lookup
#
def [](k)
super(find_key_case(k))
k = find_key_case(k)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
k = 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
91 changes: 91 additions & 0 deletions lib/msf/core/feature_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# -*- coding: binary -*-
# frozen_string_literal: true

require 'msf/core/plugin'

module Msf
###
#
# The feature manager is responsible for managing feature flags that can change characteristics of framework.
# Each feature will have a default value. The user can choose to override this default value if they wish.
###
class FeatureManager

include Singleton

CONFIG_KEY = 'framework/features'
WRAPPED_TABLES = 'wrapped_tables'
DEFAULTS = [
{
name: 'wrapped_tables',
description: 'When enabled Metasploit will wordwrap all tables to fit into the available terminal width',
default_value: false
}.freeze
].freeze

#
# Initializes the feature manager.
#
def initialize
@flag_lookup = DEFAULTS.each_with_object({}) do |feature, acc|
key = feature[:name]
acc[key] = feature.dup
end
end

def all
@flag_lookup.values.map do |feature|
feature.slice(:name, :description).merge(enabled: enabled?(feature[:name]))
end
end

def enabled?(name)
return false unless @flag_lookup[name]

feature = @flag_lookup[name]
feature.key?(:user_preference) ? feature[:user_preference] : feature[:default_value]
end

def exists?(name)
@flag_lookup.key?(name)
end

def names
all.map { |feature| feature[:name] }
end

def set(name, value)
return false unless @flag_lookup[name]

@flag_lookup[name][:user_preference] = value

if name == WRAPPED_TABLES
if value
Rex::Text::Table.wrap_tables!
else
Rex::Text::Table.unwrap_tables!
end
end
end

def load_config
conf = Msf::Config.load
conf.fetch(CONFIG_KEY, {}).each do |name, value|
set(name, value == 'true')
end
end

def save_config
# Note, we intentionally omit features that have not explicitly been set by the user.
config = Msf::Config.load
old_config = config.fetch(CONFIG_KEY, {})
new_config = @flag_lookup.values.each_with_object(old_config) do |feature, config|
next unless feature.key?(:user_preference)

config.merge!(feature[:name] => feature[:user_preference].to_s)
end

Msf::Config.save(CONFIG_KEY => new_config)
end
end
end
7 changes: 7 additions & 0 deletions lib/msf/core/framework.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def initialize(options={})
self.analyze = Analyze.new(self)
self.plugins = PluginManager.new(self)
self.browser_profiles = Hash.new
self.features = FeatureManager.instance

# Configure the thread factory
Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: self)
Expand Down Expand Up @@ -195,6 +196,11 @@ def version
# framework objects to offer related objects/actions available.
#
attr_reader :analyze
#
# The framework instance's feature manager. The feature manager is responsible
# for configuring feature flags that can change characteristics of framework.
#
attr_reader :features

#
# The framework instance's dependency
Expand Down Expand Up @@ -277,6 +283,7 @@ def search(match, logger: nil)
attr_writer :db # :nodoc:
attr_writer :browser_profiles # :nodoc:
attr_writer :analyze # :nodoc:
attr_writer :features # :nodoc:

private

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
Loading