Checks if the wp-login.php is available before attacking it - Fixes #1519
This commit is contained in:
@@ -23,27 +23,32 @@ module WPScan
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def attack_opts
|
||||||
|
@attack_opts ||= {
|
||||||
|
show_progression: user_interaction?,
|
||||||
|
multicall_max_passwords: ParsedCli.multicall_max_passwords
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
return unless ParsedCli.passwords
|
return unless ParsedCli.passwords
|
||||||
|
|
||||||
|
begin
|
||||||
|
found = []
|
||||||
|
|
||||||
if user_interaction?
|
if user_interaction?
|
||||||
output('@info',
|
output('@info',
|
||||||
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
|
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
|
||||||
end
|
end
|
||||||
|
|
||||||
attack_opts = {
|
|
||||||
show_progression: user_interaction?,
|
|
||||||
multicall_max_passwords: ParsedCli.multicall_max_passwords
|
|
||||||
}
|
|
||||||
|
|
||||||
begin
|
|
||||||
found = []
|
|
||||||
|
|
||||||
attacker.attack(users, passwords(ParsedCli.passwords), attack_opts) do |user|
|
attacker.attack(users, passwords(ParsedCli.passwords), attack_opts) do |user|
|
||||||
found << user
|
found << user
|
||||||
|
|
||||||
attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}")
|
attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}")
|
||||||
end
|
end
|
||||||
|
rescue Error::NoLoginInterfaceDetected => e
|
||||||
|
# TODO: Maybe output that in JSON as well.
|
||||||
|
output('@notice', msg: e.to_s) if user_interaction?
|
||||||
ensure
|
ensure
|
||||||
output('users', users: found)
|
output('users', users: found)
|
||||||
end
|
end
|
||||||
@@ -65,6 +70,8 @@ module WPScan
|
|||||||
|
|
||||||
case ParsedCli.password_attack
|
case ParsedCli.password_attack
|
||||||
when :wp_login
|
when :wp_login
|
||||||
|
raise Error::NoLoginInterfaceDetected unless target.login_url
|
||||||
|
|
||||||
Finders::Passwords::WpLogin.new(target)
|
Finders::Passwords::WpLogin.new(target)
|
||||||
when :xmlrpc
|
when :xmlrpc
|
||||||
raise Error::XMLRPCNotDetected unless xmlrpc
|
raise Error::XMLRPCNotDetected unless xmlrpc
|
||||||
@@ -100,8 +107,10 @@ module WPScan
|
|||||||
else
|
else
|
||||||
Finders::Passwords::XMLRPC.new(xmlrpc)
|
Finders::Passwords::XMLRPC.new(xmlrpc)
|
||||||
end
|
end
|
||||||
else
|
elsif target.login_url
|
||||||
Finders::Passwords::WpLogin.new(target)
|
Finders::Passwords::WpLogin.new(target)
|
||||||
|
else
|
||||||
|
raise Error::NoLoginInterfaceDetected
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -29,5 +29,11 @@ module WPScan
|
|||||||
' use the --scope option or make sure the --url value given is the correct one'
|
' use the --scope option or make sure the --url value given is the correct one'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class NoLoginInterfaceDetected < Standard
|
||||||
|
def to_s
|
||||||
|
'Could not find a login interface to perform the password attack against'
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -139,15 +139,16 @@ module WPScan
|
|||||||
# the first time the method is called, and the effective_url is then used
|
# the first time the method is called, and the effective_url is then used
|
||||||
# if suitable, otherwise the default wp-login will be.
|
# if suitable, otherwise the default wp-login will be.
|
||||||
#
|
#
|
||||||
# @return [ String ] The URL to the login page
|
# @return [ String, false ] The URL to the login page or false if not detected
|
||||||
def login_url
|
def login_url
|
||||||
return @login_url if @login_url
|
return @login_url unless @login_url.nil?
|
||||||
|
|
||||||
@login_url = url('wp-login.php')
|
@login_url = url('wp-login.php') # TODO: url(ParsedCli.login_uri)
|
||||||
|
|
||||||
res = Browser.get_and_follow_location(@login_url)
|
res = Browser.get_and_follow_location(@login_url)
|
||||||
|
|
||||||
@login_url = res.effective_url if res.effective_url =~ /wp-login\.php\z/i && in_scope?(res.effective_url)
|
@login_url = res.effective_url if res.effective_url =~ /wp-login\.php\z/i && in_scope?(res.effective_url)
|
||||||
|
@login_url = false if res.code == 404
|
||||||
|
|
||||||
@login_url
|
@login_url
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -107,18 +107,37 @@ describe WPScan::Controller::PasswordAttack do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#attacker' do
|
describe '#attacker' do
|
||||||
|
before do
|
||||||
|
allow(controller.target).to receive(:sub_dir)
|
||||||
|
controller.target.instance_variable_set(:@login_url, nil)
|
||||||
|
end
|
||||||
|
|
||||||
context 'when --password-attack provided' do
|
context 'when --password-attack provided' do
|
||||||
let(:cli_args) { "#{super()} --password-attack #{attack}" }
|
let(:cli_args) { "#{super()} --password-attack #{attack}" }
|
||||||
|
|
||||||
context 'when wp-login' do
|
context 'when wp-login' do
|
||||||
|
before { stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status) }
|
||||||
|
|
||||||
let(:attack) { 'wp-login' }
|
let(:attack) { 'wp-login' }
|
||||||
|
|
||||||
|
context 'when available' do
|
||||||
|
let(:status) { 200 }
|
||||||
|
|
||||||
it 'returns the correct object' do
|
it 'returns the correct object' do
|
||||||
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
||||||
expect(controller.attacker.target).to be_a WPScan::Target
|
expect(controller.attacker.target).to be_a WPScan::Target
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when not available (404)' do
|
||||||
|
let(:status) { 404 }
|
||||||
|
|
||||||
|
it 'raises an error' do
|
||||||
|
expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when xmlrpc' do
|
context 'when xmlrpc' do
|
||||||
context 'when xmlrpc not detected on target' do
|
context 'when xmlrpc not detected on target' do
|
||||||
before do
|
before do
|
||||||
@@ -172,14 +191,29 @@ describe WPScan::Controller::PasswordAttack do
|
|||||||
|
|
||||||
context 'when automatic detection' do
|
context 'when automatic detection' do
|
||||||
context 'when xmlrpc_get_users_blogs_enabled? is false' do
|
context 'when xmlrpc_get_users_blogs_enabled? is false' do
|
||||||
it 'returns the WpLogin' do
|
before do
|
||||||
expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(false)
|
expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(false)
|
||||||
|
stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when wp-login available' do
|
||||||
|
let(:status) { 200 }
|
||||||
|
|
||||||
|
it 'returns the WpLogin' do
|
||||||
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
|
||||||
expect(controller.attacker.target).to be_a WPScan::Target
|
expect(controller.attacker.target).to be_a WPScan::Target
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when wp-login.php not available' do
|
||||||
|
let(:status) { 404 }
|
||||||
|
|
||||||
|
it 'raises an error' do
|
||||||
|
expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when xmlrpc_get_users_blogs_enabled? is true' do
|
context 'when xmlrpc_get_users_blogs_enabled? is true' do
|
||||||
before do
|
before do
|
||||||
expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(true)
|
expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(true)
|
||||||
|
|||||||
@@ -246,6 +246,12 @@ shared_examples WPScan::Target::Platform::WordPress do
|
|||||||
its(:login_url) { should eql target.url('wp-login.php') }
|
its(:login_url) { should eql target.url('wp-login.php') }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when a 404' do
|
||||||
|
before { stub_request(:get, target.url('wp-login.php')).to_return(status: 404) }
|
||||||
|
|
||||||
|
its(:login_url) { should eql false }
|
||||||
|
end
|
||||||
|
|
||||||
context 'when a redirection occured' do
|
context 'when a redirection occured' do
|
||||||
before do
|
before do
|
||||||
expect(WPScan::Browser).to receive(:get_and_follow_location)
|
expect(WPScan::Browser).to receive(:get_and_follow_location)
|
||||||
|
|||||||
Reference in New Issue
Block a user