From aee9ffdb9ca6f84c42a394faef3b653e987affcb Mon Sep 17 00:00:00 2001 From: erwanlr Date: Thu, 4 Jul 2019 15:45:01 +0100 Subject: [PATCH] Fixes #1365 --- app/controllers/password_attack.rb | 15 ++- spec/app/controllers/password_attack_spec.rb | 127 ++++++++++++------- 2 files changed, 93 insertions(+), 49 deletions(-) diff --git a/app/controllers/password_attack.rb b/app/controllers/password_attack.rb index 1de25d67..6ede56bc 100644 --- a/app/controllers/password_attack.rb +++ b/app/controllers/password_attack.rb @@ -77,9 +77,22 @@ module WPScan end end + # @return [ Boolean ] + def xmlrpc_get_users_blogs_enabled? + if xmlrpc&.enabled? && + xmlrpc.available_methods.include?('wp.getUsersBlogs') && + xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]]) + .run.body !~ /XML\-RPC services are disabled/ + + true + else + false + end + end + # @return [ CMSScanner::Finders::Finder ] def attacker_from_automatic_detection - if xmlrpc&.enabled? && xmlrpc.available_methods.include?('wp.getUsersBlogs') + if xmlrpc_get_users_blogs_enabled? wp_version = target.wp_version if wp_version && wp_version < '4.4' diff --git a/spec/app/controllers/password_attack_spec.rb b/spec/app/controllers/password_attack_spec.rb index d3f2e479..815b487e 100644 --- a/spec/app/controllers/password_attack_spec.rb +++ b/spec/app/controllers/password_attack_spec.rb @@ -52,6 +52,60 @@ describe WPScan::Controller::PasswordAttack do end end + describe '#xmlrpc_get_users_blogs_enabled?' do + before { expect(controller.target).to receive(:xmlrpc).and_return(xmlrpc) } + + context 'when xmlrpc not found' do + let(:xmlrpc) { nil } + + its(:xmlrpc_get_users_blogs_enabled?) { should be false } + end + + context 'when xmlrpc not enabled' do + let(:xmlrpc) { WPScan::Model::XMLRPC.new("#{target_url}xmlrpc.php") } + + it 'returns false' do + expect(xmlrpc).to receive(:enabled?).and_return(false) + + expect(controller.xmlrpc_get_users_blogs_enabled?).to be false + end + end + + context 'when xmlrpc enabled' do + let(:xmlrpc) { WPScan::Model::XMLRPC.new("#{target_url}xmlrpc.php") } + + before { expect(xmlrpc).to receive(:enabled?).and_return(true) } + + context 'when wp.getUsersBlogs methods not listed' do + it 'returns false' do + expect(xmlrpc).to receive(:available_methods).and_return(%w[m1 m2]) + + expect(controller.xmlrpc_get_users_blogs_enabled?).to be false + end + end + + context 'when wp.getUsersBlogs method listed' do + before { expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2]) } + + context 'when wp.getUsersBlogs method disabled' do + it 'returns false' do + stub_request(:post, xmlrpc.url).to_return(body: 'XML-RPC services are disabled on this site.') + + expect(controller.xmlrpc_get_users_blogs_enabled?).to be false + end + end + + context 'when wp.getUsersBlogs method enabled' do + it 'returns true' do + stub_request(:post, xmlrpc.url).to_return(body: 'Incorrect username or password.') + + expect(controller.xmlrpc_get_users_blogs_enabled?).to be true + end + end + end + end + end + describe '#attacker' do context 'when --password-attack provided' do let(:cli_args) { "#{super()} --password-attack #{attack}" } @@ -92,7 +146,7 @@ describe WPScan::Controller::PasswordAttack do before do expect(controller.target) .to receive(:xmlrpc) - .and_return(WPScan::Model::XMLRPC.new("#{target_url}/xmlrpc.php")) + .and_return(WPScan::Model::XMLRPC.new("#{target_url}xmlrpc.php")) end context 'when single xmlrpc' do @@ -117,73 +171,50 @@ describe WPScan::Controller::PasswordAttack do end context 'when automatic detection' do - before { expect(controller.target).to receive(:xmlrpc).and_return(xmlrpc) } - - context 'when xmlrpc not found' do - let(:xmlrpc) { nil } - + context 'when xmlrpc_get_users_blogs_enabled? is false' do it 'returns the WpLogin' do - expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin - expect(controller.attacker.target).to be_a WPScan::Target - end - end - - context 'when xmlrpc not enabled' do - let(:xmlrpc) { WPScan::Model::XMLRPC.new("#{target_url}/xmlrpc.php") } - - it 'returns the WpLogin' do - expect(xmlrpc).to receive(:enabled?).and_return(false) + expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(false) expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin expect(controller.attacker.target).to be_a WPScan::Target end end - context 'when xmlrpc enabled' do - let(:xmlrpc) { WPScan::Model::XMLRPC.new("#{target_url}/xmlrpc.php") } + context 'when xmlrpc_get_users_blogs_enabled? is true' do + before do + expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(true) - before { expect(xmlrpc).to receive(:enabled?).and_return(true) } + expect(controller.target) + .to receive(:xmlrpc).and_return(WPScan::Model::XMLRPC.new("#{target_url}xmlrpc.php")) + end - context 'when wp.getUsersBlogs methods not available' do - it 'returns the WpLogin' do - expect(xmlrpc).to receive(:available_methods).and_return(%w[m1 m2]) + context 'when WP version not found' do + it 'returns the XMLRPC' do + expect(controller.target).to receive(:wp_version).and_return(false) - expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin - expect(controller.attacker.target).to be_a WPScan::Target + expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC + expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC end end - context 'when wp.getUsersBlogs method evailable' do - before { expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2]) } + context 'when WP version found' do + before { expect(controller.target).to receive(:wp_version).and_return(wp_version) } - context 'when WP version not found' do - it 'returns the XMLRPC' do - expect(controller.target).to receive(:wp_version).and_return(false) + context 'when WP < 4.4' do + let(:wp_version) { WPScan::Model::WpVersion.new('3.8.1') } - expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC + it 'returns the XMLRPCMulticall' do + expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPCMulticall expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC end end - context 'when WP version found' do - before { expect(controller.target).to receive(:wp_version).and_return(wp_version) } + context 'when WP >= 4.4' do + let(:wp_version) { WPScan::Model::WpVersion.new('4.4') } - context 'when WP < 4.4' do - let(:wp_version) { WPScan::Model::WpVersion.new('3.8.1') } - - it 'returns the XMLRPCMulticall' do - expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPCMulticall - expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC - end - end - - context 'when WP >= 4.4' do - let(:wp_version) { WPScan::Model::WpVersion.new('4.4') } - - it 'returns the XMLRPC' do - expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC - expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC - end + it 'returns the XMLRPC' do + expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC + expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC end end end