From 1e113f7fc5e92e8b18b1a23385cc68d2c4d736c5 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Tue, 18 Sep 2012 17:30:50 +0200 Subject: [PATCH] Readded missing files --- lib/wpscan/modules/wp_login_protection.rb | 106 ++++++++++++++++ lib/wpscan/wp_target.rb | 1 + lib/wpscan/wpscan_options.rb | 2 +- .../modules/wp_login_protection_spec.rb | 114 ++++++++++++++++++ spec/lib/wpscan/wp_target_spec.rb | 1 + 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 lib/wpscan/modules/wp_login_protection.rb create mode 100644 spec/lib/wpscan/modules/wp_login_protection_spec.rb diff --git a/lib/wpscan/modules/wp_login_protection.rb b/lib/wpscan/modules/wp_login_protection.rb new file mode 100644 index 00000000..595976f6 --- /dev/null +++ b/lib/wpscan/modules/wp_login_protection.rb @@ -0,0 +1,106 @@ +#-- +# WPScan - WordPress Security Scanner +# Copyright (C) 2012 +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +#++ + +module WpLoginProtection + + @@login_protection_method_pattern = /^has_(.*)_protection\?/i + # Used as cache + @login_protection_plugin = nil + + def has_login_protection? + !login_protection_plugin().nil? + end + + # Checks if a login protection plugin is enabled + # http://code.google.com/p/wpscan/issues/detail?id=111 + # return a WpPlugin object or nil if no one is found + def login_protection_plugin + unless @login_protection_plugin + protected_methods.grep(@@login_protection_method_pattern).each do |symbol_to_call| + + if send(symbol_to_call) + plugin_name = symbol_to_call[@@login_protection_method_pattern, 1].gsub('_', '-') + + return @login_protection_plugin = WpPlugin.new( + :name => plugin_name, + :url => @uri.to_s + ) + end + end + @login_protection_plugin = nil + end + @login_protection_plugin + end + + protected + # Thanks to Alip Aswalid for providing this method. + # http://wordpress.org/extend/plugins/login-lockdown/ + def has_login_lockdown_protection? + Browser.instance.get(login_url()).body =~ %r{Login LockDown}i ? true : false + end + + # http://wordpress.org/extend/plugins/login-lock/ + def has_login_lock_protection? + Browser.instance.get(login_url()).body =~ %r{LOGIN LOCK} ? true : false + end + + # http://wordpress.org/extend/plugins/better-wp-security/ + def has_better_wp_security_protection? + Browser.instance.get(better_wp_security_url()).code != 404 + end + + def better_wp_security_url + WpPlugin.create_location_url_from_name("better-wp-security", @uri) + end + + # http://wordpress.org/extend/plugins/simple-login-lockdown/ + def has_simple_login_lockdown_protection? + Browser.instance.get(simple_login_lockdown_url()).code != 404 + end + + def simple_login_lockdown_url + WpPlugin.create_location_url_from_name("simple-login-lockdown", @uri) + end + + # http://wordpress.org/extend/plugins/login-security-solution/ + def has_login_security_solution_protection? + Browser.instance.get(login_security_solution_url()).code != 404 + end + + def login_security_solution_url + WpPlugin.create_location_url_from_name("login-security-solution", @uri) + end + + # http://wordpress.org/extend/plugins/limit-login-attempts/ + def has_limit_login_attempts_protection? + Browser.instance.get(limit_login_attempts_url()).code != 404 + end + + def limit_login_attempts_url + WpPlugin.create_location_url_from_name("limit-login-attempts", @uri) + end + + # http://wordpress.org/extend/plugins/bluetrait-event-viewer/ + def has_bluetrait_event_viewer_protection? + Browser.instance.get(bluetrait_event_viewer_url()).code != 404 + end + + def bluetrait_event_viewer_url + WpPlugin.create_location_url_from_name("bluetrait-event-viewer", @uri) + end +end diff --git a/lib/wpscan/wp_target.rb b/lib/wpscan/wp_target.rb index 679b38d3..2adafb70 100644 --- a/lib/wpscan/wp_target.rb +++ b/lib/wpscan/wp_target.rb @@ -21,6 +21,7 @@ class WpTarget include WpReadme include WpFullPathDisclosure include WpConfigBackup + include WpLoginProtection include Malwares include WpUsernames include WpTimthumbs diff --git a/lib/wpscan/wpscan_options.rb b/lib/wpscan/wpscan_options.rb index d35d091b..351adba3 100644 --- a/lib/wpscan/wpscan_options.rb +++ b/lib/wpscan/wpscan_options.rb @@ -149,7 +149,7 @@ class WpscanOptions ) elsif cli_option === "--enumerate" # Special cases # Default value if no argument is given - cli_value = "Ttup" if cli_value.length == 0 + cli_value = "T!tup!" if cli_value.length == 0 enumerate_options_from_string(cli_value) else diff --git a/spec/lib/wpscan/modules/wp_login_protection_spec.rb b/spec/lib/wpscan/modules/wp_login_protection_spec.rb new file mode 100644 index 00000000..7db5c542 --- /dev/null +++ b/spec/lib/wpscan/modules/wp_login_protection_spec.rb @@ -0,0 +1,114 @@ +#-- +# WPScan - WordPress Security Scanner +# Copyright (C) 2012 +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +#++ + +shared_examples_for "WpLoginProtection" do + + before :each do + @module = WpScanModuleSpec.new('http://example.localhost') + @module.extend(WpLoginProtection) + + @fixtures_dir = SPEC_FIXTURES_WPSCAN_MODULES_DIR + '/wp_login_protection' + end + + describe "#login_url" do + it "should return the login page url : http://example.localhost/wp-login.php" do + @module.login_url.should === "http://example.localhost/wp-login.php" + end + end + + # It will test all protected methods has_.*_protection with each fixtures to be sure that + # there is not false positive : for example the login-lock must not be detected as login-lockdown + describe "#has_.*_protection?" do + + pattern = WpLoginProtection.class_variable_get(:@@login_protection_method_pattern) + fixtures = + [ + "wp-login-clean.php", "wp-login-login_lockdown.php", "wp-login-login_lock.php", + "wp-login-better_wp_security.php", "wp-login-simple_login_lockdown.php", "wp-login-login_security_solution.php", + "wp-login-limit_login_attempts.php", "wp-login-bluetrait_event_viewer.php" + ] + # For plugins which are detected from the existence of their directory into wp-content/plugins/ (or one of their file) + # and not from a regex into the login page + special_plugins = ["better_wp_security", "simple_login_lockdown", "login_security_solution", "limit_login_attempts", "bluetrait_event_viewer"] + + after :each do + stub_request_to_fixture(:url => @module.login_url, :fixture => @fixture) + + # Stub all special plugins urls to a 404 except if it's the one we want + special_plugins.each do |special_plugin| + special_plugin_call_detection_symbol = :"has_#{special_plugin}_protection?" + special_plugin_call_url_symbol = :"#{special_plugin}_url" + + status_code = (@symbol_to_call === special_plugin_call_detection_symbol and @expected === true) ? 200 : 404 + stub_request(:get, @module.send(special_plugin_call_url_symbol)).to_return(:status => status_code) + end + + @module.send(@symbol_to_call).should === @expected + end + + WpLoginProtection.protected_instance_methods.grep(pattern).each do |symbol_to_call| + plugin_name_from_symbol = symbol_to_call[pattern, 1].gsub('_', '-') + + fixtures.each do |fixture| + plugin_name_from_fixture = fixture[/wp-login-(.*)\.php/i, 1].gsub('_', '-') + expected = plugin_name_from_fixture === plugin_name_from_symbol ? true : false + + it "#{symbol_to_call} with #{fixture} should return #{expected}" do + @plugin_name = plugin_name_from_fixture + @fixture = @fixtures_dir + '/' + fixture + @symbol_to_call = symbol_to_call + @expected = expected + end + end + end + end + + # Factorise this with the code above ? :D + describe "#login_protection_plugin" do + after :each do + stub_request_to_fixture(:url => @module.login_url, :fixture => @fixture) + stub_request(:get, @module.send(:better_wp_security_url)).to_return(:status => 404) + stub_request(:get, @module.send(:simple_login_lockdown_url)).to_return(:status => 404) + stub_request(:get, @module.send(:login_security_solution_url)).to_return(:status => 404) + stub_request(:get, @module.send(:limit_login_attempts_url)).to_return(:status => 404) + stub_request(:get, @module.send(:bluetrait_event_viewer_url)).to_return(:status => 404) + + @module.login_protection_plugin().should === @plugin_expected + @module.has_login_protection?.should === @has_protection_expected + end + + it "should return nil if no protection is present" do + @fixture = @fixtures_dir + "/wp-login-clean.php" + @plugin_expected = nil + @has_protection_expected = false + end + + it "should return a login-lockdown WpPlugin object" do + @fixture = @fixtures_dir + "/wp-login-login_lockdown.php" + @plugin_expected = WpPlugin.new(WpPlugin.create_location_url_from_name("login-lockdown", @module.url)) + @has_protection_expected = true + end + + it "should return a login-lock WpPlugin object" do + @fixture = @fixtures_dir + "/wp-login-login_lock.php" + @plugin_expected = WpPlugin.new(WpPlugin.create_location_url_from_name("login-lock", @module.url)) + @has_protection_expected = true + end + end + +end diff --git a/spec/lib/wpscan/wp_target_spec.rb b/spec/lib/wpscan/wp_target_spec.rb index d839cc2b..f4713b7d 100644 --- a/spec/lib/wpscan/wp_target_spec.rb +++ b/spec/lib/wpscan/wp_target_spec.rb @@ -34,6 +34,7 @@ describe WpTarget do it_should_behave_like "WpReadme" it_should_behave_like "WpConfigBackup" it_should_behave_like "WpFullPathDisclosure" + it_should_behave_like "WpLoginProtection" it_should_behave_like "Malwares" it_should_behave_like "WpUsernames" it_should_behave_like "WpTimthumbs"