Path: blob/master/spec/app/controllers/password_attack_spec.rb
486 views
# frozen_string_literal: true12XMLRPC_FAILED_BODY = '3<?xml version="1.0" encoding="UTF-8"?>4<methodResponse>5<fault>6<value>7<struct>8<member>9<name>faultCode</name>10<value><int>405</int></value>11</member>12<member>13<name>faultString</name>14<value><string>%s</string></value>15</member>16</struct>17</value>18</fault>19</methodResponse>'2021describe WPScan::Controller::PasswordAttack do22subject(:controller) { described_class.new }23let(:target_url) { 'http://ex.lo/' }24let(:cli_args) { "--url #{target_url}" }2526before do27WPScan::ParsedCli.options = rspec_parsed_options(cli_args)28end2930describe '#cli_options' do31its(:cli_options) { should_not be_empty }32its(:cli_options) { should be_a Array }3334it 'contains to correct options' do35expect(controller.cli_options.map(&:to_sym))36.to eq(%i[passwords usernames multicall_max_passwords password_attack login_uri])37end38end3940describe '#users' do41context 'when no --usernames' do42it 'calls target.users' do43expect(controller.target).to receive(:users)44controller.users45end46end4748context 'when --usernames' do49let(:cli_args) { "#{super()} --usernames admin,editor" }5051it 'returns an array with the users' do52expected = %w[admin editor].reduce([]) do |a, e|53a << WPScan::Model::User.new(e)54end5556expect(controller.users).to eql expected57end58end59end6061describe '#run' do62context 'when no --passwords is supplied' do63it 'does not run the attacker' do64expect(controller.run).to eql nil65end66end67end6869describe '#xmlrpc_get_users_blogs_enabled?' do70before { expect(controller.target).to receive(:xmlrpc).and_return(xmlrpc) }7172context 'when xmlrpc not found' do73let(:xmlrpc) { nil }7475its(:xmlrpc_get_users_blogs_enabled?) { should be false }76end7778context 'when xmlrpc not enabled' do79let(:xmlrpc) { WPScan::Model::XMLRPC.new("#{target_url}xmlrpc.php") }8081it 'returns false' do82expect(xmlrpc).to receive(:enabled?).and_return(false)8384expect(controller.xmlrpc_get_users_blogs_enabled?).to be false85end86end8788context 'when xmlrpc enabled' do89let(:xmlrpc) { WPScan::Model::XMLRPC.new("#{target_url}xmlrpc.php") }9091before { expect(xmlrpc).to receive(:enabled?).and_return(true) }9293context 'when wp.getUsersBlogs methods not listed' do94it 'returns false' do95expect(xmlrpc).to receive(:available_methods).and_return(%w[m1 m2])9697expect(controller.xmlrpc_get_users_blogs_enabled?).to be false98end99end100101context 'when wp.getUsersBlogs method listed' do102before do103expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2])104105stub_request(:post, xmlrpc.url).to_return(body: body)106end107108context 'when wp.getUsersBlogs method disabled' do109context 'when blog is in EN' do110let(:body) { format(XMLRPC_FAILED_BODY, 'XML-RPC services are disabled on this site.') }111112it 'returns false' do113expect(controller.xmlrpc_get_users_blogs_enabled?).to be false114end115end116117context 'when blog is in FR' do118let(:body) { format(XMLRPC_FAILED_BODY, 'Les services XML-RPC sont désactivés sur ce site.') }119120it 'returns false' do121expect(controller.xmlrpc_get_users_blogs_enabled?).to be false122end123end124end125126context 'when wp.getUsersBlogs method enabled' do127let(:body) { 'Incorrect username or password.' }128129it 'returns true' do130expect(controller.xmlrpc_get_users_blogs_enabled?).to be true131end132end133end134end135end136137describe '#attacker' do138before do139allow(controller.target).to receive(:sub_dir)140controller.target.instance_variable_set(:@login_url, nil)141end142143context 'when --password-attack provided' do144let(:cli_args) { "#{super()} --password-attack #{attack}" }145146context 'when wp-login' do147before { stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status) }148149let(:attack) { 'wp-login' }150151context 'when available' do152let(:status) { 200 }153154it 'returns the correct object' do155expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin156expect(controller.attacker.target).to be_a WPScan::Target157end158end159160context 'when not available (404)' do161let(:status) { 404 }162163it 'raises an error' do164expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)165end166end167end168169context 'when xmlrpc' do170context 'when xmlrpc not detected on target' do171before do172expect(controller.target).to receive(:xmlrpc).and_return(nil)173end174175context 'when single xmlrpc' do176let(:attack) { 'xmlrpc' }177178it 'raises an error' do179expect { controller.attacker }.to raise_error(WPScan::Error::XMLRPCNotDetected)180end181end182183context 'when xmlrpc-multicall' do184let(:attack) { 'xmlrpc-multicall' }185186it 'raises an error' do187expect { controller.attacker }.to raise_error(WPScan::Error::XMLRPCNotDetected)188end189end190end191192context 'when xmlrpc detected on target' do193before do194expect(controller.target)195.to receive(:xmlrpc)196.and_return(WPScan::Model::XMLRPC.new("#{target_url}xmlrpc.php"))197end198199context 'when single xmlrpc' do200let(:attack) { 'xmlrpc' }201202it 'returns the correct object' do203expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC204expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC205end206end207208context 'when xmlrpc-multicall' do209let(:attack) { 'xmlrpc-multicall' }210211it 'returns the correct object' do212expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPCMulticall213expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC214end215end216end217end218end219220context 'when automatic detection' do221context 'when xmlrpc_get_users_blogs_enabled? is false' do222before do223expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(false)224stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status)225end226227context 'when wp-login available' do228let(:status) { 200 }229230it 'returns the WpLogin' do231expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin232expect(controller.attacker.target).to be_a WPScan::Target233end234end235236context 'when wp-login.php not available' do237let(:status) { 404 }238239it 'raises an error' do240expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)241end242end243end244245context 'when xmlrpc_get_users_blogs_enabled? is true' do246before do247expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(true)248249expect(controller.target)250.to receive(:xmlrpc).and_return(WPScan::Model::XMLRPC.new("#{target_url}xmlrpc.php"))251end252253context 'when WP version not found' do254it 'returns the XMLRPC' do255expect(controller.target).to receive(:wp_version).and_return(false)256257expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC258expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC259end260end261262context 'when WP version found' do263before { expect(controller.target).to receive(:wp_version).and_return(wp_version) }264265context 'when WP < 4.4' do266let(:wp_version) { WPScan::Model::WpVersion.new('3.8.1') }267268it 'returns the XMLRPCMulticall' do269expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPCMulticall270expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC271end272end273274context 'when WP >= 4.4' do275let(:wp_version) { WPScan::Model::WpVersion.new('4.4') }276277it 'returns the XMLRPC' do278expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC279expect(controller.attacker.target).to be_a WPScan::Model::XMLRPC280end281end282end283end284end285end286end287288289