WpTarget modules reworked
This commit is contained in:
@@ -1,107 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
module BruteForce
|
|
||||||
|
|
||||||
# param array of WpUsers wp_users
|
|
||||||
# param string wordlist_path
|
|
||||||
# param hash options
|
|
||||||
# boolean :show_progression If true, will output the details (Sucess, error etc)
|
|
||||||
def brute_force(wp_users, wordlist_path, options = {})
|
|
||||||
hydra = Browser.instance.hydra
|
|
||||||
number_of_passwords = BruteForce.lines_in_file(wordlist_path)
|
|
||||||
login_url = login_url()
|
|
||||||
found = []
|
|
||||||
show_progression = options[:show_progression] || false
|
|
||||||
|
|
||||||
wp_users.each do |wp_user|
|
|
||||||
queue_count = 0
|
|
||||||
request_count = 0
|
|
||||||
password_found = false
|
|
||||||
|
|
||||||
File.open(wordlist_path, 'r').each do |password|
|
|
||||||
# ignore file comments, but will miss passwords if they start with a hash...
|
|
||||||
next if password[0, 1] == '#'
|
|
||||||
|
|
||||||
password.strip!
|
|
||||||
|
|
||||||
# keep a count of the amount of requests to be sent
|
|
||||||
request_count += 1
|
|
||||||
queue_count += 1
|
|
||||||
|
|
||||||
# create local vars for on_complete call back, Issue 51.
|
|
||||||
login = wp_user.login
|
|
||||||
password = password
|
|
||||||
|
|
||||||
# the request object
|
|
||||||
request = Browser.instance.forge_request(login_url,
|
|
||||||
{
|
|
||||||
method: :post,
|
|
||||||
body: { log: URI::encode(login), pwd: URI::encode(password) },
|
|
||||||
cache_ttl: 0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# tell hydra what to do when the request completes
|
|
||||||
request.on_complete do |response|
|
|
||||||
|
|
||||||
puts "\n Trying Username : #{login} Password : #{password}" if @verbose
|
|
||||||
|
|
||||||
if response.body =~ /login_error/i
|
|
||||||
puts "\nIncorrect login and/or password." if @verbose
|
|
||||||
elsif response.code == 302
|
|
||||||
puts "\n " + green('[SUCCESS]') + " Login : #{login} Password : #{password}\n" if show_progression
|
|
||||||
found << { name: login, password: password }
|
|
||||||
password_found = true
|
|
||||||
elsif response.timed_out?
|
|
||||||
puts red('ERROR:') + ' Request timed out.' if show_progression
|
|
||||||
elsif response.code == 0
|
|
||||||
puts red('ERROR:') + ' No response from remote server. WAF/IPS?' if show_progression
|
|
||||||
# code is a fixnum, needs a string for regex
|
|
||||||
elsif response.code.to_s =~ /^50/
|
|
||||||
puts red('ERROR:') + ' Server error, try reducing the number of threads.' if show_progression
|
|
||||||
else
|
|
||||||
puts "\n" + red('ERROR:') + " We received an unknown response for #{password}..." if show_progression
|
|
||||||
|
|
||||||
# HACK to get the coverage :/ (otherwise some output is present in the rspec)
|
|
||||||
puts red("Code: #{response.code.to_s}") if @verbose
|
|
||||||
puts red("Body: #{response.body}") if @verbose
|
|
||||||
puts if @verbose
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# move onto the next login if we have found a valid password
|
|
||||||
break if password_found
|
|
||||||
|
|
||||||
# queue the request to be sent later
|
|
||||||
hydra.queue(request)
|
|
||||||
|
|
||||||
# progress indicator
|
|
||||||
print "\r Brute forcing user '#{login}' with #{number_of_passwords} passwords... #{(request_count * 100) / number_of_passwords}% complete." if show_progression
|
|
||||||
|
|
||||||
# it can take a long time to queue 2 million requests,
|
|
||||||
# for that reason, we queue @threads, send @threads, queue @threads and so on.
|
|
||||||
# hydra.run only returns when it has recieved all of its,
|
|
||||||
# responses. This means that while we are waiting for @threads,
|
|
||||||
# responses, we are waiting...
|
|
||||||
if queue_count >= Browser.instance.max_threads
|
|
||||||
hydra.run
|
|
||||||
queue_count = 0
|
|
||||||
puts "Sent #{Browser.instance.max_threads} requests ..." if @verbose
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# run all of the remaining requests
|
|
||||||
hydra.run
|
|
||||||
end
|
|
||||||
found
|
|
||||||
end
|
|
||||||
|
|
||||||
# Counts the number of lines in the wordlist
|
|
||||||
# It can take a couple of minutes on large
|
|
||||||
# wordlists, although bareable.
|
|
||||||
def self.lines_in_file(file_path)
|
|
||||||
lines = 0
|
|
||||||
File.open(file_path, 'r').each { |_| lines += 1 }
|
|
||||||
lines
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
module Malwares
|
|
||||||
# Used as cache :
|
|
||||||
# nil => malwares not checked,
|
|
||||||
# [] => no malwares,
|
|
||||||
# otherwise array of malwares url found
|
|
||||||
@malwares = nil
|
|
||||||
|
|
||||||
def has_malwares?(malwares_file_path = nil)
|
|
||||||
!malwares(malwares_file_path).empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
# return array of string (url of malwares found)
|
|
||||||
def malwares(malwares_file_path = nil)
|
|
||||||
unless @malwares
|
|
||||||
malwares_found = []
|
|
||||||
malwares_file = Malwares.malwares_file(malwares_file_path)
|
|
||||||
index_page_body = Browser.instance.get(@uri.to_s).body
|
|
||||||
|
|
||||||
File.open(malwares_file, 'r') do |file|
|
|
||||||
file.readlines.collect do |url|
|
|
||||||
chomped_url = url.chomp
|
|
||||||
|
|
||||||
if chomped_url.length > 0
|
|
||||||
malwares_found += index_page_body.scan(Malwares.malware_pattern(chomped_url))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
malwares_found.flatten!
|
|
||||||
malwares_found.uniq!
|
|
||||||
|
|
||||||
@malwares = malwares_found
|
|
||||||
end
|
|
||||||
@malwares
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.malwares_file(malwares_file_path)
|
|
||||||
malwares_file_path || DATA_DIR + '/malwares.txt'
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.malware_pattern(url_regex)
|
|
||||||
# no need to escape regex here, because malware.txt contains regex
|
|
||||||
%r{<(?:script|iframe).* src=(?:"|')(#{url_regex}[^"']*)(?:"|')[^>]*>}i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
module WpConfigBackup
|
|
||||||
|
|
||||||
# Checks to see if wp-config.php has a backup
|
|
||||||
# See http://www.feross.org/cmsploit/
|
|
||||||
# return an array of backup config files url
|
|
||||||
def config_backup
|
|
||||||
found = []
|
|
||||||
backups = WpConfigBackup.config_backup_files
|
|
||||||
browser = Browser.instance
|
|
||||||
hydra = browser.hydra
|
|
||||||
queue_count = 0
|
|
||||||
|
|
||||||
backups.each do |file|
|
|
||||||
file_url = @uri.merge(URI.escape(file)).to_s
|
|
||||||
request = browser.forge_request(file_url)
|
|
||||||
|
|
||||||
request.on_complete do |response|
|
|
||||||
if response.body[%r{define}i] and not response.body[%r{<\s?html}i]
|
|
||||||
found << file_url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hydra.queue(request)
|
|
||||||
queue_count += 1
|
|
||||||
|
|
||||||
if queue_count == browser.max_threads
|
|
||||||
hydra.run
|
|
||||||
queue_count = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hydra.run
|
|
||||||
|
|
||||||
found
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return Array
|
|
||||||
def self.config_backup_files
|
|
||||||
%w{
|
|
||||||
wp-config.php~ #wp-config.php# wp-config.php.save wp-config.php.swp wp-config.php.swo wp-config.php_bak
|
|
||||||
wp-config.bak wp-config.php.bak wp-config.save wp-config.old wp-config.php.old wp-config.php.orig
|
|
||||||
wp-config.orig wp-config.php.original wp-config.original wp-config.txt
|
|
||||||
} # thanks to Feross.org for these
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
module WpFullPathDisclosure
|
|
||||||
|
|
||||||
# Check for Full Path Disclosure (FPD)
|
|
||||||
def has_full_path_disclosure?
|
|
||||||
response = Browser.instance.get(full_path_disclosure_url())
|
|
||||||
response.body[%r{Fatal error}i]
|
|
||||||
end
|
|
||||||
|
|
||||||
def full_path_disclosure_url
|
|
||||||
@uri.merge('wp-includes/rss-functions.php').to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
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(
|
|
||||||
@uri,
|
|
||||||
name: plugin_name,
|
|
||||||
wp_content_dir: wp_content_dir,
|
|
||||||
wp_plugins_dir: wp_plugins_dir
|
|
||||||
)
|
|
||||||
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 plugin_url(plugin_name)
|
|
||||||
WpPlugin.new(
|
|
||||||
@uri,
|
|
||||||
name: plugin_name,
|
|
||||||
wp_content_dir: wp_content_dir,
|
|
||||||
wp_plugins_dir: wp_plugins_dir
|
|
||||||
).url
|
|
||||||
end
|
|
||||||
|
|
||||||
def better_wp_security_url
|
|
||||||
plugin_url('better-wp-security/')
|
|
||||||
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
|
|
||||||
plugin_url('simple-login-lockdown/')
|
|
||||||
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
|
|
||||||
plugin_url('login-security-solution')
|
|
||||||
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
|
|
||||||
plugin_url('limit-login-attempts')
|
|
||||||
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
|
|
||||||
plugin_url('bluetrait-event-viewer')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
module WpReadme
|
|
||||||
|
|
||||||
# Checks to see if the readme.html file exists
|
|
||||||
#
|
|
||||||
# This file comes by default in a wordpress installation,
|
|
||||||
# and if deleted is reinstated with an upgrade.
|
|
||||||
def has_readme?
|
|
||||||
response = Browser.instance.get(readme_url())
|
|
||||||
|
|
||||||
unless response.code == 404
|
|
||||||
response.body =~ %r{wordpress}i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def readme_url
|
|
||||||
@uri.merge('readme.html').to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,20 +1,24 @@
|
|||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
require 'web_site'
|
require 'web_site'
|
||||||
require 'modules/wp_readme'
|
require 'wp_target/malwares'
|
||||||
require 'modules/wp_full_path_disclosure'
|
require 'wp_target/wp_readme'
|
||||||
require 'modules/wp_config_backup'
|
require 'wp_target/brute_force'
|
||||||
require 'modules/wp_login_protection'
|
require 'wp_target/wp_registrable'
|
||||||
require 'modules/malwares'
|
require 'wp_target/wp_config_backup'
|
||||||
require 'modules/brute_force'
|
require 'wp_target/wp_login_protection'
|
||||||
|
require 'wp_target/wp_custom_directories'
|
||||||
|
require 'wp_target/wp_full_path_disclosure'
|
||||||
|
|
||||||
class WpTarget < WebSite
|
class WpTarget < WebSite
|
||||||
|
include Malwares
|
||||||
include WpReadme
|
include WpReadme
|
||||||
include WpFullPathDisclosure
|
include BruteForce
|
||||||
|
include WpRegistrable
|
||||||
include WpConfigBackup
|
include WpConfigBackup
|
||||||
include WpLoginProtection
|
include WpLoginProtection
|
||||||
include Malwares
|
include WpCustomDirectories
|
||||||
include BruteForce
|
include WpFullPathDisclosure
|
||||||
|
|
||||||
attr_reader :verbose
|
attr_reader :verbose
|
||||||
|
|
||||||
@@ -72,17 +76,21 @@ class WpTarget < WebSite
|
|||||||
[200, 301, 302, 401, 403, 500, 400]
|
[200, 301, 302, 401, 403, 500, 400]
|
||||||
end
|
end
|
||||||
|
|
||||||
# return WpTheme
|
# @return [ WpTheme ]
|
||||||
|
# :nocov:
|
||||||
def theme
|
def theme
|
||||||
WpTheme.find(@uri)
|
WpTheme.find(@uri)
|
||||||
end
|
end
|
||||||
|
# :nocov:
|
||||||
|
|
||||||
# @param [ String ] versions_xml
|
# @param [ String ] versions_xml
|
||||||
#
|
#
|
||||||
# @return [ WpVersion ]
|
# @return [ WpVersion ]
|
||||||
|
# :nocov:
|
||||||
def version(versions_xml)
|
def version(versions_xml)
|
||||||
WpVersion.find(@uri, wp_content_dir, wp_plugins_dir, versions_xml)
|
WpVersion.find(@uri, wp_content_dir, wp_plugins_dir, versions_xml)
|
||||||
end
|
end
|
||||||
|
# :nocov:
|
||||||
|
|
||||||
def has_plugin?(name, version = nil)
|
def has_plugin?(name, version = nil)
|
||||||
WpPlugin.new(
|
WpPlugin.new(
|
||||||
@@ -94,44 +102,6 @@ class WpTarget < WebSite
|
|||||||
).exists?
|
).exists?
|
||||||
end
|
end
|
||||||
|
|
||||||
def wp_content_dir
|
|
||||||
unless @wp_content_dir
|
|
||||||
index_body = Browser.instance.get(@uri.to_s).body
|
|
||||||
uri_path = @uri.path # Only use the path because domain can be text or an IP
|
|
||||||
|
|
||||||
if index_body[/\/wp-content\/(?:themes|plugins)\//i] || default_wp_content_dir_exists?
|
|
||||||
@wp_content_dir = 'wp-content'
|
|
||||||
else
|
|
||||||
domains_excluded = '(?:www\.)?(facebook|twitter)\.com'
|
|
||||||
@wp_content_dir = index_body[/(?:href|src)\s*=\s*(?:"|').+#{Regexp.escape(uri_path)}((?!#{domains_excluded})[^"']+)\/(?:themes|plugins)\/.*(?:"|')/i, 1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@wp_content_dir
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_wp_content_dir_exists?
|
|
||||||
response = Browser.instance.get(@uri.merge('wp-content').to_s)
|
|
||||||
hash = Digest::MD5.hexdigest(response.body)
|
|
||||||
|
|
||||||
if WpTarget.valid_response_codes.include?(response.code)
|
|
||||||
return true if hash != error_404_hash and hash != homepage_hash
|
|
||||||
end
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def wp_plugins_dir
|
|
||||||
unless @wp_plugins_dir
|
|
||||||
@wp_plugins_dir = "#{wp_content_dir}/plugins"
|
|
||||||
end
|
|
||||||
@wp_plugins_dir
|
|
||||||
end
|
|
||||||
|
|
||||||
def wp_plugins_dir_exists?
|
|
||||||
Browser.instance.get(@uri.merge(wp_plugins_dir)).code != 404
|
|
||||||
end
|
|
||||||
|
|
||||||
def has_debug_log?
|
def has_debug_log?
|
||||||
# We only get the first 700 bytes of the file to avoid loading huge file (like 2Go)
|
# We only get the first 700 bytes of the file to avoid loading huge file (like 2Go)
|
||||||
response_body = Browser.instance.get(debug_log_url(), headers: {'range' => 'bytes=0-700'}).body
|
response_body = Browser.instance.get(debug_log_url(), headers: {'range' => 'bytes=0-700'}).body
|
||||||
@@ -153,46 +123,4 @@ class WpTarget < WebSite
|
|||||||
resp = Browser.instance.get(search_replace_db_2_url)
|
resp = Browser.instance.get(search_replace_db_2_url)
|
||||||
resp.code == 200 && resp.body[%r{by interconnect}i]
|
resp.code == 200 && resp.body[%r{by interconnect}i]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Should check wp-login.php if registration is enabled or not
|
|
||||||
def registration_enabled?
|
|
||||||
resp = Browser.instance.get(registration_url)
|
|
||||||
# redirect only on non multi sites
|
|
||||||
if resp.code == 302 and resp.headers_hash['location'] =~ /wp-login\.php\?registration=disabled/i
|
|
||||||
enabled = false
|
|
||||||
# multi site registration form
|
|
||||||
elsif resp.code == 200 and resp.body =~ /<form id="setupform" method="post" action="[^"]*wp-signup\.php[^"]*">/i
|
|
||||||
enabled = true
|
|
||||||
# normal registration form
|
|
||||||
elsif resp.code == 200 and resp.body =~ /<form name="registerform" id="registerform" action="[^"]*wp-login\.php[^"]*"/i
|
|
||||||
enabled = true
|
|
||||||
# registration disabled
|
|
||||||
else
|
|
||||||
enabled = false
|
|
||||||
end
|
|
||||||
enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
def registration_url
|
|
||||||
is_multisite? ? @uri.merge('wp-signup.php') : @uri.merge('wp-login.php?action=register')
|
|
||||||
end
|
|
||||||
|
|
||||||
def is_multisite?
|
|
||||||
unless @multisite
|
|
||||||
# when multi site, there is no redirection or a redirect to the site itself
|
|
||||||
# otherwise redirect to wp-login.php
|
|
||||||
url = @uri.merge('wp-signup.php')
|
|
||||||
resp = Browser.instance.get(url)
|
|
||||||
if resp.code == 302 and resp.headers_hash['location'] =~ /wp-login\.php\?action=register/
|
|
||||||
@multisite = false
|
|
||||||
elsif resp.code == 302 and resp.headers_hash['location'] =~ /wp-signup\.php/
|
|
||||||
@multisite = true
|
|
||||||
elsif resp.code == 200
|
|
||||||
@multisite = true
|
|
||||||
else
|
|
||||||
@multisite = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@multisite
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
110
lib/wpscan/wp_target/brute_force.rb
Normal file
110
lib/wpscan/wp_target/brute_force.rb
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module BruteForce
|
||||||
|
|
||||||
|
# param array of WpUsers wp_users
|
||||||
|
# param string wordlist_path
|
||||||
|
# param hash options
|
||||||
|
# boolean :show_progression If true, will output the details (Sucess, error etc)
|
||||||
|
def brute_force(wp_users, wordlist_path, options = {})
|
||||||
|
hydra = Browser.instance.hydra
|
||||||
|
number_of_passwords = BruteForce.lines_in_file(wordlist_path)
|
||||||
|
login_url = login_url()
|
||||||
|
found = []
|
||||||
|
show_progression = options[:show_progression] || false
|
||||||
|
|
||||||
|
wp_users.each do |wp_user|
|
||||||
|
queue_count = 0
|
||||||
|
request_count = 0
|
||||||
|
password_found = false
|
||||||
|
|
||||||
|
File.open(wordlist_path, 'r').each do |password|
|
||||||
|
# ignore file comments, but will miss passwords if they start with a hash...
|
||||||
|
next if password[0, 1] == '#'
|
||||||
|
|
||||||
|
password.strip!
|
||||||
|
|
||||||
|
# keep a count of the amount of requests to be sent
|
||||||
|
request_count += 1
|
||||||
|
queue_count += 1
|
||||||
|
|
||||||
|
# create local vars for on_complete call back, Issue 51.
|
||||||
|
login = wp_user.login
|
||||||
|
password = password
|
||||||
|
|
||||||
|
# the request object
|
||||||
|
request = Browser.instance.forge_request(login_url,
|
||||||
|
{
|
||||||
|
method: :post,
|
||||||
|
body: { log: URI::encode(login), pwd: URI::encode(password) },
|
||||||
|
cache_ttl: 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# tell hydra what to do when the request completes
|
||||||
|
request.on_complete do |response|
|
||||||
|
|
||||||
|
puts "\n Trying Username : #{login} Password : #{password}" if @verbose
|
||||||
|
|
||||||
|
if response.body =~ /login_error/i
|
||||||
|
puts "\nIncorrect login and/or password." if @verbose
|
||||||
|
elsif response.code == 302
|
||||||
|
puts "\n " + green('[SUCCESS]') + " Login : #{login} Password : #{password}\n" if show_progression
|
||||||
|
found << { name: login, password: password }
|
||||||
|
password_found = true
|
||||||
|
elsif response.timed_out?
|
||||||
|
puts red('ERROR:') + ' Request timed out.' if show_progression
|
||||||
|
elsif response.code == 0
|
||||||
|
puts red('ERROR:') + ' No response from remote server. WAF/IPS?' if show_progression
|
||||||
|
# code is a fixnum, needs a string for regex
|
||||||
|
elsif response.code.to_s =~ /^50/
|
||||||
|
puts red('ERROR:') + ' Server error, try reducing the number of threads.' if show_progression
|
||||||
|
else
|
||||||
|
puts "\n" + red('ERROR:') + " We received an unknown response for #{password}..." if show_progression
|
||||||
|
|
||||||
|
# HACK to get the coverage :/ (otherwise some output is present in the rspec)
|
||||||
|
puts red("Code: #{response.code.to_s}") if @verbose
|
||||||
|
puts red("Body: #{response.body}") if @verbose
|
||||||
|
puts if @verbose
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# move onto the next login if we have found a valid password
|
||||||
|
break if password_found
|
||||||
|
|
||||||
|
# queue the request to be sent later
|
||||||
|
hydra.queue(request)
|
||||||
|
|
||||||
|
# progress indicator
|
||||||
|
print "\r Brute forcing user '#{login}' with #{number_of_passwords} passwords... #{(request_count * 100) / number_of_passwords}% complete." if show_progression
|
||||||
|
|
||||||
|
# it can take a long time to queue 2 million requests,
|
||||||
|
# for that reason, we queue @threads, send @threads, queue @threads and so on.
|
||||||
|
# hydra.run only returns when it has recieved all of its,
|
||||||
|
# responses. This means that while we are waiting for @threads,
|
||||||
|
# responses, we are waiting...
|
||||||
|
if queue_count >= Browser.instance.max_threads
|
||||||
|
hydra.run
|
||||||
|
queue_count = 0
|
||||||
|
puts "Sent #{Browser.instance.max_threads} requests ..." if @verbose
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# run all of the remaining requests
|
||||||
|
hydra.run
|
||||||
|
end
|
||||||
|
found
|
||||||
|
end
|
||||||
|
|
||||||
|
# Counts the number of lines in the wordlist
|
||||||
|
# It can take a couple of minutes on large
|
||||||
|
# wordlists, although bareable.
|
||||||
|
def self.lines_in_file(file_path)
|
||||||
|
lines = 0
|
||||||
|
File.open(file_path, 'r').each { |_| lines += 1 }
|
||||||
|
lines
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
50
lib/wpscan/wp_target/malwares.rb
Normal file
50
lib/wpscan/wp_target/malwares.rb
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module Malwares
|
||||||
|
# Used as cache :
|
||||||
|
# nil => malwares not checked,
|
||||||
|
# [] => no malwares,
|
||||||
|
# otherwise array of malwares url found
|
||||||
|
@malwares = nil
|
||||||
|
|
||||||
|
def has_malwares?(malwares_file_path = nil)
|
||||||
|
!malwares(malwares_file_path).empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
# return array of string (url of malwares found)
|
||||||
|
def malwares(malwares_file_path = nil)
|
||||||
|
unless @malwares
|
||||||
|
malwares_found = []
|
||||||
|
malwares_file = Malwares.malwares_file(malwares_file_path)
|
||||||
|
index_page_body = Browser.instance.get(@uri.to_s).body
|
||||||
|
|
||||||
|
File.open(malwares_file, 'r') do |file|
|
||||||
|
file.readlines.collect do |url|
|
||||||
|
chomped_url = url.chomp
|
||||||
|
|
||||||
|
if chomped_url.length > 0
|
||||||
|
malwares_found += index_page_body.scan(Malwares.malware_pattern(chomped_url))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
malwares_found.flatten!
|
||||||
|
malwares_found.uniq!
|
||||||
|
|
||||||
|
@malwares = malwares_found
|
||||||
|
end
|
||||||
|
@malwares
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.malwares_file(malwares_file_path)
|
||||||
|
malwares_file_path || DATA_DIR + '/malwares.txt'
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.malware_pattern(url_regex)
|
||||||
|
# no need to escape regex here, because malware.txt contains regex
|
||||||
|
%r{<(?:script|iframe).* src=(?:"|')(#{url_regex}[^"']*)(?:"|')[^>]*>}i
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
50
lib/wpscan/wp_target/wp_config_backup.rb
Normal file
50
lib/wpscan/wp_target/wp_config_backup.rb
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module WpConfigBackup
|
||||||
|
|
||||||
|
# Checks to see if wp-config.php has a backup
|
||||||
|
# See http://www.feross.org/cmsploit/
|
||||||
|
# return an array of backup config files url
|
||||||
|
def config_backup
|
||||||
|
found = []
|
||||||
|
backups = WpConfigBackup.config_backup_files
|
||||||
|
browser = Browser.instance
|
||||||
|
hydra = browser.hydra
|
||||||
|
queue_count = 0
|
||||||
|
|
||||||
|
backups.each do |file|
|
||||||
|
file_url = @uri.merge(URI.escape(file)).to_s
|
||||||
|
request = browser.forge_request(file_url)
|
||||||
|
|
||||||
|
request.on_complete do |response|
|
||||||
|
if response.body[%r{define}i] and not response.body[%r{<\s?html}i]
|
||||||
|
found << file_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hydra.queue(request)
|
||||||
|
queue_count += 1
|
||||||
|
|
||||||
|
if queue_count == browser.max_threads
|
||||||
|
hydra.run
|
||||||
|
queue_count = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hydra.run
|
||||||
|
|
||||||
|
found
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return Array
|
||||||
|
def self.config_backup_files
|
||||||
|
%w{
|
||||||
|
wp-config.php~ #wp-config.php# wp-config.php.save wp-config.php.swp wp-config.php.swo wp-config.php_bak
|
||||||
|
wp-config.bak wp-config.php.bak wp-config.save wp-config.old wp-config.php.old wp-config.php.orig
|
||||||
|
wp-config.orig wp-config.php.original wp-config.original wp-config.txt
|
||||||
|
} # thanks to Feross.org for these
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
49
lib/wpscan/wp_target/wp_custom_directories.rb
Normal file
49
lib/wpscan/wp_target/wp_custom_directories.rb
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module WpCustomDirectories
|
||||||
|
|
||||||
|
# @return [ String ] The wp-content directory
|
||||||
|
def wp_content_dir
|
||||||
|
unless @wp_content_dir
|
||||||
|
index_body = Browser.instance.get(@uri.to_s).body
|
||||||
|
uri_path = @uri.path # Only use the path because domain can be text or an IP
|
||||||
|
|
||||||
|
if index_body[/\/wp-content\/(?:themes|plugins)\//i] || default_wp_content_dir_exists?
|
||||||
|
@wp_content_dir = 'wp-content'
|
||||||
|
else
|
||||||
|
domains_excluded = '(?:www\.)?(facebook|twitter)\.com'
|
||||||
|
@wp_content_dir = index_body[/(?:href|src)\s*=\s*(?:"|').+#{Regexp.escape(uri_path)}((?!#{domains_excluded})[^"']+)\/(?:themes|plugins)\/.*(?:"|')/i, 1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@wp_content_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def default_wp_content_dir_exists?
|
||||||
|
response = Browser.instance.get(@uri.merge('wp-content').to_s)
|
||||||
|
hash = Digest::MD5.hexdigest(response.body)
|
||||||
|
|
||||||
|
if WpTarget.valid_response_codes.include?(response.code)
|
||||||
|
return true if hash != error_404_hash and hash != homepage_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String ] The wp-plugins directory
|
||||||
|
def wp_plugins_dir
|
||||||
|
unless @wp_plugins_dir
|
||||||
|
@wp_plugins_dir = "#{wp_content_dir}/plugins"
|
||||||
|
end
|
||||||
|
@wp_plugins_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def wp_plugins_dir_exists?
|
||||||
|
Browser.instance.get(@uri.merge(wp_plugins_dir)).code != 404
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
20
lib/wpscan/wp_target/wp_full_path_disclosure.rb
Normal file
20
lib/wpscan/wp_target/wp_full_path_disclosure.rb
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module WpFullPathDisclosure
|
||||||
|
|
||||||
|
# Check for Full Path Disclosure (FPD)
|
||||||
|
#
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def has_full_path_disclosure?
|
||||||
|
response = Browser.instance.get(full_path_disclosure_url())
|
||||||
|
response.body[%r{Fatal error}i] ? true : false
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String ]
|
||||||
|
def full_path_disclosure_url
|
||||||
|
@uri.merge('wp-includes/rss-functions.php').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
104
lib/wpscan/wp_target/wp_login_protection.rb
Normal file
104
lib/wpscan/wp_target/wp_login_protection.rb
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
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(
|
||||||
|
@uri,
|
||||||
|
name: plugin_name,
|
||||||
|
wp_content_dir: wp_content_dir,
|
||||||
|
wp_plugins_dir: wp_plugins_dir
|
||||||
|
)
|
||||||
|
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 plugin_url(plugin_name)
|
||||||
|
WpPlugin.new(
|
||||||
|
@uri,
|
||||||
|
name: plugin_name,
|
||||||
|
wp_content_dir: wp_content_dir,
|
||||||
|
wp_plugins_dir: wp_plugins_dir
|
||||||
|
).url
|
||||||
|
end
|
||||||
|
|
||||||
|
def better_wp_security_url
|
||||||
|
plugin_url('better-wp-security/')
|
||||||
|
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
|
||||||
|
plugin_url('simple-login-lockdown/')
|
||||||
|
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
|
||||||
|
plugin_url('login-security-solution')
|
||||||
|
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
|
||||||
|
plugin_url('limit-login-attempts')
|
||||||
|
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
|
||||||
|
plugin_url('bluetrait-event-viewer')
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
27
lib/wpscan/wp_target/wp_readme.rb
Normal file
27
lib/wpscan/wp_target/wp_readme.rb
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module WpReadme
|
||||||
|
|
||||||
|
# Checks to see if the readme.html file exists
|
||||||
|
#
|
||||||
|
# This file comes by default in a wordpress installation,
|
||||||
|
# and if deleted is reinstated with an upgrade.
|
||||||
|
#
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def has_readme?
|
||||||
|
response = Browser.instance.get(readme_url())
|
||||||
|
|
||||||
|
unless response.code == 404
|
||||||
|
return response.body =~ %r{wordpress}i ? true : false
|
||||||
|
end
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String ] The readme URL
|
||||||
|
def readme_url
|
||||||
|
@uri.merge('readme.html').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
54
lib/wpscan/wp_target/wp_registrable.rb
Normal file
54
lib/wpscan/wp_target/wp_registrable.rb
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module WpRegistrable
|
||||||
|
|
||||||
|
# Should check wp-login.php if registration is enabled or not
|
||||||
|
#
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def registration_enabled?
|
||||||
|
resp = Browser.instance.get(registration_url)
|
||||||
|
# redirect only on non multi sites
|
||||||
|
if resp.code == 302 and resp.headers_hash['location'] =~ /wp-login\.php\?registration=disabled/i
|
||||||
|
enabled = false
|
||||||
|
# multi site registration form
|
||||||
|
elsif resp.code == 200 and resp.body =~ /<form id="setupform" method="post" action="[^"]*wp-signup\.php[^"]*">/i
|
||||||
|
enabled = true
|
||||||
|
# normal registration form
|
||||||
|
elsif resp.code == 200 and resp.body =~ /<form name="registerform" id="registerform" action="[^"]*wp-login\.php[^"]*"/i
|
||||||
|
enabled = true
|
||||||
|
# registration disabled
|
||||||
|
else
|
||||||
|
enabled = false
|
||||||
|
end
|
||||||
|
enabled
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String ] The registration URL
|
||||||
|
def registration_url
|
||||||
|
multisite? ? @uri.merge('wp-signup.php').to_s : @uri.merge('wp-login.php?action=register').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def multisite?
|
||||||
|
unless @multisite
|
||||||
|
# when multi site, there is no redirection or a redirect to the site itself
|
||||||
|
# otherwise redirect to wp-login.php
|
||||||
|
url = @uri.merge('wp-signup.php')
|
||||||
|
resp = Browser.instance.get(url)
|
||||||
|
|
||||||
|
if resp.code == 302 and resp.headers_hash['location'] =~ /wp-login\.php\?action=register/
|
||||||
|
@multisite = false
|
||||||
|
elsif resp.code == 302 and resp.headers_hash['location'] =~ /wp-signup\.php/
|
||||||
|
@multisite = true
|
||||||
|
elsif resp.code == 200
|
||||||
|
@multisite = true
|
||||||
|
else
|
||||||
|
@multisite = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@multisite
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
shared_examples_for 'BruteForce' do
|
|
||||||
before :each do
|
|
||||||
@module = WpScanModuleSpec.new('http://example.localhost')
|
|
||||||
@target_url = @module.uri.to_s
|
|
||||||
@fixtures_dir = SPEC_FIXTURES_WPSCAN_MODULES_DIR + '/bruteforce'
|
|
||||||
@wordlist = @fixtures_dir + '/wordlist.txt'
|
|
||||||
@username = 'admin'
|
|
||||||
|
|
||||||
@module.extend(BruteForce)
|
|
||||||
Browser.instance.max_threads = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#lines_in_file' do
|
|
||||||
it 'should return 6' do
|
|
||||||
lines = BruteForce.lines_in_file(@wordlist)
|
|
||||||
lines.should == 6
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#brute_force' do
|
|
||||||
before :each do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should get the correct password' do
|
|
||||||
passwords = []
|
|
||||||
File.open(@wordlist, 'r').each do |password|
|
|
||||||
# ignore comments
|
|
||||||
passwords << password.strip unless password.strip[0, 1] == '#'
|
|
||||||
end
|
|
||||||
# Last status must be 302 to get full code coverage
|
|
||||||
passwords.each do |password|
|
|
||||||
stub_request(:post, @module.login_url).
|
|
||||||
to_return(
|
|
||||||
{ status: 200, body: 'login_error' },
|
|
||||||
{ status: 0, body: 'no reponse' },
|
|
||||||
{ status: 500, body: 'server error' },
|
|
||||||
{ status: 999, body: 'invalid' },
|
|
||||||
{ status: 302, body: 'FOUND!' }
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
user = WpUser.new(@module.uri, login: 'admin')
|
|
||||||
result = @module.brute_force([user], @wordlist)
|
|
||||||
|
|
||||||
result.length.should == 1
|
|
||||||
result.should === [{ name: 'admin', password: 'root' }]
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should cover the timeout branch and return an empty array' do
|
|
||||||
stub_request(:post, @module.login_url).to_timeout
|
|
||||||
|
|
||||||
user = WpUser.new(@module.uri, login: 'admin')
|
|
||||||
result = @module.brute_force([user], @wordlist)
|
|
||||||
result.should == []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
shared_examples_for 'Malwares' do
|
|
||||||
|
|
||||||
before :each do
|
|
||||||
@module = WpScanModuleSpec.new('http://example.localhost')
|
|
||||||
@target_url = @module.uri.to_s
|
|
||||||
@fixtures_dir = SPEC_FIXTURES_WPSCAN_MODULES_DIR + '/malwares'
|
|
||||||
@malwares_file_path = @fixtures_dir + '/malwares.txt'
|
|
||||||
|
|
||||||
@module.extend(Malwares)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#malwares_file' do
|
|
||||||
it "should return #{SPEC_FIXTURES_WPSCAN_MODULES_DIR}/wp_malwares.txt" do
|
|
||||||
Malwares.malwares_file(@malwares_file_path).should === @malwares_file_path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#malwares & #has_malwares' do
|
|
||||||
after :each do
|
|
||||||
if @fixture
|
|
||||||
stub_request_to_fixture(url: @target_url, fixture: File.new(@fixture))
|
|
||||||
end
|
|
||||||
|
|
||||||
malwares = @module.malwares(@malwares_file_path)
|
|
||||||
|
|
||||||
malwares.sort.should === @expected_malwares.sort
|
|
||||||
@module.has_malwares?.should === (@expected_malwares.empty? ? false : true)
|
|
||||||
end
|
|
||||||
it 'should return an empty array on a 404' do
|
|
||||||
stub_request(:get, @target_url).to_return(status: 404)
|
|
||||||
|
|
||||||
@expected_malwares = []
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return an array empty array if no infection found' do
|
|
||||||
@fixture = @fixtures_dir + '/clean.html'
|
|
||||||
@expected_malwares = []
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return an array with 1 malware url (.rr.nu check)' do
|
|
||||||
@fixture = @fixtures_dir + '/single-infection.html'
|
|
||||||
@expected_malwares = ['http://irstde24clined.rr.nu/mm.php?d=1']
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return an array with 1 malware url (iframe check)' do
|
|
||||||
@fixture = @fixtures_dir + '/single-iframe-infection.html'
|
|
||||||
@expected_malwares = ['http://www.thesea.org/media.php']
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return an array with 3 malwares url' do
|
|
||||||
@fixture = @fixtures_dir + '/multiple-infections.html'
|
|
||||||
@expected_malwares = ['http://irstde24clined.rr.nu/mm.php?d=1', 'http://atio79srem.rr.nu/pmg.php?dr=1', 'http://www.thesea.org/media.php']
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
shared_examples_for 'WpConfigBackup' do
|
|
||||||
|
|
||||||
before :all do
|
|
||||||
@module = WpScanModuleSpec.new('http://example.localhost')
|
|
||||||
@fixtures_dir = SPEC_FIXTURES_WPSCAN_MODULES_DIR + '/wp_config_backup'
|
|
||||||
@config_backup_files = WpConfigBackup.config_backup_files
|
|
||||||
|
|
||||||
@module.extend(WpConfigBackup)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#config_backup' do
|
|
||||||
|
|
||||||
# set all @config_backup_files to point to a 404
|
|
||||||
before :each do
|
|
||||||
@config_backup_files.each do |backup_file|
|
|
||||||
file_url = @module.uri.merge(URI.escape(backup_file)).to_s
|
|
||||||
|
|
||||||
stub_request(:get, file_url).to_return(status: 404)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'shoud return an empty array if no config backup is present' do
|
|
||||||
@module.config_backup.should be_empty
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return an array with 1 backup file' do
|
|
||||||
expected = []
|
|
||||||
|
|
||||||
@config_backup_files.sample(1).each do |backup_file|
|
|
||||||
file_url = @module.uri.merge(URI.escape(backup_file)).to_s
|
|
||||||
expected << file_url
|
|
||||||
|
|
||||||
stub_request_to_fixture(url: file_url, fixture: @fixtures_dir + '/wp-config.php')
|
|
||||||
end
|
|
||||||
|
|
||||||
wp_config_backup = @module.config_backup
|
|
||||||
wp_config_backup.should_not be_empty
|
|
||||||
wp_config_backup.should === expected
|
|
||||||
end
|
|
||||||
|
|
||||||
# Is there a way to factorise that one with the previous test ?
|
|
||||||
it 'should return an array with 2 backup file' do
|
|
||||||
expected = []
|
|
||||||
|
|
||||||
@config_backup_files.sample(2).each do |backup_file|
|
|
||||||
file_url = @module.uri.merge(URI.escape(backup_file)).to_s
|
|
||||||
expected << file_url
|
|
||||||
|
|
||||||
stub_request_to_fixture(url: file_url, fixture: @fixtures_dir + '/wp-config.php')
|
|
||||||
end
|
|
||||||
|
|
||||||
wp_config_backup = @module.config_backup
|
|
||||||
wp_config_backup.should_not be_empty
|
|
||||||
wp_config_backup.sort.should === expected.sort
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#config_backup_files' do
|
|
||||||
it 'should not contain duplicates' do
|
|
||||||
WpConfigBackup.config_backup_files.flatten.uniq.length.should == WpConfigBackup.config_backup_files.length
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
shared_examples_for 'WpFullPathDisclosure' do
|
|
||||||
|
|
||||||
before :all do
|
|
||||||
@module = WpScanModuleSpec.new('http://example.localhost')
|
|
||||||
@module.extend(WpFullPathDisclosure)
|
|
||||||
|
|
||||||
@fixtures_dir = SPEC_FIXTURES_WPSCAN_MODULES_DIR + '/wp_full_path_disclosure'
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#full_path_disclosure_url' do
|
|
||||||
it 'should return http://example.localhost/wp-includes/rss-functions.php' do
|
|
||||||
@module.full_path_disclosure_url.should === 'http://example.localhost/wp-includes/rss-functions.php'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#has_full_path_disclosure?' do
|
|
||||||
|
|
||||||
it 'should return false on a 404' do
|
|
||||||
stub_request(:get, @module.full_path_disclosure_url).
|
|
||||||
to_return(status: 404)
|
|
||||||
|
|
||||||
@module.has_full_path_disclosure?.should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return false if no fpd found (blank page for example)' do
|
|
||||||
stub_request(:get, @module.full_path_disclosure_url).
|
|
||||||
to_return(status: 200, body: '')
|
|
||||||
|
|
||||||
@module.has_full_path_disclosure?.should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return true' do
|
|
||||||
stub_request(:get, @module.full_path_disclosure_url).
|
|
||||||
to_return(status: 200, body: File.new(@fixtures_dir + '/rss-functions-disclosure.php'))
|
|
||||||
|
|
||||||
@module.has_full_path_disclosure?.should be_true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
shared_examples_for 'WpLoginProtection' do
|
|
||||||
|
|
||||||
before :each do
|
|
||||||
@module = WpScanModuleSpec.new('http://example.localhost')
|
|
||||||
@module.extend(WpLoginProtection)
|
|
||||||
@module.stub(:wp_plugins_dir).and_return('wp-content/plugins')
|
|
||||||
|
|
||||||
@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::LOGIN_PROTECTION_METHOD_PATTERN
|
|
||||||
fixtures =
|
|
||||||
%w{
|
|
||||||
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 = %w{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_s).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_s).to_return(status: 404)
|
|
||||||
stub_request(:get, @module.send(:simple_login_lockdown_url).to_s).to_return(status: 404)
|
|
||||||
stub_request(:get, @module.send(:login_security_solution_url).to_s).to_return(status: 404)
|
|
||||||
stub_request(:get, @module.send(:limit_login_attempts_url).to_s).to_return(status: 404)
|
|
||||||
stub_request(:get, @module.send(:bluetrait_event_viewer_url).to_s).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(@module.uri, name: 'login-lockdown')
|
|
||||||
@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(@module.uri, name: 'login-lock')
|
|
||||||
@has_protection_expected = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
shared_examples_for 'WpReadme' do
|
|
||||||
|
|
||||||
before :all do
|
|
||||||
@module = WpScanModuleSpec.new('http://example.localhost')
|
|
||||||
@fixtures_dir = SPEC_FIXTURES_WPSCAN_MODULES_DIR + '/wp_readme'
|
|
||||||
|
|
||||||
@module.extend(WpReadme)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#readme_url' do
|
|
||||||
it 'should return http://example.localhost/readme.html' do
|
|
||||||
@module.readme_url.should === "#{@module.uri}readme.html"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#has_readme?' do
|
|
||||||
|
|
||||||
it 'should return false on a 404' do
|
|
||||||
stub_request(:get, @module.readme_url).
|
|
||||||
to_return(status: 404)
|
|
||||||
|
|
||||||
@module.has_readme?.should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return true if it exists' do
|
|
||||||
stub_request(:get, @module.readme_url).
|
|
||||||
to_return(status: 200, body: File.new(@fixtures_dir + '/readme-3.2.1.html'))
|
|
||||||
|
|
||||||
@module.has_readme?.should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
# http://code.google.com/p/wpscan/issues/detail?id=108
|
|
||||||
it 'should return true even if the readme.html is not in english' do
|
|
||||||
stub_request(:get, @module.readme_url).
|
|
||||||
to_return(status: 200, body: File.new(@fixtures_dir + '/readme-3.3.2-fr.html'))
|
|
||||||
|
|
||||||
@module.has_readme?.should be_true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
@@ -3,27 +3,29 @@
|
|||||||
require File.expand_path(File.dirname(__FILE__) + '/wpscan_helper')
|
require File.expand_path(File.dirname(__FILE__) + '/wpscan_helper')
|
||||||
|
|
||||||
describe WpTarget do
|
describe WpTarget do
|
||||||
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR }
|
subject(:wp_target) { WpTarget.new(target_url, options) }
|
||||||
let(:target_url) { 'http://example.localhost/' }
|
let(:target_url) { 'http://example.localhost/' }
|
||||||
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR }
|
||||||
before :each do
|
let(:login_url) { wp_target.uri.merge('wp-login.php').to_s }
|
||||||
Browser::reset
|
let(:options) {
|
||||||
@options =
|
|
||||||
{
|
{
|
||||||
config_file: SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json',
|
config_file: SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json',
|
||||||
cache_ttl: 0,
|
cache_ttl: 0,
|
||||||
wp_content_dir: 'wp-content',
|
wp_content_dir: 'wp-content',
|
||||||
wp_plugins_dir: 'wp-content/plugins'
|
wp_plugins_dir: 'wp-content/plugins'
|
||||||
}
|
}
|
||||||
@wp_target = WpTarget.new(target_url, @options)
|
}
|
||||||
end
|
|
||||||
|
|
||||||
it_should_behave_like 'WpReadme'
|
before { Browser::reset }
|
||||||
it_should_behave_like 'WpConfigBackup'
|
|
||||||
it_should_behave_like 'WpFullPathDisclosure'
|
it_behaves_like 'WpTarget::Malwares'
|
||||||
it_should_behave_like 'WpLoginProtection'
|
it_behaves_like 'WpTarget::WpReadme'
|
||||||
it_should_behave_like 'Malwares'
|
it_behaves_like 'WpTarget::BruteForce'
|
||||||
it_should_behave_like 'BruteForce'
|
it_behaves_like 'WpTarget::WpRegistrable'
|
||||||
|
it_behaves_like 'WpTarget::WpConfigBackup'
|
||||||
|
it_behaves_like 'WpTarget::WpLoginProtection'
|
||||||
|
it_behaves_like 'WpTarget::WpCustomDirectories'
|
||||||
|
it_behaves_like 'WpTarget::WpFullPathDisclosure'
|
||||||
|
|
||||||
describe '#initialize' do
|
describe '#initialize' do
|
||||||
it 'should raise an error if the target_url is nil or empty' do
|
it 'should raise an error if the target_url is nil or empty' do
|
||||||
@@ -33,256 +35,109 @@ describe WpTarget do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#login_url' do
|
describe '#login_url' do
|
||||||
let(:login_url) { @wp_target.uri.merge('wp-login.php').to_s }
|
it 'returns the login url of the target' do
|
||||||
|
|
||||||
it 'should return the login url of the target' do
|
|
||||||
stub_request(:get, login_url).to_return(status: 200, body: '')
|
stub_request(:get, login_url).to_return(status: 200, body: '')
|
||||||
|
|
||||||
@wp_target.login_url.should === login_url
|
wp_target.login_url.should === login_url
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return the redirection url if there is one (ie: for https)' do
|
it 'returns the redirection url if there is one (ie: for https)' do
|
||||||
https_login_url = login_url.gsub(/^http:/, 'https:')
|
https_login_url = login_url.gsub(/^http:/, 'https:')
|
||||||
|
|
||||||
stub_request(:get, login_url).to_return(status: 302, headers: { location: https_login_url })
|
stub_request(:get, login_url).to_return(status: 302, headers: { location: https_login_url })
|
||||||
stub_request(:get, https_login_url).to_return(status: 200)
|
stub_request(:get, https_login_url).to_return(status: 200)
|
||||||
|
|
||||||
@wp_target.login_url.should === https_login_url
|
wp_target.login_url.should === https_login_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#wordpress?' do
|
describe '#wordpress?' do
|
||||||
# each url (wp-login and xmlrpc) pointed to a 404
|
# each url (wp-login and xmlrpc) pointed to a 404
|
||||||
before :each do
|
before :each do
|
||||||
stub_request(:get, @wp_target.url).
|
stub_request(:get, wp_target.url).
|
||||||
to_return(status: 200, body: '', headers: { 'X-Pingback' => @wp_target.uri.merge('xmlrpc.php')})
|
to_return(status: 200, body: '', headers: { 'X-Pingback' => wp_target.uri.merge('xmlrpc.php')})
|
||||||
|
|
||||||
# Preventing redirection check from login_url()
|
# Preventing redirection check from login_url()
|
||||||
@wp_target.stub(redirection: nil)
|
wp_target.stub(redirection: nil)
|
||||||
|
|
||||||
[@wp_target.login_url, @wp_target.xml_rpc_url].each do |url|
|
[wp_target.login_url, wp_target.xml_rpc_url].each do |url|
|
||||||
stub_request(:get, url).to_return(status: 404, body: '')
|
stub_request(:get, url).to_return(status: 404, body: '')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return true if there is a /wp-content/ detected in the index page source' do
|
it 'returns true if there is a /wp-content/ detected in the index page source' do
|
||||||
stub_request_to_fixture(url: @wp_target.url, fixture: fixtures_dir + '/wp_content_dir/wordpress-3.4.1.htm')
|
stub_request_to_fixture(url: wp_target.url, fixture: fixtures_dir + '/wp_content_dir/wordpress-3.4.1.htm')
|
||||||
|
|
||||||
@wp_target.should be_wordpress
|
wp_target.should be_wordpress
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return true if the xmlrpc is found' do
|
it 'returns true if the xmlrpc is found' do
|
||||||
stub_request(:get, @wp_target.xml_rpc_url).
|
stub_request(:get, wp_target.xml_rpc_url).
|
||||||
to_return(status: 200, body: File.new(fixtures_dir + '/xmlrpc.php'))
|
to_return(status: 200, body: File.new(fixtures_dir + '/xmlrpc.php'))
|
||||||
|
|
||||||
@wp_target.should be_wordpress
|
wp_target.should be_wordpress
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return true if the wp-login is found and is a valid wordpress one' do
|
it 'returns true if the wp-login is found and is a valid wordpress one' do
|
||||||
stub_request(:get, @wp_target.login_url).
|
stub_request(:get, wp_target.login_url).
|
||||||
to_return(status: 200, body: File.new(fixtures_dir + '/wp-login.php'))
|
to_return(status: 200, body: File.new(fixtures_dir + '/wp-login.php'))
|
||||||
|
|
||||||
@wp_target.should be_wordpress
|
wp_target.should be_wordpress
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return false if both files are not found (404)' do
|
it 'returns false if both files are not found (404)' do
|
||||||
@wp_target.should_not be_wordpress
|
wp_target.should_not be_wordpress
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the url contains "wordpress" and is a 404' do
|
context 'when the url contains "wordpress" and is a 404' do
|
||||||
let(:target_url) { 'http://lamp/wordpress-3.5./' }
|
let(:target_url) { 'http://lamp/wordpress-3.5./' }
|
||||||
|
|
||||||
it 'returns false' do
|
it 'returns false' do
|
||||||
stub_request(:get, @wp_target.login_url).to_return(status: 404, body: 'The requested URL /wordpress-3.5. was not found on this server.')
|
stub_request(:get, wp_target.login_url).to_return(status: 404, body: 'The requested URL /wordpress-3.5. was not found on this server.')
|
||||||
|
|
||||||
@wp_target.should_not be_wordpress
|
wp_target.should_not be_wordpress
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#redirection' do
|
describe '#redirection' do
|
||||||
it 'should return nil if no redirection detected' do
|
it 'returns nil if no redirection detected' do
|
||||||
stub_request(:get, @wp_target.url).to_return(status: 200, body: '')
|
stub_request(:get, wp_target.url).to_return(status: 200, body: '')
|
||||||
|
|
||||||
@wp_target.redirection.should be_nil
|
wp_target.redirection.should be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
[301, 302].each do |status_code|
|
[301, 302].each do |status_code|
|
||||||
it "should return http://new-location.com if the status code is #{status_code}" do
|
it "returns http://new-location.com if the status code is #{status_code}" do
|
||||||
new_location = 'http://new-location.com'
|
new_location = 'http://new-location.com'
|
||||||
|
|
||||||
stub_request(:get, @wp_target.url).
|
stub_request(:get, wp_target.url).
|
||||||
to_return(status: status_code, headers: { location: new_location })
|
to_return(status: status_code, headers: { location: new_location })
|
||||||
|
|
||||||
stub_request(:get, new_location).to_return(status: 200)
|
stub_request(:get, new_location).to_return(status: 200)
|
||||||
|
|
||||||
@wp_target.redirection.should === 'http://new-location.com'
|
wp_target.redirection.should === 'http://new-location.com'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when multiple redirections' do
|
context 'when multiple redirections' do
|
||||||
it 'should return the last redirection' do
|
it 'returns the last redirection' do
|
||||||
first_redirection = 'www.redirection.com'
|
first_redirection = 'www.redirection.com'
|
||||||
last_redirection = 'redirection.com'
|
last_redirection = 'redirection.com'
|
||||||
|
|
||||||
stub_request(:get, @wp_target.url).to_return(status: 301, headers: { location: first_redirection })
|
stub_request(:get, wp_target.url).to_return(status: 301, headers: { location: first_redirection })
|
||||||
stub_request(:get, first_redirection).to_return(status: 302, headers: { location: last_redirection })
|
stub_request(:get, first_redirection).to_return(status: 302, headers: { location: last_redirection })
|
||||||
stub_request(:get, last_redirection).to_return(status: 200)
|
stub_request(:get, last_redirection).to_return(status: 200)
|
||||||
|
|
||||||
@wp_target.redirection.should === last_redirection
|
wp_target.redirection.should === last_redirection
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#wp_content_dir' do
|
|
||||||
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/wp_content_dir' }
|
|
||||||
|
|
||||||
after :each do
|
|
||||||
@wp_target = WpTarget.new(@target_url) if @target_url
|
|
||||||
|
|
||||||
stub_request_to_fixture(url: @wp_target.url, fixture: @fixture) if @fixture
|
|
||||||
stub_request(:any, /.*\/wp-content\/?$/).to_return(:status => 200, :body => '') # default dir request
|
|
||||||
stub_request(:any, /.*\.html$/).to_return(:status => 200, :body => '') # 404 hash request
|
|
||||||
|
|
||||||
@wp_target.wp_content_dir.should === @expected
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return the string set in the initialize method' do
|
|
||||||
@wp_target = WpTarget.new('http://example.localhost/', @options.merge(wp_content_dir: 'hello-world'))
|
|
||||||
@expected = 'hello-world'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return 'wp-content'" do
|
|
||||||
@target_url = 'http://lamp/wordpress-3.4.1'
|
|
||||||
@fixture = fixtures_dir + '/wordpress-3.4.1.htm'
|
|
||||||
@expected = 'wp-content'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return 'wp-content' if url has trailing slash" do
|
|
||||||
@target_url = 'http://lamp/wordpress-3.4.1/'
|
|
||||||
@fixture = fixtures_dir + '/wordpress-3.4.1.htm'
|
|
||||||
@expected = 'wp-content'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should find the default 'wp-content' dir even if the target_url is not the same (ie : the user supply an IP address and the url used in the code is a domain)" do
|
|
||||||
@target_url = 'http://192.168.1.103/wordpress-3.4.1/'
|
|
||||||
@fixture = fixtures_dir + '/wordpress-3.4.1.htm'
|
|
||||||
@expected = 'wp-content'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return 'custom-content'" do
|
|
||||||
@target_url = 'http://lamp/wordpress-3.4.1-custom'
|
|
||||||
@fixture = fixtures_dir + '/wordpress-3.4.1-custom.htm'
|
|
||||||
@expected = 'custom-content'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return 'custom content spaces'" do
|
|
||||||
@target_url = 'http://lamp/wordpress-3.4.1-custom'
|
|
||||||
@fixture = fixtures_dir + '/wordpress-3.4.1-custom-with-spaces.htm'
|
|
||||||
@expected = 'custom content spaces'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return 'custom-dir/subdir/content'" do
|
|
||||||
@target_url = 'http://lamp/wordpress-3.4.1-custom'
|
|
||||||
@fixture = fixtures_dir + '/wordpress-3.4.1-custom-subdirectories.htm'
|
|
||||||
@expected = 'custom-dir/subdir/content'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should also check in src attributes' do
|
|
||||||
@target_url = 'http://lamp/wordpress-3.4.1'
|
|
||||||
@fixture = fixtures_dir + '/wordpress-3.4.1-in-src.htm'
|
|
||||||
@expected = 'wp-content'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should find the location even if the src or href goes in the plugins dir' do
|
|
||||||
@target_url = 'http://wordpress-3.4.1-in-plugins.htm'
|
|
||||||
@fixture = fixtures_dir + '/wordpress-3.4.1-in-plugins.htm'
|
|
||||||
@expected = 'wp-content'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should not detect facebook.com as a custom wp-content directory' do
|
|
||||||
@target_url = 'http://lamp.localhost/'
|
|
||||||
@fixture = fixtures_dir + '/facebook-detection.htm'
|
|
||||||
@expected = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#default_wp_content_dir_exists?' do
|
|
||||||
after :each do
|
|
||||||
@wp_target = WpTarget.new('http://lamp.localhost/')
|
|
||||||
stub_request(:any, @wp_target.url).to_return(:status => 200, :body => 'homepage') # homepage request
|
|
||||||
|
|
||||||
@wp_target.default_wp_content_dir_exists?.should === @expected
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false if wp-content returns an invalid response code' do
|
|
||||||
stub_request(:any, /.*\/wp-content\/?$/).to_return(:status => 404, :body => '') # default dir request
|
|
||||||
stub_request(:any, /.*\.html$/).to_return(:status => 404, :body => '') # 404 hash request
|
|
||||||
@expected = false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false if wp-content and homepage have same bodies' do
|
|
||||||
stub_request(:any, /.*\/wp-content\/?$/).to_return(:status => 200, :body => 'homepage') # default dir request
|
|
||||||
stub_request(:any, /.*\.html$/).to_return(:status => 404, :body => '404!') # 404 hash request
|
|
||||||
@expected = false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns false if wp-content and 404 page have same bodies' do
|
|
||||||
stub_request(:any, /.*\/wp-content\/?$/).to_return(:status => 200, :body => '404!') # default dir request
|
|
||||||
stub_request(:any, /.*\.html$/).to_return(:status => 404, :body => '404!') # 404 hash request
|
|
||||||
@expected = false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns true if wp-content, 404 page and hoempage return different bodies' do
|
|
||||||
stub_request(:any, /.*\/wp-content\/?$/).to_return(:status => 200, :body => '') # default dir request
|
|
||||||
stub_request(:any, /.*\.html$/).to_return(:status => 200, :body => '404!') # 404 hash request
|
|
||||||
@expected = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#wp_plugins_dir' do
|
|
||||||
after :each do
|
|
||||||
@wp_target.stub(wp_plugins_dir: @stub_value) if @stub_value
|
|
||||||
|
|
||||||
@wp_target.wp_plugins_dir.should === @expected
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return the string set in the initialize method' do
|
|
||||||
@wp_target = WpTarget.new('http://example.localhost/', @options.merge(wp_content_dir: 'asdf', wp_plugins_dir: 'custom-plugins'))
|
|
||||||
@expected = 'custom-plugins'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return 'plugins'" do
|
|
||||||
@stub_value = 'plugins'
|
|
||||||
@expected = 'plugins'
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should return 'wp-content/plugins'" do
|
|
||||||
@wp_target = WpTarget.new('http://example.localhost/', @options.merge(wp_content_dir: 'wp-content', wp_plugins_dir: nil))
|
|
||||||
@expected = 'wp-content/plugins'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#wp_plugins_dir_exists?' do
|
|
||||||
it 'should return true' do
|
|
||||||
target = WpTarget.new('http://example.localhost/', @options.merge(wp_content_dir: 'asdf', wp_plugins_dir: 'custom-plugins'))
|
|
||||||
url = target.uri.merge(target.wp_plugins_dir).to_s
|
|
||||||
stub_request(:any, url).to_return(status: 200)
|
|
||||||
target.wp_plugins_dir_exists?.should == true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return false' do
|
|
||||||
target = WpTarget.new('http://example.localhost/', @options.merge(wp_content_dir: 'asdf', wp_plugins_dir: 'custom-plugins'))
|
|
||||||
url = target.uri.merge(target.wp_plugins_dir).to_s
|
|
||||||
stub_request(:any, url).to_return(status: 404)
|
|
||||||
target.wp_plugins_dir_exists?.should == false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#debug_log_url' do
|
describe '#debug_log_url' do
|
||||||
it "should return 'http://example.localhost/wp-content/debug.log" do
|
it "returns 'http://example.localhost/wp-content/debug.log" do
|
||||||
@wp_target.stub(wp_content_dir: 'wp-content')
|
wp_target.stub(wp_content_dir: 'wp-content')
|
||||||
@wp_target.debug_log_url.should === 'http://example.localhost/wp-content/debug.log'
|
wp_target.debug_log_url.should === 'http://example.localhost/wp-content/debug.log'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -290,17 +145,17 @@ describe WpTarget do
|
|||||||
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/debug_log' }
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/debug_log' }
|
||||||
|
|
||||||
after :each do
|
after :each do
|
||||||
@wp_target.stub(wp_content_dir: 'wp-content')
|
wp_target.stub(wp_content_dir: 'wp-content')
|
||||||
stub_request_to_fixture(url: @wp_target.debug_log_url(), fixture: @fixture)
|
stub_request_to_fixture(url: wp_target.debug_log_url(), fixture: @fixture)
|
||||||
@wp_target.has_debug_log?.should === @expected
|
wp_target.has_debug_log?.should === @expected
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return false' do
|
it 'returns false' do
|
||||||
@fixture = SPEC_FIXTURES_DIR + '/empty-file'
|
@fixture = SPEC_FIXTURES_DIR + '/empty-file'
|
||||||
@expected = false
|
@expected = false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return true' do
|
it 'returns true' do
|
||||||
@fixture = fixtures_dir + '/debug.log'
|
@fixture = fixtures_dir + '/debug.log'
|
||||||
@expected = true
|
@expected = true
|
||||||
end
|
end
|
||||||
@@ -312,102 +167,26 @@ describe WpTarget do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#search_replace_db_2_url' do
|
describe '#search_replace_db_2_url' do
|
||||||
it 'should return the correct url' do
|
it 'returns the correct url' do
|
||||||
@wp_target.search_replace_db_2_url.should == 'http://example.localhost/searchreplacedb2.php'
|
wp_target.search_replace_db_2_url.should == 'http://example.localhost/searchreplacedb2.php'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#search_replace_db_2_exists?' do
|
describe '#search_replace_db_2_exists?' do
|
||||||
it 'should return true' do
|
it 'returns true' do
|
||||||
stub_request(:any, @wp_target.search_replace_db_2_url).to_return(status: 200, body: 'asdf by interconnect asdf')
|
stub_request(:any, wp_target.search_replace_db_2_url).to_return(status: 200, body: 'asdf by interconnect asdf')
|
||||||
@wp_target.search_replace_db_2_exists?.should be_true
|
wp_target.search_replace_db_2_exists?.should be_true
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return false' do
|
it 'returns false' do
|
||||||
stub_request(:any, @wp_target.search_replace_db_2_url).to_return(status: 500)
|
stub_request(:any, wp_target.search_replace_db_2_url).to_return(status: 500)
|
||||||
@wp_target.search_replace_db_2_exists?.should be_false
|
wp_target.search_replace_db_2_exists?.should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should return false' do
|
it 'returns false' do
|
||||||
stub_request(:any, @wp_target.search_replace_db_2_url).to_return(status: 500, body: 'asdf by interconnect asdf')
|
stub_request(:any, wp_target.search_replace_db_2_url).to_return(status: 500, body: 'asdf by interconnect asdf')
|
||||||
@wp_target.search_replace_db_2_exists?.should be_false
|
wp_target.search_replace_db_2_exists?.should be_false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#registration_url' do
|
|
||||||
it 'should return the correct url (multisite)' do
|
|
||||||
# set to multi site
|
|
||||||
stub_request(:any, 'http://example.localhost/wp-signup.php').to_return(status: 200)
|
|
||||||
@wp_target.registration_url.to_s.should == 'http://example.localhost/wp-signup.php'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return the correct url (not multisite)' do
|
|
||||||
# set to single site
|
|
||||||
stub_request(:any, 'http://example.localhost/wp-signup.php').to_return(status: 302, headers: { 'Location' => 'wp-login.php?action=register' })
|
|
||||||
@wp_target.registration_url.to_s.should == 'http://example.localhost/wp-login.php?action=register'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#registration_enabled?' do
|
|
||||||
it 'should return false (multisite)' do
|
|
||||||
# set to multi site
|
|
||||||
stub_request(:any, 'http://example.localhost/wp-signup.php').to_return(status: 200)
|
|
||||||
stub_request(:any, @wp_target.registration_url.to_s).to_return(status: 302, headers: { 'Location' => 'wp-login.php?registration=disabled' })
|
|
||||||
@wp_target.registration_enabled?.should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return true (multisite)' do
|
|
||||||
# set to multi site
|
|
||||||
stub_request(:any, 'http://example.localhost/wp-signup.php').to_return(status: 200)
|
|
||||||
stub_request(:any, @wp_target.registration_url.to_s).to_return(status: 200, body: %{<form id="setupform" method="post" action="wp-signup.php">})
|
|
||||||
@wp_target.registration_enabled?.should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return false (not multisite)' do
|
|
||||||
# set to single site
|
|
||||||
stub_request(:any, 'http://example.localhost/wp-signup.php').to_return(status: 302, headers: { 'Location' => 'wp-login.php?action=register' })
|
|
||||||
stub_request(:any, @wp_target.registration_url.to_s).to_return(status: 302, headers: { 'Location' => 'wp-login.php?registration=disabled' })
|
|
||||||
@wp_target.registration_enabled?.should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return true (not multisite)' do
|
|
||||||
# set to single site
|
|
||||||
stub_request(:any, 'http://example.localhost/wp-signup.php').to_return(status: 302, headers: { 'Location' => 'wp-login.php?action=register' })
|
|
||||||
stub_request(:any, @wp_target.registration_url.to_s).to_return(status: 200, body: %{<form name="registerform" id="registerform" action="wp-login.php"})
|
|
||||||
@wp_target.registration_enabled?.should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return false' do
|
|
||||||
# set to single site
|
|
||||||
stub_request(:any, 'http://example.localhost/wp-signup.php').to_return(status: 302, headers: { 'Location' => 'wp-login.php?action=register' })
|
|
||||||
stub_request(:any, @wp_target.registration_url.to_s).to_return(status: 500)
|
|
||||||
@wp_target.registration_enabled?.should be_false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#is_multisite?' do
|
|
||||||
before :each do
|
|
||||||
@url = @wp_target.uri.merge('wp-signup.php').to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return false' do
|
|
||||||
stub_request(:any, @url).to_return(status: 302, headers: { 'Location' => 'wp-login.php?action=register' })
|
|
||||||
@wp_target.is_multisite?.should be_false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return true' do
|
|
||||||
stub_request(:any, @url).to_return(status: 302, headers: { 'Location' => 'http://example.localhost/wp-signup.php' })
|
|
||||||
@wp_target.is_multisite?.should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return true' do
|
|
||||||
stub_request(:any, @url).to_return(status: 200)
|
|
||||||
@wp_target.is_multisite?.should be_true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should return false' do
|
|
||||||
stub_request(:any, @url).to_return(status: 500)
|
|
||||||
@wp_target.is_multisite?.should be_false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,36 +5,9 @@ require 'spec_helper'
|
|||||||
require WPSCAN_LIB_DIR + '/wpscan_helper'
|
require WPSCAN_LIB_DIR + '/wpscan_helper'
|
||||||
|
|
||||||
SPEC_FIXTURES_WPSCAN_DIR = SPEC_FIXTURES_DIR + '/wpscan'
|
SPEC_FIXTURES_WPSCAN_DIR = SPEC_FIXTURES_DIR + '/wpscan'
|
||||||
SPEC_FIXTURES_WPSCAN_MODULES_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/modules'
|
|
||||||
SPEC_FIXTURES_WPSCAN_WEB_SITE_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/web_site'
|
SPEC_FIXTURES_WPSCAN_WEB_SITE_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/web_site'
|
||||||
SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wp_target'
|
SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wp_target'
|
||||||
SPEC_FIXTURES_WPSCAN_WPSCAN_OPTIONS_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wpscan_options'
|
SPEC_FIXTURES_WPSCAN_WPSCAN_OPTIONS_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wpscan_options'
|
||||||
SPEC_FIXTURES_WPSCAN_WP_THEME_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wp_theme'
|
SPEC_FIXTURES_WPSCAN_WP_THEME_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wp_theme'
|
||||||
SPEC_FIXTURES_WPSCAN_WP_PLUGIN_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wp_plugin'
|
SPEC_FIXTURES_WPSCAN_WP_PLUGIN_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wp_plugin'
|
||||||
SPEC_FIXTURES_WPSCAN_WP_VERSION_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wp_version'
|
SPEC_FIXTURES_WPSCAN_WP_VERSION_DIR = SPEC_FIXTURES_WPSCAN_DIR + '/wp_version'
|
||||||
|
|
||||||
# This class is a HACK to simulate the WpTarget behavior in order
|
|
||||||
# to be able to test the modules independently
|
|
||||||
class WpScanModuleSpec
|
|
||||||
attr_reader :uri
|
|
||||||
attr_accessor :error_404_hash, :homepage_hash, :wp_content_dir, :verbose
|
|
||||||
|
|
||||||
def initialize(target_url)
|
|
||||||
@uri = URI.parse(add_trailing_slash(add_http_protocol(target_url)))
|
|
||||||
|
|
||||||
Browser::reset
|
|
||||||
Browser.instance(
|
|
||||||
config_file: SPEC_FIXTURES_CONF_DIR + '/browser/browser.conf.json',
|
|
||||||
cache_ttl: 0
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def url
|
|
||||||
@uri.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def login_url
|
|
||||||
@uri.merge('wp-login.php').to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
<html><head>
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"></head><body>no image specified</body></html>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
plugins/feature-slideshow/timthumb.php
|
|
||||||
plugins/fotoslide/timthumb.php
|
|
||||||
plugins/feature-slideshow/timthumb.php
|
|
||||||
@@ -1,245 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<!--[if IE 7]>
|
|
||||||
<html class="ie ie7" lang="en-US">
|
|
||||||
<![endif]-->
|
|
||||||
<!--[if IE 8]>
|
|
||||||
<html class="ie ie8" lang="en-US">
|
|
||||||
<![endif]-->
|
|
||||||
<!--[if !(IE 7) | !(IE 8) ]><!-->
|
|
||||||
<html lang="en-US">
|
|
||||||
<!--<![endif]-->
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<title>first last | user's Blog!</title>
|
|
||||||
<link rel="profile" href="http://gmpg.org/xfn/11" />
|
|
||||||
<link rel="pingback" href="http://10.211.55.15/wordpress/xmlrpc.php" />
|
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<script src="http://10.211.55.15/wordpress/wp-content/themes/twentytwelve/js/html5.js" type="text/javascript"></script>
|
|
||||||
<![endif]-->
|
|
||||||
<link rel="alternate" type="application/rss+xml" title="user's Blog! » Feed" href="http://10.211.55.15/wordpress/feed/" />
|
|
||||||
<link rel="alternate" type="application/rss+xml" title="user's Blog! » Comments Feed" href="http://10.211.55.15/wordpress/comments/feed/" />
|
|
||||||
<link rel="alternate" type="application/rss+xml" title="user's Blog! » Posts by first last Feed" href="http://10.211.55.15/wordpress/author/test/feed/" />
|
|
||||||
<link rel='stylesheet' id='gllrStylesheet-css' href='http://10.211.55.15/wordpress/wp-content/plugins/gallery-plugin/css/stylesheet.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='gllrFancyboxStylesheet-css' href='http://10.211.55.15/wordpress/wp-content/plugins/gallery-plugin/fancybox/jquery.fancybox-1.3.4.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='twentytwelve-fonts-css' href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700&subset=latin,latin-ext' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='twentytwelve-style-css' href='http://10.211.55.15/wordpress/wp-content/themes/twentytwelve/style.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<!--[if lt IE 9]>
|
|
||||||
<link rel='stylesheet' id='twentytwelve-ie-css' href='http://10.211.55.15/wordpress/wp-content/themes/twentytwelve/css/ie.css?ver=20121010' type='text/css' media='all' />
|
|
||||||
<![endif]-->
|
|
||||||
<link rel='stylesheet' id='sraf_styles-css' href='http://10.211.55.15/wordpress/wp-content/plugins/super-refer-a-friend/css/sraf.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='thick_box_styles-css' href='http://10.211.55.15/wordpress/wp-content/plugins/super-refer-a-friend/css/thickbox.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='wpgallery-css' href='http://10.211.55.15/wordpress/wp-content/plugins/wordpress-gallery-plugin/css/wp_gallery.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='nivoslider-css' href='http://10.211.55.15/wordpress/wp-content/plugins/wordpress-gallery-plugin/css/nivo-slider.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='colorbox-css' href='http://10.211.55.15/wordpress/wp-content/plugins/wordpress-gallery-plugin/lightbox/colorbox/colorbox.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<link rel='stylesheet' id='wppa_style-css' href='http://10.211.55.15/wordpress/wp-content/plugins/wp-photo-album-plus/theme/wppa-style.css?ver=3.5' type='text/css' media='all' />
|
|
||||||
<script type="text/JavaScript">
|
|
||||||
var srafSponsorWrapperClass = "sraf-sponsor-link-wrapper";
|
|
||||||
var srafSponsorText = "<div class=\"sraf-sponsor-link-wrapper\">Super Refer-A-Friend Plugin by <a href=\"http:\/\/wplift.com\/\">WPLift<\/a><\/div>";
|
|
||||||
</script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-includes/js/jquery/jquery.js?ver=1.8.3'></script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/plugins/wp-photo-album-plus/wppa.min.js?ver=3.5'></script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/plugins/gallery-plugin/fancybox/jquery.mousewheel-3.0.4.pack.js?ver=3.5'></script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/plugins/gallery-plugin/fancybox/jquery.fancybox-1.3.4.pack.js?ver=3.5'></script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/plugins/super-refer-a-friend/js/jquery.validate.js?ver=3.5'></script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/plugins/super-refer-a-friend/js/thickbox.js?ver=3.5'></script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/plugins/super-refer-a-friend/js/sraf.js?ver=3.5'></script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/plugins/wordpress-gallery-plugin/js/jquery.nivo.slider.pack.js?ver=3.5'></script>
|
|
||||||
<script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/plugins/wordpress-gallery-plugin/lightbox/colorbox/jquery.colorbox-min.js?ver=3.5'></script>
|
|
||||||
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://10.211.55.15/wordpress/xmlrpc.php?rsd" />
|
|
||||||
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://10.211.55.15/wordpress/wp-includes/wlwmanifest.xml" />
|
|
||||||
<meta name="generator" content="WordPress 3.5" />
|
|
||||||
|
|
||||||
<meta http-equiv="Content-Language" content="en-US" />
|
|
||||||
<style type="text/css" media="screen">
|
|
||||||
.qtrans_flag span { display:none }
|
|
||||||
.qtrans_flag { height:12px; width:18px; display:block }
|
|
||||||
.qtrans_flag_and_text { padding-left:20px }
|
|
||||||
.qtrans_flag_de { background:url(http://10.211.55.15/wordpress/wp-content/plugins/qtranslate/flags/de.png) no-repeat }
|
|
||||||
.qtrans_flag_en { background:url(http://10.211.55.15/wordpress/wp-content/plugins/qtranslate/flags/gb.png) no-repeat }
|
|
||||||
.qtrans_flag_zh { background:url(http://10.211.55.15/wordpress/wp-content/plugins/qtranslate/flags/cn.png) no-repeat }
|
|
||||||
</style>
|
|
||||||
<link hreflang="de" href="http://10.211.55.15/wordpress/de/?author=2" rel="alternate" />
|
|
||||||
<link hreflang="zh" href="http://10.211.55.15/wordpress/zh/?author=2" rel="alternate" />
|
|
||||||
<style type="text/css">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>
|
|
||||||
|
|
||||||
<!-- WPPA+ Runtime parameters -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
/* <![CDATA[ */
|
|
||||||
|
|
||||||
wppaBackgroundColorImage = "#eeeeee";
|
|
||||||
wppaPopupLinkType = "photo";
|
|
||||||
wppaAnimationType = "fadeover";
|
|
||||||
wppaAnimationSpeed = 800;
|
|
||||||
wppaImageDirectory = "http://10.211.55.15/wordpress/wp-content/plugins/wp-photo-album-plus/images/";
|
|
||||||
wppaThumbnailAreaDelta = 9;
|
|
||||||
wppaTextFrameDelta = 181;
|
|
||||||
wppaBoxDelta = 16;
|
|
||||||
wppaSlideShowTimeOut = 2500;
|
|
||||||
wppaPreambule = 4;
|
|
||||||
wppaFilmShowGlue = true;
|
|
||||||
wppaSlideShow = "Slideshow";
|
|
||||||
wppaStart = "Start";
|
|
||||||
wppaStop = "Stop";
|
|
||||||
wppaSlower = "Slower";
|
|
||||||
wppaFaster = "Faster";
|
|
||||||
wppaPhoto = "Photo";
|
|
||||||
wppaOf = "of";
|
|
||||||
wppaPreviousPhoto = "Previous photo";
|
|
||||||
wppaNextPhoto = "Next photo";
|
|
||||||
wppaPrevP = "Prev.";
|
|
||||||
wppaNextP = "Next";
|
|
||||||
wppaMiniTreshold = 300;
|
|
||||||
wppaUserName = "10.211.55.2";
|
|
||||||
wppaRatingOnce = false;
|
|
||||||
wppaPleaseName = "Please enter your name";
|
|
||||||
wppaPleaseEmail = "Please enter a valid email address";
|
|
||||||
wppaPleaseComment = "Please enter a comment";
|
|
||||||
wppaHideWhenEmpty = false;
|
|
||||||
wppaBGcolorNumbar = "#cccccc";
|
|
||||||
wppaBcolorNumbar = "#cccccc";
|
|
||||||
wppaBGcolorNumbarActive = "#333333";
|
|
||||||
wppaBcolorNumbarActive = "#333333";
|
|
||||||
wppaFontFamilyNumbar = "";
|
|
||||||
wppaFontSizeNumbar = "px";
|
|
||||||
wppaFontColorNumbar = "#777777";
|
|
||||||
wppaFontWeightNumbar = "normal";
|
|
||||||
wppaFontFamilyNumbarActive = "";
|
|
||||||
wppaFontSizeNumbarActive = "px";
|
|
||||||
wppaFontColorNumbarActive = "#777777";
|
|
||||||
wppaFontWeightNumbarActive = "bold";
|
|
||||||
wppaNumbarMax = "10";
|
|
||||||
wppaLocale = "en_US";
|
|
||||||
wppaAjaxUrl = "http://10.211.55.15/wordpress/wp-admin/admin-ajax.php";
|
|
||||||
wppaNextOnCallback = false;
|
|
||||||
wppaRatingUseAjax = false;
|
|
||||||
wppaStarOpacity = 0.2;
|
|
||||||
wppaTickImg.src = "http://10.211.55.15/wordpress/wp-content/plugins/wp-photo-album-plus/images/tick.png";
|
|
||||||
wppaClockImg.src = "http://10.211.55.15/wordpress/wp-content/plugins/wp-photo-album-plus/images/clock.png";
|
|
||||||
wppaSlideWrap = true;
|
|
||||||
wppaLightBox = "";
|
|
||||||
wppaEmailRequired = true;
|
|
||||||
wppaSlideBorderWidth = 0;
|
|
||||||
wppaAllowAjax = false;
|
|
||||||
wppaUsePhotoNamesInUrls = false;
|
|
||||||
wppaThumbTargetBlank = false;
|
|
||||||
wppaRatingMax = 5;
|
|
||||||
wppaRatingDisplayType = "graphic";
|
|
||||||
wppaRatingPrec = 2;
|
|
||||||
wppaStretch = false;
|
|
||||||
wppaMinThumbSpace = 4;
|
|
||||||
wppaThumbSpaceAuto = true;
|
|
||||||
wppaMagnifierCursor = "magnifier-small.png";
|
|
||||||
wppaArtMonkyLink = "none";
|
|
||||||
wppaAutoOpenComments = true;
|
|
||||||
/* ]]> */
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- WPPA+ Rendering enabled -->
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="archive author author-test author-2 custom-font-enabled single-author">
|
|
||||||
<div id="page" class="hfeed site">
|
|
||||||
<header id="masthead" class="site-header" role="banner">
|
|
||||||
<hgroup>
|
|
||||||
<h1 class="site-title"><a href="http://10.211.55.15/wordpress/" title="user's Blog!" rel="home">user's Blog!</a></h1>
|
|
||||||
<h2 class="site-description">Just another WordPress site</h2>
|
|
||||||
</hgroup>
|
|
||||||
|
|
||||||
<nav id="site-navigation" class="main-navigation" role="navigation">
|
|
||||||
<h3 class="menu-toggle">Menu</h3>
|
|
||||||
<a class="assistive-text" href="#content" title="Skip to content">Skip to content</a>
|
|
||||||
<div class="nav-menu"><ul><li ><a href="http://10.211.55.15/wordpress/" title="Home">Home</a></li><li class="page_item page-item-2"><a href="http://10.211.55.15/wordpress/sample-page/">Sample Page</a></li></ul></div>
|
|
||||||
</nav><!-- #site-navigation -->
|
|
||||||
|
|
||||||
</header><!-- #masthead -->
|
|
||||||
|
|
||||||
<div id="main" class="wrapper">
|
|
||||||
<section id="primary" class="site-content">
|
|
||||||
<div id="content" role="main">
|
|
||||||
|
|
||||||
|
|
||||||
<article id="post-0" class="post no-results not-found">
|
|
||||||
<header class="entry-header">
|
|
||||||
<h1 class="entry-title">Nothing Found</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="entry-content">
|
|
||||||
<p>Apologies, but no results were found. Perhaps searching will help find a related post.</p>
|
|
||||||
<form role="search" method="get" id="searchform" action="http://10.211.55.15/wordpress/" >
|
|
||||||
<div><label class="screen-reader-text" for="s">Search for:</label>
|
|
||||||
<input type="text" value="" name="s" id="s" />
|
|
||||||
<input type="submit" id="searchsubmit" value="Search" />
|
|
||||||
</div>
|
|
||||||
</form> </div><!-- .entry-content -->
|
|
||||||
</article><!-- #post-0 -->
|
|
||||||
|
|
||||||
</div><!-- #content -->
|
|
||||||
</section><!-- #primary -->
|
|
||||||
|
|
||||||
|
|
||||||
<div id="secondary" class="widget-area" role="complementary">
|
|
||||||
<aside id="search-2" class="widget widget_search"><form role="search" method="get" id="searchform" action="http://10.211.55.15/wordpress/" >
|
|
||||||
<div><label class="screen-reader-text" for="s">Search for:</label>
|
|
||||||
<input type="text" value="" name="s" id="s" />
|
|
||||||
<input type="submit" id="searchsubmit" value="Search" />
|
|
||||||
</div>
|
|
||||||
</form></aside> <aside id="recent-posts-2" class="widget widget_recent_entries"> <h3 class="widget-title">Recent Posts</h3> <ul>
|
|
||||||
<li>
|
|
||||||
<a href="http://10.211.55.15/wordpress/hello-world/" title="Hello world!">Hello world!</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</aside><aside id="recent-comments-2" class="widget widget_recent_comments"><h3 class="widget-title">Recent Comments</h3><ul id="recentcomments"><li class="recentcomments"><a href='http://wordpress.org/' rel='external nofollow' class='url'>Mr WordPress</a> on <a href="http://10.211.55.15/wordpress/hello-world/#comment-1">Hello world!</a></li></ul></aside><aside id="archives-2" class="widget widget_archive"><h3 class="widget-title">Archives</h3> <ul>
|
|
||||||
<li><a href='http://10.211.55.15/wordpress/2012/12/' title='December 2012'>December 2012</a></li>
|
|
||||||
</ul>
|
|
||||||
</aside><aside id="categories-2" class="widget widget_categories"><h3 class="widget-title">Categories</h3> <ul>
|
|
||||||
<li class="cat-item cat-item-1"><a href="http://10.211.55.15/wordpress/category/uncategorized/" title="View all posts filed under Uncategorized">Uncategorized</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</aside><aside id="meta-2" class="widget widget_meta"><h3 class="widget-title">Meta</h3> <ul>
|
|
||||||
<li><a href="http://10.211.55.15/wordpress/wp-login.php">Log in</a></li>
|
|
||||||
<li><a href="http://10.211.55.15/wordpress/feed/" title="Syndicate this site using RSS 2.0">Entries <abbr title="Really Simple Syndication">RSS</abbr></a></li>
|
|
||||||
<li><a href="http://10.211.55.15/wordpress/comments/feed/" title="The latest comments to all posts in RSS">Comments <abbr title="Really Simple Syndication">RSS</abbr></a></li>
|
|
||||||
<li><a href="http://wordpress.org/" title="Powered by WordPress, state-of-the-art semantic personal publishing platform.">WordPress.org</a></li>
|
|
||||||
</ul>
|
|
||||||
</aside> </div><!-- #secondary -->
|
|
||||||
</div><!-- #main .wrapper -->
|
|
||||||
<footer id="colophon" role="contentinfo">
|
|
||||||
<div class="site-info">
|
|
||||||
<a href="http://bitnami.org/stack/wordpress/" title="Semantic Personal Publishing Platform">Proudly powered by BitNami WordPress Stack</a>
|
|
||||||
</div><!-- .site-info -->
|
|
||||||
</footer><!-- #colophon -->
|
|
||||||
</div><!-- #page -->
|
|
||||||
|
|
||||||
<div class="sraf-sponsor-link-wrapper">Super Refer-A-Friend Plugin by <a href="http://wplift.com/">WPLift</a></div>
|
|
||||||
<!-- start WPPA+ Footer data -->
|
|
||||||
<div id="wppa-overlay-bg" style="text-align:center; display:none; position:fixed; top:0; left:0; z-index:100090; width:100%; height:2048px; background-color:black;" onclick="wppaOvlOnclick(event)" ></div>
|
|
||||||
<div id="wppa-overlay-ic" style="position:fixed; top:0; padding-top:10px; z-index:100095; opacity:1; box-shadow:none;"
|
|
||||||
ontouchstart="wppaTouchStart(event, 'wppa-overlay-ic', -1);" ontouchend="wppaTouchEnd(event);"
|
|
||||||
ontouchmove="wppaTouchMove(event);" ontouchcancel="wppaTouchCancel(event);" >
|
|
||||||
</div>
|
|
||||||
<img id="wppa-overlay-sp" style="position:fixed; top:200px; left:200px; z-index:100100; opacity:1; visibility:hidden; box-shadow:none;" src="http://10.211.55.15/wordpress/wp-content/plugins/wp-photo-album-plus/images/loading.gif" />
|
|
||||||
<script type="text/javascript">jQuery("#wppa-overlay-bg").css({height:screen.height+"px"});
|
|
||||||
wppaOvlTxtHeight = "auto";
|
|
||||||
wppaOvlCloseTxt = "CLOSE";
|
|
||||||
wppaOvlOpacity = 0.8;
|
|
||||||
wppaOvlOnclickType = "none";
|
|
||||||
wppaOvlTheme = "black";
|
|
||||||
wppaOvlAnimSpeed = 300;
|
|
||||||
wppaVer4WindowWidth = 800;
|
|
||||||
wppaVer4WindowHeight = 600;
|
|
||||||
wppaOvlShowCounter = true;
|
|
||||||
|
|
||||||
wppaOvlFontSize = "10"
|
|
||||||
|
|
||||||
wppaOvlFontWeight = "bold"
|
|
||||||
wppaOvlLineHeight = "12"
|
|
||||||
</script>
|
|
||||||
<!-- end WPPA+ Footer data --><script type='text/javascript' src='http://10.211.55.15/wordpress/wp-content/themes/twentytwelve/js/navigation.js?ver=1.0'></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/
|
|
||||||
|
|
||||||
Page Caching using disk: enhanced (Requested URI contains query)
|
|
||||||
|
|
||||||
Served from: 10.211.55.15 @ 2013-02-05 09:01:41 -->
|
|
||||||
57
spec/shared_examples/wp_target/brute_force.rb
Normal file
57
spec/shared_examples/wp_target/brute_force.rb
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
shared_examples 'WpTarget::BruteForce' do
|
||||||
|
|
||||||
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/bruteforce' }
|
||||||
|
let(:wordlist) { fixtures_dir + '/wordlist.txt' }
|
||||||
|
|
||||||
|
before :each do
|
||||||
|
wp_target.stub(:login_url).and_return('http://example.localhost/wp-login.php')
|
||||||
|
|
||||||
|
Browser.instance.max_threads = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#lines_in_file' do
|
||||||
|
it 'returns 6' do
|
||||||
|
lines = WpTarget::BruteForce.lines_in_file(wordlist)
|
||||||
|
lines.should == 6
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#brute_force' do
|
||||||
|
|
||||||
|
it 'gets the correct password' do
|
||||||
|
passwords = []
|
||||||
|
File.open(wordlist, 'r').each do |password|
|
||||||
|
# ignore comments
|
||||||
|
passwords << password.strip unless password.strip[0, 1] == '#'
|
||||||
|
end
|
||||||
|
# Last status must be 302 to get full code coverage
|
||||||
|
passwords.each do |password|
|
||||||
|
stub_request(:post, wp_target.login_url).
|
||||||
|
to_return(
|
||||||
|
{ status: 200, body: 'login_error' },
|
||||||
|
{ status: 0, body: 'no reponse' },
|
||||||
|
{ status: 500, body: 'server error' },
|
||||||
|
{ status: 999, body: 'invalid' },
|
||||||
|
{ status: 302, body: 'FOUND!' }
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
user = WpUser.new(wp_target.uri, login: 'admin')
|
||||||
|
result = wp_target.brute_force([user], wordlist)
|
||||||
|
|
||||||
|
result.length.should == 1
|
||||||
|
result.should === [{ name: 'admin', password: 'root' }]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'covers the timeout branch and return an empty array' do
|
||||||
|
stub_request(:post, wp_target.login_url).to_timeout
|
||||||
|
|
||||||
|
user = WpUser.new(wp_target.uri, login: 'admin')
|
||||||
|
result = wp_target.brute_force([user], wordlist)
|
||||||
|
result.should == []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
53
spec/shared_examples/wp_target/malwares.rb
Normal file
53
spec/shared_examples/wp_target/malwares.rb
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
shared_examples 'WpTarget::Malwares' do
|
||||||
|
|
||||||
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/malwares' }
|
||||||
|
let(:malwares_file) { fixtures_dir + '/malwares.txt' }
|
||||||
|
|
||||||
|
describe '#malwares_file' do
|
||||||
|
it "returns the correct file path" do
|
||||||
|
WpTarget::Malwares.malwares_file(malwares_file).should === malwares_file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#malwares & #has_malwares' do
|
||||||
|
after :each do
|
||||||
|
if @fixture
|
||||||
|
stub_request_to_fixture(url: wp_target.url, fixture: File.new(fixtures_dir + @fixture))
|
||||||
|
end
|
||||||
|
|
||||||
|
malwares = wp_target.malwares(@malwares_file_path)
|
||||||
|
|
||||||
|
malwares.sort.should === @expected.sort
|
||||||
|
wp_target.has_malwares?.should === (@expected.empty? ? false : true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty array on a 404' do
|
||||||
|
stub_request(:get, wp_target.url).to_return(status: 404)
|
||||||
|
|
||||||
|
@expected = []
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an array empty array if no infection found' do
|
||||||
|
@fixture = '/clean.html'
|
||||||
|
@expected = []
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an array with 1 malware url (.rr.nu check)' do
|
||||||
|
@fixture = '/single-infection.html'
|
||||||
|
@expected = ['http://irstde24clined.rr.nu/mm.php?d=1']
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an array with 1 malware url (iframe check)' do
|
||||||
|
@fixture = '/single-iframe-infection.html'
|
||||||
|
@expected = ['http://www.thesea.org/media.php']
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an array with 3 malwares url' do
|
||||||
|
@fixture = '/multiple-infections.html'
|
||||||
|
@expected = ['http://irstde24clined.rr.nu/mm.php?d=1', 'http://atio79srem.rr.nu/pmg.php?dr=1', 'http://www.thesea.org/media.php']
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
61
spec/shared_examples/wp_target/wp_config_backup.rb
Normal file
61
spec/shared_examples/wp_target/wp_config_backup.rb
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
shared_examples 'WpTarget::WpConfigBackup' do
|
||||||
|
|
||||||
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/wp_config_backup' }
|
||||||
|
let(:config_backup_files) { WpTarget::WpConfigBackup.config_backup_files }
|
||||||
|
|
||||||
|
describe '#config_backup' do
|
||||||
|
|
||||||
|
# set all @config_backup_files to point to a 404
|
||||||
|
before :each do
|
||||||
|
config_backup_files.each do |backup_file|
|
||||||
|
file_url = wp_target.uri.merge(URI.escape(backup_file)).to_s
|
||||||
|
|
||||||
|
stub_request(:get, file_url).to_return(status: 404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shoud return an empty array if no config backup is present' do
|
||||||
|
wp_target.config_backup.should be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an array with 1 backup file' do
|
||||||
|
expected = []
|
||||||
|
|
||||||
|
config_backup_files.sample(1).each do |backup_file|
|
||||||
|
file_url = wp_target.uri.merge(URI.escape(backup_file)).to_s
|
||||||
|
expected << file_url
|
||||||
|
|
||||||
|
stub_request_to_fixture(url: file_url, fixture: fixtures_dir + '/wp-config.php')
|
||||||
|
end
|
||||||
|
|
||||||
|
wp_config_backup = wp_target.config_backup
|
||||||
|
wp_config_backup.should_not be_empty
|
||||||
|
wp_config_backup.should === expected
|
||||||
|
end
|
||||||
|
|
||||||
|
# Is there a way to factorise that one with the previous test ?
|
||||||
|
it 'returns an array with 2 backup file' do
|
||||||
|
expected = []
|
||||||
|
|
||||||
|
config_backup_files.sample(2).each do |backup_file|
|
||||||
|
file_url = wp_target.uri.merge(URI.escape(backup_file)).to_s
|
||||||
|
expected << file_url
|
||||||
|
|
||||||
|
stub_request_to_fixture(url: file_url, fixture: fixtures_dir + '/wp-config.php')
|
||||||
|
end
|
||||||
|
|
||||||
|
wp_config_backup = wp_target.config_backup
|
||||||
|
wp_config_backup.should_not be_empty
|
||||||
|
wp_config_backup.sort.should === expected.sort
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#config_backup_files' do
|
||||||
|
it 'does not contain duplicates' do
|
||||||
|
config_backup_files.flatten.uniq.length.should == config_backup_files.length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
143
spec/shared_examples/wp_target/wp_custom_directories.rb
Normal file
143
spec/shared_examples/wp_target/wp_custom_directories.rb
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
shared_examples 'WpTarget::WpCustomDirectories' do
|
||||||
|
|
||||||
|
describe '#wp_content_dir' do
|
||||||
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/wp_content_dir' }
|
||||||
|
|
||||||
|
after :each do
|
||||||
|
@wp_target = WpTarget.new(@target_url) if @target_url
|
||||||
|
|
||||||
|
stub_request_to_fixture(url: @wp_target.url, fixture: @fixture) if @fixture
|
||||||
|
stub_request(:get, /.*\/wp-content\/?$/).to_return(:status => 200, :body => '') # default dir request
|
||||||
|
stub_request(:get, /.*\.html$/).to_return(:status => 200, :body => '') # 404 hash request
|
||||||
|
|
||||||
|
@wp_target.wp_content_dir.should === @expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the string set in the initialize method' do
|
||||||
|
@wp_target = WpTarget.new('http://example.localhost/', options.merge(wp_content_dir: 'hello-world'))
|
||||||
|
@expected = 'hello-world'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 'wp-content'" do
|
||||||
|
@target_url = 'http://lamp/wordpress-3.4.1'
|
||||||
|
@fixture = fixtures_dir + '/wordpress-3.4.1.htm'
|
||||||
|
@expected = 'wp-content'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 'wp-content' if url has trailing slash" do
|
||||||
|
@target_url = 'http://lamp/wordpress-3.4.1/'
|
||||||
|
@fixture = fixtures_dir + '/wordpress-3.4.1.htm'
|
||||||
|
@expected = 'wp-content'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should find the default 'wp-content' dir even if the target_url is not the same (ie : the user supply an IP address and the url used in the code is a domain)" do
|
||||||
|
@target_url = 'http://192.168.1.103/wordpress-3.4.1/'
|
||||||
|
@fixture = fixtures_dir + '/wordpress-3.4.1.htm'
|
||||||
|
@expected = 'wp-content'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 'custom-content'" do
|
||||||
|
@target_url = 'http://lamp/wordpress-3.4.1-custom'
|
||||||
|
@fixture = fixtures_dir + '/wordpress-3.4.1-custom.htm'
|
||||||
|
@expected = 'custom-content'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 'custom content spaces'" do
|
||||||
|
@target_url = 'http://lamp/wordpress-3.4.1-custom'
|
||||||
|
@fixture = fixtures_dir + '/wordpress-3.4.1-custom-with-spaces.htm'
|
||||||
|
@expected = 'custom content spaces'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 'custom-dir/subdir/content'" do
|
||||||
|
@target_url = 'http://lamp/wordpress-3.4.1-custom'
|
||||||
|
@fixture = fixtures_dir + '/wordpress-3.4.1-custom-subdirectories.htm'
|
||||||
|
@expected = 'custom-dir/subdir/content'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should also check in src attributes' do
|
||||||
|
@target_url = 'http://lamp/wordpress-3.4.1'
|
||||||
|
@fixture = fixtures_dir + '/wordpress-3.4.1-in-src.htm'
|
||||||
|
@expected = 'wp-content'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should find the location even if the src or href goes in the plugins dir' do
|
||||||
|
@target_url = 'http://wordpress-3.4.1-in-plugins.htm'
|
||||||
|
@fixture = fixtures_dir + '/wordpress-3.4.1-in-plugins.htm'
|
||||||
|
@expected = 'wp-content'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should not detect facebook.com as a custom wp-content directory' do
|
||||||
|
@target_url = 'http://lamp.localhost/'
|
||||||
|
@fixture = fixtures_dir + '/facebook-detection.htm'
|
||||||
|
@expected = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#default_wp_content_dir_exists?' do
|
||||||
|
after :each do
|
||||||
|
@wp_target = WpTarget.new('http://lamp.localhost/')
|
||||||
|
stub_request(:get, @wp_target.url).to_return(:status => 200, :body => 'homepage') # homepage request
|
||||||
|
|
||||||
|
@wp_target.default_wp_content_dir_exists?.should === @expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false if wp-content returns an invalid response code' do
|
||||||
|
stub_request(:get, /.*\/wp-content\/?$/).to_return(:status => 404, :body => '') # default dir request
|
||||||
|
stub_request(:get, /.*\.html$/).to_return(:status => 404, :body => '') # 404 hash request
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false if wp-content and homepage have same bodies' do
|
||||||
|
stub_request(:get, /.*\/wp-content\/?$/).to_return(:status => 200, :body => 'homepage') # default dir request
|
||||||
|
stub_request(:get, /.*\.html$/).to_return(:status => 404, :body => '404!') # 404 hash request
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false if wp-content and 404 page have same bodies' do
|
||||||
|
stub_request(:get, /.*\/wp-content\/?$/).to_return(:status => 200, :body => '404!') # default dir request
|
||||||
|
stub_request(:get, /.*\.html$/).to_return(:status => 404, :body => '404!') # 404 hash request
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true if wp-content, 404 page and hoempage return different bodies' do
|
||||||
|
stub_request(:get, /.*\/wp-content\/?$/).to_return(:status => 200, :body => '') # default dir request
|
||||||
|
stub_request(:get, /.*\.html$/).to_return(:status => 200, :body => '404!') # 404 hash request
|
||||||
|
@expected = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#wp_plugins_dir' do
|
||||||
|
after :each do
|
||||||
|
@wp_target.wp_plugins_dir.should === @expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the string set in the initialize method' do
|
||||||
|
@wp_target = WpTarget.new('http://example.localhost/', options.merge(wp_content_dir: 'asdf', wp_plugins_dir: 'custom-plugins'))
|
||||||
|
@expected = 'custom-plugins'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns 'custom/plugins'" do
|
||||||
|
@wp_target = WpTarget.new('http://example.localhost/', options.merge(wp_content_dir: 'custom', wp_plugins_dir: nil))
|
||||||
|
@expected = 'custom/plugins'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#wp_plugins_dir_exists?' do
|
||||||
|
let(:wp_target) { WpTarget.new('http://example.localhost/', custom_options) }
|
||||||
|
let(:custom_options) { options.merge(wp_content_dir: 'asdf', wp_plugins_dir: 'custom-plugins') }
|
||||||
|
let(:url) { wp_target.uri.merge(wp_target.wp_plugins_dir).to_s }
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
stub_request(:get, url).to_return(status: 200)
|
||||||
|
wp_target.wp_plugins_dir_exists?.should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
stub_request(:get, url).to_return(status: 404)
|
||||||
|
wp_target.wp_plugins_dir_exists?.should == false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
37
spec/shared_examples/wp_target/wp_full_path_disclosure.rb
Normal file
37
spec/shared_examples/wp_target/wp_full_path_disclosure.rb
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
shared_examples 'WpTarget::WpFullPathDisclosure' do
|
||||||
|
|
||||||
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/wp_full_path_disclosure' }
|
||||||
|
|
||||||
|
describe '#full_path_disclosure_url' do
|
||||||
|
it 'returns http://example.localhost/wp-includes/rss-functions.php' do
|
||||||
|
wp_target.full_path_disclosure_url.should === 'http://example.localhost/wp-includes/rss-functions.php'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#has_full_path_disclosure?' do
|
||||||
|
after do
|
||||||
|
stub_request(:get, wp_target.full_path_disclosure_url).
|
||||||
|
to_return(@stub)
|
||||||
|
|
||||||
|
wp_target.has_full_path_disclosure?.should === @expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false on a 404' do
|
||||||
|
@stub = { status: 404 }
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false if no fpd found (blank page for example)' do
|
||||||
|
@stub = { status: 200, body: '' }
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
@stub = { status: 200, body: File.new(fixtures_dir + '/rss-functions-disclosure.php') }
|
||||||
|
@expected = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
89
spec/shared_examples/wp_target/wp_login_protection.rb
Normal file
89
spec/shared_examples/wp_target/wp_login_protection.rb
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
shared_examples 'WpTarget::WpLoginProtection' do
|
||||||
|
|
||||||
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/wp_login_protection' }
|
||||||
|
|
||||||
|
before { wp_target.stub(:wp_plugins_dir).and_return('wp-content/plugins') }
|
||||||
|
|
||||||
|
# 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 = WpTarget::WpLoginProtection::LOGIN_PROTECTION_METHOD_PATTERN
|
||||||
|
fixtures = %w{
|
||||||
|
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 = %w{better_wp_security simple_login_lockdown login_security_solution limit_login_attempts bluetrait_event_viewer}
|
||||||
|
|
||||||
|
after :each do
|
||||||
|
stub_request_to_fixture(url: 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, wp_target.send(special_plugin_call_url_symbol).to_s).to_return(status: status_code)
|
||||||
|
end
|
||||||
|
|
||||||
|
wp_target.send(@symbol_to_call).should === @expected
|
||||||
|
end
|
||||||
|
|
||||||
|
self.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} returns #{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: login_url, fixture: @fixture)
|
||||||
|
stub_request(:get, wp_target.send(:better_wp_security_url).to_s).to_return(status: 404)
|
||||||
|
stub_request(:get, wp_target.send(:simple_login_lockdown_url).to_s).to_return(status: 404)
|
||||||
|
stub_request(:get, wp_target.send(:login_security_solution_url).to_s).to_return(status: 404)
|
||||||
|
stub_request(:get, wp_target.send(:limit_login_attempts_url).to_s).to_return(status: 404)
|
||||||
|
stub_request(:get, wp_target.send(:bluetrait_event_viewer_url).to_s).to_return(status: 404)
|
||||||
|
|
||||||
|
wp_target.login_protection_plugin().should == @plugin_expected
|
||||||
|
wp_target.has_login_protection?.should === @has_protection_expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns nil if no protection is present' do
|
||||||
|
@fixture = fixtures_dir + '/wp-login-clean.php'
|
||||||
|
@plugin_expected = nil
|
||||||
|
@has_protection_expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a login-lockdown WpPlugin object' do
|
||||||
|
@fixture = fixtures_dir + '/wp-login-login_lockdown.php'
|
||||||
|
@plugin_expected = WpPlugin.new(wp_target.uri, name: 'login-lockdown')
|
||||||
|
@has_protection_expected = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a login-lock WpPlugin object' do
|
||||||
|
@fixture = fixtures_dir + '/wp-login-login_lock.php'
|
||||||
|
@plugin_expected = WpPlugin.new(wp_target.uri, name: 'login-lock')
|
||||||
|
@has_protection_expected = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
37
spec/shared_examples/wp_target/wp_readme.rb
Normal file
37
spec/shared_examples/wp_target/wp_readme.rb
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
shared_examples 'WpTarget::WpReadme' do
|
||||||
|
|
||||||
|
let(:fixtures_dir) { SPEC_FIXTURES_WPSCAN_WP_TARGET_DIR + '/wp_readme' }
|
||||||
|
|
||||||
|
describe '#readme_url' do
|
||||||
|
it 'returns http://example.localhost/readme.html' do
|
||||||
|
wp_target.readme_url.should === "#{wp_target.uri}readme.html"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#has_readme?' do
|
||||||
|
after do
|
||||||
|
stub_request(:get, wp_target.readme_url).to_return(@stub)
|
||||||
|
|
||||||
|
wp_target.has_readme?.should === @expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false on a 404' do
|
||||||
|
@stub = { status: 404 }
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true if it exists' do
|
||||||
|
@stub = { status: 200, body: File.new(fixtures_dir + '/readme-3.2.1.html') }
|
||||||
|
@expected = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# http://code.google.com/p/wpscan/issues/detail?id=108
|
||||||
|
it 'returns true even if the readme.html is not in english' do
|
||||||
|
@stub = { status: 200, body: File.new(fixtures_dir + '/readme-3.3.2-fr.html') }
|
||||||
|
@expected = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
96
spec/shared_examples/wp_target/wp_registrable.rb
Normal file
96
spec/shared_examples/wp_target/wp_registrable.rb
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
shared_examples 'WpTarget::WpRegistrable' do
|
||||||
|
|
||||||
|
let(:signup_url) { wp_target.uri.merge('wp-signup.php').to_s }
|
||||||
|
|
||||||
|
describe '#registration_url' do
|
||||||
|
after { wp_target.registration_url.should === @expected }
|
||||||
|
|
||||||
|
context 'when multisite' do
|
||||||
|
it 'returns the signup url' do
|
||||||
|
wp_target.stub(:multisite?).and_return(true)
|
||||||
|
|
||||||
|
@expected = signup_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not multisite' do
|
||||||
|
it 'returns the login url with ?action=register' do
|
||||||
|
wp_target.stub(:multisite?).and_return(false)
|
||||||
|
|
||||||
|
@expected = login_url + '?action=register'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#registration_enabled?' do
|
||||||
|
after do
|
||||||
|
wp_target.stub(:multisite?).and_return(multisite)
|
||||||
|
stub_request(:get, wp_target.registration_url.to_s).to_return(@stub)
|
||||||
|
|
||||||
|
wp_target.registration_enabled?.should === @expected
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when multisite' do
|
||||||
|
let(:multisite) { true }
|
||||||
|
it 'returns false (multisite)' do
|
||||||
|
@stub = { status: 302, headers: { 'Location' => 'wp-login.php?registration=disabled' } }
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true (multisite)' do
|
||||||
|
@stub = { status: 200, body: %{<form id="setupform" method="post" action="wp-signup.php">} }
|
||||||
|
@expected = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when not multisite' do
|
||||||
|
let(:multisite) { false }
|
||||||
|
|
||||||
|
it 'returns false (not multisite)' do
|
||||||
|
@stub = { status: 302, headers: { 'Location' => 'wp-login.php?registration=disabled' } }
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true (not multisite)' do
|
||||||
|
@stub = { status: 200, body: %{<form name="registerform" id="registerform" action="wp-login.php"} }
|
||||||
|
@expected = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
@stub = { status: 500 }
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#multisite?' do
|
||||||
|
after do
|
||||||
|
stub_request(:get, signup_url).to_return(@stub)
|
||||||
|
|
||||||
|
wp_target.multisite?.should === @expected
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
@stub = { status: 302, headers: { 'Location' => 'wp-login.php?action=register' } }
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
@stub = { status: 302, headers: { 'Location' => 'http://example.localhost/wp-signup.php' } }
|
||||||
|
@expected = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns true' do
|
||||||
|
@stub = { status: 200 }
|
||||||
|
@expected = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
@stub = { status: 500 }
|
||||||
|
@expected = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -21,7 +21,7 @@ MODELS_FIXTURES = SPEC_FIXTURES_DIR + '/common/models'
|
|||||||
COLLECTIONS_FIXTURES = SPEC_FIXTURES_DIR + '/common/collections'
|
COLLECTIONS_FIXTURES = SPEC_FIXTURES_DIR + '/common/collections'
|
||||||
|
|
||||||
# Load all the shared examples
|
# Load all the shared examples
|
||||||
require_files_from_directory(SHARED_EXAMPLES_DIR)
|
require_files_from_directory(SHARED_EXAMPLES_DIR, '**/*.rb')
|
||||||
|
|
||||||
def count_files_in_dir(absolute_dir_path, files_pattern = '*')
|
def count_files_in_dir(absolute_dir_path, files_pattern = '*')
|
||||||
Dir.glob(File.join(absolute_dir_path, files_pattern)).count
|
Dir.glob(File.join(absolute_dir_path, files_pattern)).count
|
||||||
|
|||||||
Reference in New Issue
Block a user