diff --git a/Gemfile b/Gemfile index 5f10ba8c..2906793f 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,5 @@ source 'https://rubygems.org' gemspec + +# gem 'cms_scanner', branch: 'xxx', git: 'https://github.com/wpscanteam/CMSScanner.git' diff --git a/app/controllers/core.rb b/app/controllers/core.rb index 5143da7a..ea08a6b5 100644 --- a/app/controllers/core.rb +++ b/app/controllers/core.rb @@ -27,12 +27,12 @@ module WPScan # @return [ Boolean ] def update_db_required? if local_db.missing_files? - raise Error::MissingDatabaseFile if parsed_options[:update] == false + raise Error::MissingDatabaseFile if ParsedCli.update == false return true end - return parsed_options[:update] unless parsed_options[:update].nil? + return ParsedCli.update unless ParsedCli.update.nil? return false unless user_interaction? && local_db.outdated? @@ -44,9 +44,9 @@ module WPScan def update_db output('db_update_started') - output('db_update_finished', updated: local_db.update, verbose: parsed_options[:verbose]) + output('db_update_finished', updated: local_db.update, verbose: ParsedCli.verbose) - exit(0) unless parsed_options[:url] + exit(0) unless ParsedCli.url end def before_scan @@ -61,7 +61,7 @@ module WPScan check_wordpress_state rescue Error::NotWordPress => e target.maybe_add_cookies - raise e unless target.wordpress?(parsed_options[:detection_mode]) + raise e unless target.wordpress?(ParsedCli.detection_mode) end # Raises errors if the target is hosted on wordpress.com or is not running WordPress @@ -76,7 +76,7 @@ module WPScan exit(WPScan::ExitCode::VULNERABLE) end - raise Error::NotWordPress unless target.wordpress?(parsed_options[:detection_mode]) || parsed_options[:force] + raise Error::NotWordPress unless target.wordpress?(ParsedCli.detection_mode) || ParsedCli.force end # Loads the related server module in the target @@ -88,7 +88,7 @@ module WPScan server = target.server || :Apache # Tries to auto detect the server # Force a specific server module to be loaded if supplied - case parsed_options[:server] + case ParsedCli.server when :apache server = :Apache when :iis diff --git a/app/controllers/custom_directories.rb b/app/controllers/custom_directories.rb index 1e956a5c..d7c111ae 100644 --- a/app/controllers/custom_directories.rb +++ b/app/controllers/custom_directories.rb @@ -13,8 +13,8 @@ module WPScan end def before_scan - target.content_dir = parsed_options[:wp_content_dir] if parsed_options[:wp_content_dir] - target.plugins_dir = parsed_options[:wp_plugins_dir] if parsed_options[:wp_plugins_dir] + target.content_dir = ParsedCli.wp_content_dir if ParsedCli.wp_content_dir + target.plugins_dir = ParsedCli.wp_plugins_dir if ParsedCli.wp_plugins_dir return if target.content_dir diff --git a/app/controllers/enumeration.rb b/app/controllers/enumeration.rb index 989b003c..37d52bf4 100644 --- a/app/controllers/enumeration.rb +++ b/app/controllers/enumeration.rb @@ -17,7 +17,7 @@ module WPScan end def run - enum = parsed_options[:enumerate] || {} + enum = ParsedCli.enumerate || {} enum_plugins if enum_plugins?(enum) enum_themes if enum_themes?(enum) diff --git a/app/controllers/enumeration/enum_methods.rb b/app/controllers/enumeration/enum_methods.rb index 1bf6158e..59a27854 100644 --- a/app/controllers/enumeration/enum_methods.rb +++ b/app/controllers/enumeration/enum_methods.rb @@ -7,13 +7,13 @@ module WPScan # @param [ String ] type (plugins or themes) # @param [ Symbol ] detection_mode # - # @return [ String ] The related enumration message depending on the parsed_options and type supplied + # @return [ String ] The related enumration message depending on the ParsedCli and type supplied def enum_message(type, detection_mode) return unless %w[plugins themes].include?(type) - details = if parsed_options[:enumerate][:"vulnerable_#{type}"] + details = if ParsedCli.enumerate[:"vulnerable_#{type}"] 'Vulnerable' - elsif parsed_options[:enumerate][:"all_#{type}"] + elsif ParsedCli.enumerate[:"all_#{type}"] 'All' else 'Most Popular' @@ -39,15 +39,15 @@ module WPScan # # @return [ Hash ] def default_opts(type) - mode = parsed_options[:"#{type}_detection"] || parsed_options[:detection_mode] + mode = ParsedCli.options[:"#{type}_detection"] || ParsedCli.detection_mode { mode: mode, - exclude_content: parsed_options[:exclude_content_based], + exclude_content: ParsedCli.exclude_content_based, show_progression: user_interaction?, version_detection: { - mode: parsed_options[:"#{type}_version_detection"] || mode, - confidence_threshold: parsed_options[:"#{type}_version_all"] ? 0 : 100 + mode: ParsedCli.options[:"#{type}_version_detection"] || mode, + confidence_threshold: ParsedCli.options[:"#{type}_version_all"] ? 0 : 100 } } end @@ -61,7 +61,7 @@ module WPScan def enum_plugins opts = default_opts('plugins').merge( - list: plugins_list_from_opts(parsed_options), + list: plugins_list_from_opts(ParsedCli.options), sort: true ) @@ -77,7 +77,7 @@ module WPScan plugins.each(&:version) - plugins.select!(&:vulnerable?) if parsed_options[:enumerate][:vulnerable_plugins] + plugins.select!(&:vulnerable?) if ParsedCli.enumerate[:vulnerable_plugins] output('plugins', plugins: plugins) end @@ -107,7 +107,7 @@ module WPScan def enum_themes opts = default_opts('themes').merge( - list: themes_list_from_opts(parsed_options), + list: themes_list_from_opts(ParsedCli.options), sort: true ) @@ -123,7 +123,7 @@ module WPScan themes.each(&:version) - themes.select!(&:vulnerable?) if parsed_options[:enumerate][:vulnerable_themes] + themes.select!(&:vulnerable?) if ParsedCli.enumerate[:vulnerable_themes] output('themes', themes: themes) end @@ -145,28 +145,28 @@ module WPScan end def enum_timthumbs - opts = default_opts('timthumbs').merge(list: parsed_options[:timthumbs_list]) + opts = default_opts('timthumbs').merge(list: ParsedCli.timthumbs_list) output('@info', msg: "Enumerating Timthumbs #{enum_detection_message(opts[:mode])}") if user_interaction? output('timthumbs', timthumbs: target.timthumbs(opts)) end def enum_config_backups - opts = default_opts('config_backups').merge(list: parsed_options[:config_backups_list]) + opts = default_opts('config_backups').merge(list: ParsedCli.config_backups_list) output('@info', msg: "Enumerating Config Backups #{enum_detection_message(opts[:mode])}") if user_interaction? output('config_backups', config_backups: target.config_backups(opts)) end def enum_db_exports - opts = default_opts('db_exports').merge(list: parsed_options[:db_exports_list]) + opts = default_opts('db_exports').merge(list: ParsedCli.db_exports_list) output('@info', msg: "Enumerating DB Exports #{enum_detection_message(opts[:mode])}") if user_interaction? output('db_exports', db_exports: target.db_exports(opts)) end def enum_medias - opts = default_opts('medias').merge(range: parsed_options[:enumerate][:medias]) + opts = default_opts('medias').merge(range: ParsedCli.enumerate[:medias]) if user_interaction? output('@info', @@ -181,13 +181,13 @@ module WPScan # # @return [ Boolean ] Wether or not to enumerate the users def enum_users?(opts) - opts[:users] || (parsed_options[:passwords] && !parsed_options[:username] && !parsed_options[:usernames]) + opts[:users] || (ParsedCli.passwords && !ParsedCli.username && !ParsedCli.usernames) end def enum_users opts = default_opts('users').merge( range: enum_users_range, - list: parsed_options[:users_list] + list: ParsedCli.users_list ) output('@info', msg: "Enumerating Users #{enum_detection_message(opts[:mode])}") if user_interaction? @@ -198,7 +198,7 @@ module WPScan # If the --enumerate is used, the default value is handled by the Option # However, when using --passwords alone, the default has to be set by the code below def enum_users_range - parsed_options[:enumerate][:users] || cli_enum_choices[0].choices[:u].validate(nil) + ParsedCli.enumerate[:users] || cli_enum_choices[0].choices[:u].validate(nil) end end end diff --git a/app/controllers/main_theme.rb b/app/controllers/main_theme.rb index f3c0ce7b..4a4a9711 100644 --- a/app/controllers/main_theme.rb +++ b/app/controllers/main_theme.rb @@ -18,9 +18,9 @@ module WPScan output( 'theme', theme: target.main_theme( - mode: parsed_options[:main_theme_detection] || parsed_options[:detection_mode] + mode: ParsedCli.main_theme_detection || ParsedCli.detection_mode ), - verbose: parsed_options[:verbose] + verbose: ParsedCli.verbose ) end end diff --git a/app/controllers/password_attack.rb b/app/controllers/password_attack.rb index 4749f864..1de25d67 100644 --- a/app/controllers/password_attack.rb +++ b/app/controllers/password_attack.rb @@ -24,7 +24,7 @@ module WPScan end def run - return unless parsed_options[:passwords] + return unless ParsedCli.passwords if user_interaction? output('@info', @@ -33,13 +33,13 @@ module WPScan attack_opts = { show_progression: user_interaction?, - multicall_max_passwords: parsed_options[:multicall_max_passwords] + multicall_max_passwords: ParsedCli.multicall_max_passwords } begin found = [] - attacker.attack(users, passwords(parsed_options[:passwords]), attack_opts) do |user| + attacker.attack(users, passwords(ParsedCli.passwords), attack_opts) do |user| found << user attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}") @@ -61,9 +61,9 @@ module WPScan # @return [ CMSScanner::Finders::Finder ] def attacker_from_cli_options - return unless parsed_options[:password_attack] + return unless ParsedCli.password_attack - case parsed_options[:password_attack] + case ParsedCli.password_attack when :wp_login WPScan::Finders::Passwords::WpLogin.new(target) when :xmlrpc @@ -94,9 +94,9 @@ module WPScan # @return [ Array ] The users to brute force def users - return target.users unless parsed_options[:usernames] + return target.users unless ParsedCli.usernames - parsed_options[:usernames].reduce([]) do |acc, elem| + ParsedCli.usernames.reduce([]) do |acc, elem| acc << Model::User.new(elem.chomp) end end diff --git a/app/controllers/wp_version.rb b/app/controllers/wp_version.rb index 7e28d5f8..11397a60 100644 --- a/app/controllers/wp_version.rb +++ b/app/controllers/wp_version.rb @@ -24,8 +24,8 @@ module WPScan output( 'version', version: target.wp_version( - mode: parsed_options[:wp_version_detection] || parsed_options[:detection_mode], - confidence_threshold: parsed_options[:wp_version_all] ? 0 : 100, + mode: ParsedCli.wp_version_detection || ParsedCli.detection_mode, + confidence_threshold: ParsedCli.wp_version_all ? 0 : 100, show_progression: user_interaction? ) ) diff --git a/app/finders/users/author_id_brute_forcing.rb b/app/finders/users/author_id_brute_forcing.rb index 725a7190..45741ae3 100644 --- a/app/finders/users/author_id_brute_forcing.rb +++ b/app/finders/users/author_id_brute_forcing.rb @@ -7,6 +7,11 @@ module WPScan class AuthorIdBruteForcing < CMSScanner::Finders::Finder include CMSScanner::Finders::Finder::Enumerator + # @return [ Array ] + def valid_response_codes + @valid_response_codes ||= [200, 301, 302] + end + # @param [ Hash ] opts # @option opts [ Range ] :range Mandatory # @@ -15,7 +20,7 @@ module WPScan found = [] found_by_msg = 'Author Id Brute Forcing - %s (Aggressive Detection)' - enumerate(target_urls(opts), opts) do |res, id| + enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, id| username, found_by, confidence = potential_username(res) next unless username @@ -49,7 +54,7 @@ module WPScan super(opts.merge(title: ' Brute Forcing Author IDs -')) end - def request_params + def full_request_params { followlocation: true } end diff --git a/lib/wpscan.rb b/lib/wpscan.rb index 4229d7a2..869119e2 100644 --- a/lib/wpscan.rb +++ b/lib/wpscan.rb @@ -19,6 +19,7 @@ require 'wpscan/helper' require 'wpscan/db' require 'wpscan/version' require 'wpscan/errors' +require 'wpscan/parsed_cli' require 'wpscan/browser' require 'wpscan/target' require 'wpscan/finders' diff --git a/lib/wpscan/browser.rb b/lib/wpscan/browser.rb index 715b2bac..1f738e80 100644 --- a/lib/wpscan/browser.rb +++ b/lib/wpscan/browser.rb @@ -5,11 +5,6 @@ module WPScan class Browser < CMSScanner::Browser extend Actions - # @return [ String ] The path to the user agents list - def user_agents_list - @user_agents_list ||= DB_DIR.join('user-agents.txt').to_s - end - # @return [ String ] def default_user_agent "WPScan v#{VERSION} (https://wpscan.org/)" diff --git a/lib/wpscan/db/updater.rb b/lib/wpscan/db/updater.rb index 9575d45b..834cba75 100644 --- a/lib/wpscan/db/updater.rb +++ b/lib/wpscan/db/updater.rb @@ -8,11 +8,11 @@ module WPScan # /!\ Might want to also update the Enumeration#cli_options when some filenames are changed here FILES = %w[ plugins.json themes.json wordpresses.json - timthumbs-v3.txt user-agents.txt config_backups.txt - db_exports.txt dynamic_finders.yml wp_fingerprints.json LICENSE + timthumbs-v3.txt config_backups.txt db_exports.txt + dynamic_finders.yml wp_fingerprints.json LICENSE ].freeze - OLD_FILES = %w[wordpress.db dynamic_finders_01.yml].freeze + OLD_FILES = %w[wordpress.db user-agents.txt dynamic_finders_01.yml].freeze attr_reader :repo_directory diff --git a/lib/wpscan/parsed_cli.rb b/lib/wpscan/parsed_cli.rb new file mode 100644 index 00000000..56ed43bd --- /dev/null +++ b/lib/wpscan/parsed_cli.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module WPScan + # To be able to use ParsedCli directly, rather than having to access it via WPscan::ParsedCli + class ParsedCli < CMSScanner::ParsedCli + end +end diff --git a/spec/app/controllers/aliases_spec.rb b/spec/app/controllers/aliases_spec.rb index 97be5604..bb8820da 100644 --- a/spec/app/controllers/aliases_spec.rb +++ b/spec/app/controllers/aliases_spec.rb @@ -3,12 +3,10 @@ describe WPScan::Controller::Aliases do subject(:controller) { described_class.new } let(:target_url) { 'http://ex.lo/' } - let(:parsed_options) { rspec_parsed_options(cli_args) } let(:cli_args) { "--url #{target_url}" } before do - WPScan::Browser.reset - described_class.parsed_options = parsed_options + WPScan::ParsedCli.options = rspec_parsed_options(cli_args) end describe '#cli_options' do @@ -22,14 +20,18 @@ describe WPScan::Controller::Aliases do describe 'parsed_options' do context 'when no --stealthy supplied' do - its(:parsed_options) { should eql parsed_options } + it 'contains the correct options' do + expect(WPScan::ParsedCli.options).to include( + detection_mode: :mixed, plugins_version_detection: :mixed + ) + end end context 'when --stealthy supplied' do let(:cli_args) { "#{super()} --stealthy" } it 'contains the correct options' do - expect(controller.parsed_options).to include( + expect(WPScan::ParsedCli.options).to include( random_user_agent: true, detection_mode: :passive, plugins_version_detection: :passive ) end diff --git a/spec/app/controllers/core_spec.rb b/spec/app/controllers/core_spec.rb index 1fc70575..5755940e 100644 --- a/spec/app/controllers/core_spec.rb +++ b/spec/app/controllers/core_spec.rb @@ -3,13 +3,11 @@ describe WPScan::Controller::Core do subject(:core) { described_class.new } let(:target_url) { 'http://ex.lo/' } - let(:parsed_options) { rspec_parsed_options(cli_args) } let(:cli_args) { "--url #{target_url}" } before do - WPScan::Browser.reset described_class.reset - described_class.parsed_options = parsed_options + WPScan::ParsedCli.options = rspec_parsed_options(cli_args) end describe '#cli_options' do @@ -140,7 +138,7 @@ describe WPScan::Controller::Core do expect(core.formatter).to receive(:output).with('banner', hash_including(verbose: nil), 'core') - expect(core).to receive(:update_db_required?).and_return(false) unless parsed_options[:update] + expect(core).to receive(:update_db_required?).and_return(false) unless WPScan::ParsedCli.update end context 'when --update' do diff --git a/spec/app/controllers/custom_directories_spec.rb b/spec/app/controllers/custom_directories_spec.rb index 5351de69..5d165112 100644 --- a/spec/app/controllers/custom_directories_spec.rb +++ b/spec/app/controllers/custom_directories_spec.rb @@ -3,12 +3,10 @@ describe WPScan::Controller::CustomDirectories do subject(:controller) { described_class.new } let(:target_url) { 'http://ex.lo/' } - let(:parsed_options) { rspec_parsed_options(cli_args) } let(:cli_args) { "--url #{target_url}" } before do - WPScan::Browser.reset - described_class.parsed_options = parsed_options + WPScan::ParsedCli.options = rspec_parsed_options(cli_args) end describe '#cli_options' do @@ -34,7 +32,7 @@ describe WPScan::Controller::CustomDirectories do it 'does not raise any error' do expect { controller.before_scan }.to_not raise_error - expect(controller.target.content_dir).to eq parsed_options[:wp_content_dir] + expect(controller.target.content_dir).to eq WPScan::ParsedCli.wp_content_dir end end end diff --git a/spec/app/controllers/enumeration_spec.rb b/spec/app/controllers/enumeration_spec.rb index 8374bb61..10f15c40 100644 --- a/spec/app/controllers/enumeration_spec.rb +++ b/spec/app/controllers/enumeration_spec.rb @@ -3,16 +3,13 @@ describe WPScan::Controller::Enumeration do subject(:controller) { described_class.new } let(:target_url) { 'http://wp.lab/' } - let(:parsed_options) { rspec_parsed_options(cli_args) } let(:cli_args) { "--url #{target_url}" } before do - WPScan::Browser.reset - ## For the --passwords options allow_any_instance_of(OptParseValidator::OptPath).to receive(:check_file) - described_class.parsed_options = parsed_options + WPScan::ParsedCli.options = rspec_parsed_options(cli_args) end describe '#enum_message' do @@ -120,7 +117,7 @@ describe WPScan::Controller::Enumeration do expect(controller).to receive(:enum_plugins) expect(controller).to receive(:enum_config_backups) - expect(parsed_options[:plugins_detection]).to eql :passive + expect(WPScan::ParsedCli.plugins_detection).to eql :passive end it 'calls enum_plugins and enum_config_backups' do diff --git a/spec/app/controllers/password_attack_spec.rb b/spec/app/controllers/password_attack_spec.rb index 83e85c53..d3f2e479 100644 --- a/spec/app/controllers/password_attack_spec.rb +++ b/spec/app/controllers/password_attack_spec.rb @@ -3,12 +3,10 @@ describe WPScan::Controller::PasswordAttack do subject(:controller) { described_class.new } let(:target_url) { 'http://ex.lo/' } - let(:parsed_options) { rspec_parsed_options(cli_args) } let(:cli_args) { "--url #{target_url}" } before do - WPScan::Browser.reset - described_class.parsed_options = parsed_options + WPScan::ParsedCli.options = rspec_parsed_options(cli_args) end describe '#cli_options' do diff --git a/spec/app/controllers/wp_version_spec.rb b/spec/app/controllers/wp_version_spec.rb index 34ece8ae..1cfcdef5 100644 --- a/spec/app/controllers/wp_version_spec.rb +++ b/spec/app/controllers/wp_version_spec.rb @@ -24,12 +24,10 @@ end describe WPScan::Controller::WpVersion do subject(:controller) { described_class.new } let(:target_url) { 'http://ex.lo/' } - let(:parsed_options) { rspec_parsed_options(cli_args) } let(:cli_args) { "--url #{target_url}" } before do - WPScan::Browser.reset - described_class.parsed_options = parsed_options + WPScan::ParsedCli.options = rspec_parsed_options(cli_args) end describe '#cli_options' do @@ -46,8 +44,8 @@ describe WPScan::Controller::WpVersion do expect(controller.target).to receive(:wp_version) .with( hash_including( - mode: parsed_options[:wp_version_detection] || parsed_options[:detection_mode], - confidence_threshold: parsed_options[:wp_version_all] ? 0 : 100 + mode: WPScan::ParsedCli.wp_version_detection || WPScan::ParsedCli.detection_mode, + confidence_threshold: WPScan::ParsedCli.wp_version_all ? 0 : 100 ) ).and_return(stubbed) end diff --git a/spec/app/views_spec.rb b/spec/app/views_spec.rb index e5cd9b9d..82a7b4fb 100644 --- a/spec/app/views_spec.rb +++ b/spec/app/views_spec.rb @@ -16,7 +16,7 @@ describe 'App::Views' do let(:parsed_options) { { url: target_url, format: formatter.to_s.underscore.dasherize } } before do - controller.class.parsed_options = parsed_options + WPScan::ParsedCli.options = parsed_options # Resets the formatter to ensure the correct one is loaded controller.class.class_variable_set(:@@formatter, nil) end diff --git a/spec/fixtures/db/user-agents.txt b/spec/fixtures/db/user-agents.txt deleted file mode 100644 index 32576df6..00000000 --- a/spec/fixtures/db/user-agents.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Coments should be ignored -UA-1 -UA-2 diff --git a/spec/lib/browser_spec.rb b/spec/lib/browser_spec.rb index 2941967d..95b5f687 100644 --- a/spec/lib/browser_spec.rb +++ b/spec/lib/browser_spec.rb @@ -2,20 +2,9 @@ describe WPScan::Browser do subject(:browser) { described_class.instance(options) } - before { described_class.reset } let(:options) { {} } - describe '#user_agents_list' do - context 'when not set' do - its(:user_agents_list) { should eql WPScan::DB_DIR.join('user-agents.txt').to_s } - end - - context 'when set' do - let(:options) { super().merge(user_agents_list: 'test.txt') } - - its(:user_agents_list) { should eql 'test.txt' } - end - end + before { described_class.reset } describe '#user_agent' do context 'when not set' do diff --git a/wpscan.gemspec b/wpscan.gemspec index 9d3d4fc5..0a9c3a0e 100644 --- a/wpscan.gemspec +++ b/wpscan.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |s| s.executables = ['wpscan'] s.require_paths = ['lib'] - s.add_dependency 'cms_scanner', '~> 0.0.43.2' + s.add_dependency 'cms_scanner', '~> 0.0.44.0' s.add_development_dependency 'bundler', '>= 1.6' s.add_development_dependency 'coveralls', '~> 0.8.0'