From e39a192e8d6cf66f13b916fed23d58e5a035bf34 Mon Sep 17 00:00:00 2001 From: erwanlr Date: Thu, 10 Oct 2019 19:59:09 +0100 Subject: [PATCH] Checks default wp-content dir regardless of detection mode if not found passively --- app/controllers/custom_directories.rb | 2 +- lib/wpscan/target/platform/wordpress.rb | 2 +- .../platform/wordpress/custom_directories.rb | 7 +- spec/app/controllers/core_spec.rb | 172 +++++++++--------- .../controllers/custom_directories_spec.rb | 2 +- .../target/platform/wordpress.rb | 4 +- .../platform/wordpress/custom_directories.rb | 36 ++-- 7 files changed, 109 insertions(+), 116 deletions(-) diff --git a/app/controllers/custom_directories.rb b/app/controllers/custom_directories.rb index a5761fb6..ffffbb7c 100644 --- a/app/controllers/custom_directories.rb +++ b/app/controllers/custom_directories.rb @@ -18,7 +18,7 @@ module WPScan 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(ParsedCli.detection_mode) + return if target.content_dir raise Error::WpContentDirNotDetected end diff --git a/lib/wpscan/target/platform/wordpress.rb b/lib/wpscan/target/platform/wordpress.rb index 3fe01432..a3360562 100644 --- a/lib/wpscan/target/platform/wordpress.rb +++ b/lib/wpscan/target/platform/wordpress.rb @@ -90,7 +90,7 @@ module WPScan def wordpress_hosted? return true if /\.wordpress\.com$/i.match?(uri.host) - unless content_dir(:passive) + unless content_dir pattern = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze uris_from_page(homepage_res) do |uri| diff --git a/lib/wpscan/target/platform/wordpress/custom_directories.rb b/lib/wpscan/target/platform/wordpress/custom_directories.rb index cd1b6ef4..f8989c89 100644 --- a/lib/wpscan/target/platform/wordpress/custom_directories.rb +++ b/lib/wpscan/target/platform/wordpress/custom_directories.rb @@ -13,9 +13,8 @@ module WPScan @plugins_dir = dir.chomp('/') end - # @param [ Symbol ] detection_mode # @return [ String ] The wp-content directory - def content_dir(detection_mode = :mixed) + def content_dir unless @content_dir # scope_url_pattern is from CMSScanner::Target pattern = %r{#{scope_url_pattern}([\w\s\-/]+)\\?/(?:themes|plugins|uploads|cache)\\?/}i @@ -29,9 +28,7 @@ module WPScan return @content_dir = match[1] end - unless detection_mode == :passive - return @content_dir = 'wp-content' if default_content_dir_exists? - end + return @content_dir = 'wp-content' if default_content_dir_exists? end @content_dir diff --git a/spec/app/controllers/core_spec.rb b/spec/app/controllers/core_spec.rb index 5755940e..84eac92a 100644 --- a/spec/app/controllers/core_spec.rb +++ b/spec/app/controllers/core_spec.rb @@ -166,6 +166,8 @@ describe WPScan::Controller::Core do before do expect(core).to receive(:load_server_module) expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true) + expect(core.target).to receive(:wordpress_hosted?).and_return(false) + # expect(core.target).to receive(:content_dir).and_return('wp-content') end it 'calls the formatter when started and finished to update the db' do @@ -174,56 +176,6 @@ describe WPScan::Controller::Core do end end - context 'when a redirect occurs' do - before do - stub_request(:any, target_url) - - expect(core.target).to receive(:homepage_res) - .at_least(1) - .and_return(Typhoeus::Response.new(effective_url: redirection, body: '')) - end - - context 'to the wp-admin/install.php' do - let(:redirection) { "#{target_url}wp-admin/install.php" } - - it 'calls the formatter with the correct parameters and exit' do - expect(core.formatter).to receive(:output) - .with('not_fully_configured', hash_including(url: redirection), 'core').ordered - - # TODO: Would be cool to be able to test the exit code - expect { core.before_scan }.to raise_error(SystemExit) - end - end - - context 'to something else' do - let(:redirection) { 'http://g.com/' } - - it 'raises an error' do - expect { core.before_scan }.to raise_error(CMSScanner::Error::HTTPRedirect) - end - end - - context 'to another path with the wp-admin/install.php in the query' do - let(:redirection) { "#{target_url}index.php?a=/wp-admin/install.php" } - - context 'when wordpress' do - it 'does not raise an error' do - expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true) - - expect { core.before_scan }.to_not raise_error - end - end - - context 'when not wordpress' do - it 'raises an error' do - expect(core.target).to receive(:wordpress?).twice.with(:mixed).and_return(false) - - expect { core.before_scan }.to raise_error(WPScan::Error::NotWordPress) - end - end - end - end - context 'when hosted on wordpress.com' do let(:target_url) { 'http://ex.wordpress.com' } @@ -234,52 +186,106 @@ describe WPScan::Controller::Core do end end - context 'when wordpress' do - before do - expect(core).to receive(:load_server_module) - expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true) - end + context 'when not hosted on wordpress.com' do + before { allow(core.target).to receive(:wordpress_hosted?).and_return(false) } - it 'does not raise any error' do - expect { core.before_scan }.to_not raise_error - end - end + context 'when a redirect occurs' do + before do + stub_request(:any, target_url) - context 'when not wordpress' do - before do - expect(core).to receive(:load_server_module) - end + expect(core.target).to receive(:homepage_res) + .at_least(1) + .and_return(Typhoeus::Response.new(effective_url: redirection, body: '')) + end - context 'when no --force' do - before { expect(core.target).to receive(:maybe_add_cookies) } + context 'to the wp-admin/install.php' do + let(:redirection) { "#{target_url}wp-admin/install.php" } - context 'when no cookies added or still not wordpress after being added' do - it 'raises an error' do - expect(core.target).to receive(:wordpress?).twice.with(:mixed).and_return(false) + it 'calls the formatter with the correct parameters and exit' do + expect(core.formatter).to receive(:output) + .with('not_fully_configured', hash_including(url: redirection), 'core').ordered - expect { core.before_scan }.to raise_error(WPScan::Error::NotWordPress) + # TODO: Would be cool to be able to test the exit code + expect { core.before_scan }.to raise_error(SystemExit) end end - context 'when the added cookies solved it' do - it 'does not raise an error' do - expect(core.target).to receive(:wordpress?).with(:mixed).and_return(false).ordered - expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true).ordered + context 'to something else' do + let(:redirection) { 'http://g.com/' } + + it 'raises an error' do + expect { core.before_scan }.to raise_error(CMSScanner::Error::HTTPRedirect) + end + end + + context 'to another path with the wp-admin/install.php in the query' do + let(:redirection) { "#{target_url}index.php?a=/wp-admin/install.php" } + + context 'when wordpress' do + it 'does not raise an error' do + expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true) + + expect { core.before_scan }.to_not raise_error + end + end + + context 'when not wordpress' do + it 'raises an error' do + expect(core.target).to receive(:wordpress?).twice.with(:mixed).and_return(false) + + expect { core.before_scan }.to raise_error(WPScan::Error::NotWordPress) + end + end + end + end + + context 'when wordpress' do + before do + expect(core).to receive(:load_server_module) + expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true) + end + + it 'does not raise any error' do + expect { core.before_scan }.to_not raise_error + end + end + + context 'when not wordpress' do + before do + expect(core).to receive(:load_server_module) + end + + context 'when no --force' do + before { expect(core.target).to receive(:maybe_add_cookies) } + + context 'when no cookies added or still not wordpress after being added' do + it 'raises an error' do + expect(core.target).to receive(:wordpress?).twice.with(:mixed).and_return(false) + + expect { core.before_scan }.to raise_error(WPScan::Error::NotWordPress) + end + end + + context 'when the added cookies solved it' do + it 'does not raise an error' do + expect(core.target).to receive(:wordpress?).with(:mixed).and_return(false).ordered + expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true).ordered + + expect { core.before_scan }.to_not raise_error + end + end + end + + context 'when --force' do + let(:cli_args) { "#{super()} --force" } + + it 'does not raise any error' do + expect(core.target).to receive(:wordpress?).with(:mixed).and_return(false) expect { core.before_scan }.to_not raise_error end end end - - context 'when --force' do - let(:cli_args) { "#{super()} --force" } - - it 'does not raise any error' do - expect(core.target).to receive(:wordpress?).with(:mixed).and_return(false) - - expect { core.before_scan }.to_not raise_error - end - end end end end diff --git a/spec/app/controllers/custom_directories_spec.rb b/spec/app/controllers/custom_directories_spec.rb index 828b46f0..dd82143d 100644 --- a/spec/app/controllers/custom_directories_spec.rb +++ b/spec/app/controllers/custom_directories_spec.rb @@ -20,7 +20,7 @@ describe WPScan::Controller::CustomDirectories do describe '#before_scan' do context 'when the content_dir is not found and not supplied' do - before { expect(controller.target).to receive(:content_dir).with(:mixed) } + before { expect(controller.target).to receive(:content_dir).and_return(nil) } it 'raises an exception' do expect { controller.before_scan }.to raise_error(WPScan::Error::WpContentDirNotDetected) diff --git a/spec/shared_examples/target/platform/wordpress.rb b/spec/shared_examples/target/platform/wordpress.rb index 646f724a..3088ffae 100644 --- a/spec/shared_examples/target/platform/wordpress.rb +++ b/spec/shared_examples/target/platform/wordpress.rb @@ -152,7 +152,7 @@ shared_examples WPScan::Target::Platform::WordPress do context 'when wp-content not detected' do before do - expect(target).to receive(:content_dir).with(:passive).and_return(nil) + expect(target).to receive(:content_dir).and_return(nil) stub_request(:get, target.url).to_return(body: File.read(fixtures.join(fixture).to_s)) end @@ -170,7 +170,7 @@ shared_examples WPScan::Target::Platform::WordPress do end context 'when wp-content detected' do - before { expect(target).to receive(:content_dir).with(:passive).and_return('wp-content') } + before { expect(target).to receive(:content_dir).and_return('wp-content') } its(:wordpress_hosted?) { should be false } end diff --git a/spec/shared_examples/target/platform/wordpress/custom_directories.rb b/spec/shared_examples/target/platform/wordpress/custom_directories.rb index b76b35fb..ec124d39 100644 --- a/spec/shared_examples/target/platform/wordpress/custom_directories.rb +++ b/spec/shared_examples/target/platform/wordpress/custom_directories.rb @@ -42,35 +42,25 @@ shared_examples 'WordPress::CustomDirectories' do end context 'when not found via the homepage' do - before { stub_request(:get, target.url).to_return(body: '') } + before do + stub_request(:get, target.url).to_return(body: '') + + expect(target).to receive(:default_content_dir_exists?).and_return(dir_exist) + end + + context 'when default dir does not exist' do + let(:dir_exist) { false } - context 'when detection mode is passive' do it 'returns nil' do - expect(target).not_to receive(:default_content_dir_exist?) - - expect(target.content_dir(:passive)).to eql nil + expect(target.content_dir).to eql nil end end - context 'when detection mode is mixed or aggressive' do - before { expect(target).to receive(:default_content_dir_exists?).and_return(dir_exist) } + context 'when default dir exists' do + let(:dir_exist) { true } - %i[mixed aggressive].each do |mode| - context 'when default content dir exists' do - let(:dir_exist) { true } - - it 'returns wp-content' do - expect(target.content_dir(mode)).to eql 'wp-content' - end - end - - context 'when default content dir does not exist' do - let(:dir_exist) { false } - - it 'returns nil' do - expect(target.content_dir(mode)).to eql nil - end - end + it 'returns wp-content' do + expect(target.content_dir).to eql 'wp-content' end end end