WPSTools plugins mode activated
This commit is contained in:
89
lib/common/custom_option_parser.rb
Normal file
89
lib/common/custom_option_parser.rb
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# WPScan - WordPress Security Scanner
|
||||||
|
# Copyright (C) 2012-2013
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#++
|
||||||
|
|
||||||
|
class CustomOptionParser < OptionParser
|
||||||
|
|
||||||
|
attr_reader :symbols_used
|
||||||
|
|
||||||
|
def initialize(banner = nil, width = 32, indent = ' ' * 4)
|
||||||
|
@results = {}
|
||||||
|
@symbols_used = []
|
||||||
|
super(banner, width, indent)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# param Array(Array) or Array options
|
||||||
|
def add(options)
|
||||||
|
if options.is_a?(Array)
|
||||||
|
if options[0].is_a?(Array)
|
||||||
|
options.each do |option|
|
||||||
|
add_option(option)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
add_option(options)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Options must be at least an Array, or an Array(Array). #{options.class} supplied"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# param Array option
|
||||||
|
def add_option(option)
|
||||||
|
if option.is_a?(Array)
|
||||||
|
option_symbol = CustomOptionParser::option_to_symbol(option)
|
||||||
|
|
||||||
|
unless @symbols_used.include?(option_symbol)
|
||||||
|
@symbols_used << option_symbol
|
||||||
|
|
||||||
|
self.on(*option) do |arg|
|
||||||
|
@results[option_symbol] = arg
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "The option #{option_symbol} is already used !"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "The option must be an array, #{option.class} supplied : '#{option}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# return Hash
|
||||||
|
def results(argv = default_argv)
|
||||||
|
self.parse!(argv) if @results.empty?
|
||||||
|
|
||||||
|
@results
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
# param Array option
|
||||||
|
def self.option_to_symbol(option)
|
||||||
|
option_name = nil
|
||||||
|
|
||||||
|
option.each do |option_attr|
|
||||||
|
if option_attr =~ /^--/
|
||||||
|
option_name = option_attr
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if option_name
|
||||||
|
option_name = option_name.gsub(/^--/, '').gsub(/-/, '_').gsub(/ .*$/, '')
|
||||||
|
:"#{option_name}"
|
||||||
|
else
|
||||||
|
raise "Could not find the option name for #{option}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
40
lib/common/plugins/plugin.rb
Normal file
40
lib/common/plugins/plugin.rb
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# WPScan - WordPress Security Scanner
|
||||||
|
# Copyright (C) 2012-2013
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#++
|
||||||
|
|
||||||
|
class Plugin
|
||||||
|
|
||||||
|
attr_reader :author, :registered_options
|
||||||
|
|
||||||
|
def initialize(infos = {})
|
||||||
|
@author = infos[:author]
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(options = {})
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
# param Array options
|
||||||
|
def register_options(*options)
|
||||||
|
options.each do |option|
|
||||||
|
unless option.is_a?(Array)
|
||||||
|
raise "Each option must be an array, #{option.class} supplied"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@registered_options = options
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
55
lib/common/plugins/plugins.rb
Normal file
55
lib/common/plugins/plugins.rb
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# WPScan - WordPress Security Scanner
|
||||||
|
# Copyright (C) 2012-2013
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#++
|
||||||
|
|
||||||
|
class Plugins < Array
|
||||||
|
|
||||||
|
attr_reader :option_parser
|
||||||
|
|
||||||
|
def initialize(option_parser = nil)
|
||||||
|
if option_parser
|
||||||
|
if option_parser.is_a?(CustomOptionParser)
|
||||||
|
@option_parser = option_parser
|
||||||
|
else
|
||||||
|
raise "The parser must be an instance of CustomOptionParser, #{option_parser.class} supplied"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@option_parser = CustomOptionParser.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# param Array(Plugin) plugins
|
||||||
|
def register(*plugins)
|
||||||
|
plugins.each do |plugin|
|
||||||
|
register_plugin(plugin)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# param Plugin plugin
|
||||||
|
def register_plugin(plugin)
|
||||||
|
if plugin.is_a?(Plugin)
|
||||||
|
self << plugin
|
||||||
|
|
||||||
|
# A plugin may not have options
|
||||||
|
if plugin_options = plugin.registered_options
|
||||||
|
@option_parser.add(plugin_options)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "The argument must be an instance of Plugin, #{plugin.class} supplied"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -16,17 +16,22 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#++
|
#++
|
||||||
|
|
||||||
LIB_DIR = File.dirname(__FILE__)
|
LIB_DIR = File.dirname(__FILE__)
|
||||||
ROOT_DIR = File.expand_path(LIB_DIR + '/..') # expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
|
ROOT_DIR = File.expand_path(LIB_DIR + '/..') # expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
|
||||||
DATA_DIR = ROOT_DIR + "/data"
|
DATA_DIR = ROOT_DIR + "/data"
|
||||||
CONF_DIR = ROOT_DIR + "/conf"
|
CONF_DIR = ROOT_DIR + "/conf"
|
||||||
CACHE_DIR = ROOT_DIR + "/cache"
|
CACHE_DIR = ROOT_DIR + "/cache"
|
||||||
WPSCAN_LIB_DIR = LIB_DIR + "/wpscan"
|
WPSCAN_LIB_DIR = LIB_DIR + "/wpscan"
|
||||||
WPSTOOLS_LIB_DIR = LIB_DIR + "/wpstools"
|
WPSTOOLS_LIB_DIR = LIB_DIR + "/wpstools"
|
||||||
UPDATER_LIB_DIR = LIB_DIR + "/updater"
|
UPDATER_LIB_DIR = LIB_DIR + "/updater"
|
||||||
LOG_FILE = ROOT_DIR + "/log.txt"
|
COMMON_LIB_DIR = LIB_DIR + "/common"
|
||||||
|
LOG_FILE = ROOT_DIR + "/log.txt"
|
||||||
|
# Plugins directories
|
||||||
|
COMON_PLUGINS_DIR = COMMON_LIB_DIR + "/plugins"
|
||||||
|
WPSCAN_PLUGINS_DIR = WPSCAN_LIB_DIR + "/plugins"
|
||||||
|
WPSTOOLS_PLUGINS_DIR = WPSTOOLS_LIB_DIR + "/plugins"
|
||||||
|
|
||||||
WPSCAN_VERSION = "2.0"
|
WPSCAN_VERSION = "2.0"
|
||||||
|
|
||||||
require "#{LIB_DIR}/environment"
|
require "#{LIB_DIR}/environment"
|
||||||
|
|
||||||
@@ -39,6 +44,9 @@ def require_files_from_directory(absolute_dir_path, files_pattern = "*.rb")
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#require_files_from_directory(COMMON_LIB_DIR)
|
||||||
|
require_files_from_directory(COMMON_LIB_DIR, "**/*.rb")
|
||||||
|
|
||||||
# Add protocol
|
# Add protocol
|
||||||
def add_http_protocol(url)
|
def add_http_protocol(url)
|
||||||
url =~ /^https?:/ ? url : "http://#{url}"
|
url =~ /^https?:/ ? url : "http://#{url}"
|
||||||
@@ -148,9 +156,9 @@ def get_metasploit_url(module_path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Override for puts to enable logging
|
# Override for puts to enable logging
|
||||||
def puts(o = "")
|
#def puts(o = "")
|
||||||
# remove color for logging
|
# remove color for logging
|
||||||
temp = o.gsub(/\e\[\d+m(?<text>.*)?\e\[0m/, '\k<text>')
|
#temp = o.gsub(/\e\[\d+m(?<text>.*)?\e\[0m/, '\k<text>')
|
||||||
File.open(LOG_FILE, "a+") { |f| f.puts(temp) }
|
#File.open(LOG_FILE, "a+") { |f| f.puts(temp) }
|
||||||
super(o)
|
#super(o)
|
||||||
end
|
#end
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ begin
|
|||||||
# Standard libs
|
# Standard libs
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'getoptlong'
|
require 'getoptlong'
|
||||||
|
require 'optparse' # Will replace getoptlong
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'time'
|
require 'time'
|
||||||
require 'resolv'
|
require 'resolv'
|
||||||
|
|||||||
148
lib/wpstools/plugins/checker/checker_plugin.rb
Normal file
148
lib/wpstools/plugins/checker/checker_plugin.rb
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
# WPScan - WordPress Security Scanner
|
||||||
|
# Copyright (C) 2012-2013
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#++
|
||||||
|
|
||||||
|
class CheckerPlugin < Plugin
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super(
|
||||||
|
:author => "@wpscanteam - @erwanlr"
|
||||||
|
)
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
["--check-vuln-ref-urls", "--cvru", "Check all the vulnerabilities reference urls for 404"],
|
||||||
|
["--check-local-vulnerable-files LOCAL_DIRECTORY", "--clvf", "Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells"]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(options = {})
|
||||||
|
if options[:check_vuln_ref_urls]
|
||||||
|
check_vuln_ref_urls
|
||||||
|
end
|
||||||
|
|
||||||
|
if options[:check_local_vulnerable_files]
|
||||||
|
check_local_vulnerable_files(options[:check_local_vulnerable_files])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_vuln_ref_urls
|
||||||
|
vuln_ref_files = ["plugin_vulns.xml", "theme_vulns.xml", "wp_vulns.xml"]
|
||||||
|
error_codes = [404, 500, 403]
|
||||||
|
not_found_regexp = %r{No Results Found|error 404|ID Invalid or Not Found}i
|
||||||
|
|
||||||
|
puts "[+] Checking vulnerabilities reference urls"
|
||||||
|
|
||||||
|
vuln_ref_files.each do |vuln_ref_file|
|
||||||
|
xml = Nokogiri::XML(File.open(DATA_DIR + '/' + vuln_ref_file)) do |config|
|
||||||
|
config.noblanks
|
||||||
|
end
|
||||||
|
|
||||||
|
urls = []
|
||||||
|
xml.xpath("//reference").each { |node| urls << node.text }
|
||||||
|
|
||||||
|
urls.uniq!
|
||||||
|
|
||||||
|
dead_urls = []
|
||||||
|
queue_count = 0
|
||||||
|
request_count = 0
|
||||||
|
browser = Browser.instance
|
||||||
|
hydra = browser.hydra
|
||||||
|
number_of_urls = urls.size
|
||||||
|
|
||||||
|
urls.each do |url|
|
||||||
|
request = browser.forge_request(url, { :cache_timeout => 0, :follow_location => true })
|
||||||
|
request_count += 1
|
||||||
|
|
||||||
|
request.on_complete do |response|
|
||||||
|
print "\r [+] Checking #{vuln_ref_file} #{number_of_urls} total ... #{(request_count * 100) / number_of_urls}% complete."
|
||||||
|
|
||||||
|
if error_codes.include?(response.code) or not_found_regexp.match(response.body)
|
||||||
|
dead_urls << url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hydra.queue(request)
|
||||||
|
queue_count += 1
|
||||||
|
|
||||||
|
if queue_count == browser.max_threads
|
||||||
|
hydra.run
|
||||||
|
queue_count = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hydra.run
|
||||||
|
puts
|
||||||
|
unless dead_urls.empty?
|
||||||
|
dead_urls.each { |url| puts " Not Found #{url}" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_local_vulnerable_files(dir_to_scan)
|
||||||
|
if Dir::exist?(dir_to_scan)
|
||||||
|
xml_file = DATA_DIR + "/local_vulnerable_files.xml"
|
||||||
|
local_hashes = {}
|
||||||
|
file_extension_to_scan = "*.{js,php,swf,html,htm}"
|
||||||
|
|
||||||
|
print "[+] Generating local hashes ... "
|
||||||
|
|
||||||
|
Dir[File::join(dir_to_scan, "**", file_extension_to_scan)].each do |filename|
|
||||||
|
sha1sum = Digest::SHA1.file(filename).hexdigest
|
||||||
|
|
||||||
|
if local_hashes.has_key?(sha1sum)
|
||||||
|
local_hashes[sha1sum] << filename
|
||||||
|
else
|
||||||
|
local_hashes[sha1sum] = [filename]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "done."
|
||||||
|
|
||||||
|
puts "[+] Checking for vulnerable files ..."
|
||||||
|
|
||||||
|
xml = Nokogiri::XML(File.open(xml_file)) do |config|
|
||||||
|
config.noblanks
|
||||||
|
end
|
||||||
|
|
||||||
|
xml.xpath("//hash").each do |node|
|
||||||
|
sha1sum = node.attribute("sha1").text
|
||||||
|
|
||||||
|
if local_hashes.has_key?(sha1sum)
|
||||||
|
local_filenames = local_hashes[sha1sum]
|
||||||
|
vuln_title = node.search("title").text
|
||||||
|
vuln_filename = node.search("file").text
|
||||||
|
vuln_refrence = node.search("reference").text
|
||||||
|
|
||||||
|
puts " #{vuln_filename} found :"
|
||||||
|
puts " | Location(s):"
|
||||||
|
local_filenames.each do |file|
|
||||||
|
puts " | - #{file}"
|
||||||
|
end
|
||||||
|
puts " |"
|
||||||
|
puts " | Title: #{vuln_title}"
|
||||||
|
puts " | Refrence: #{vuln_refrence}" if !vuln_refrence.empty?
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "done."
|
||||||
|
|
||||||
|
else
|
||||||
|
puts "The supplied directory '#{dir_to_scan}' does not exist"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
#++
|
#++
|
||||||
|
|
||||||
# This tool generates a list to use for plugin and theme enumeration
|
# This tool generates a list to use for plugin and theme enumeration
|
||||||
class Generate_List
|
class GenerateList
|
||||||
|
|
||||||
attr_accessor :verbose
|
attr_accessor :verbose
|
||||||
|
|
||||||
@@ -70,14 +70,14 @@ class Generate_List
|
|||||||
|
|
||||||
def generate_full_list
|
def generate_full_list
|
||||||
set_file_name(:full)
|
set_file_name(:full)
|
||||||
items = Svn_Parser.new(@svn_url, @verbose).parse
|
items = SvnParser.new(@svn_url, @verbose).parse
|
||||||
save items
|
save items
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_popular_list(pages)
|
def generate_popular_list(pages)
|
||||||
set_file_name(:popular)
|
set_file_name(:popular)
|
||||||
popular = get_popular_items(pages)
|
popular = get_popular_items(pages)
|
||||||
items = Svn_Parser.new(@svn_url, @verbose).parse(popular)
|
items = SvnParser.new(@svn_url, @verbose).parse(popular)
|
||||||
save items
|
save items
|
||||||
end
|
end
|
||||||
|
|
||||||
69
lib/wpstools/plugins/list_generator/list_generator_plugin.rb
Normal file
69
lib/wpstools/plugins/list_generator/list_generator_plugin.rb
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# WPScan - WordPress Security Scanner
|
||||||
|
# Copyright (C) 2012-2013
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#++
|
||||||
|
|
||||||
|
class ListGeneratorPlugin < Plugin
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
super(
|
||||||
|
:author => "WPScanTeam - @FireFart"
|
||||||
|
)
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
["--generate-plugin-list [NUMBER_OF_PAGES]", "--gpl", Integer, "Generate a new data/plugins.txt file. (supply number of *pages* to parse, default : 150)"],
|
||||||
|
["--generate-full-plugin-list", "--gfpl", "Generate a new full data/plugins.txt file"],
|
||||||
|
|
||||||
|
["--generate-theme-list [NUMBER_OF_PAGES]", "--gtl", Integer, "Generate a new data/themes.txt file. (supply number of *pages* to parse, default : 150)"],
|
||||||
|
["--generate-full-theme-list", "--gftl", "Generate a new full data/themes.txt file"],
|
||||||
|
|
||||||
|
["--generate-all", "--ga", "Generate a new full plugins, full themes, popular plugins and popular themes list"],
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(options = {})
|
||||||
|
verbose = options[:verbose] || false
|
||||||
|
generate_all = options[:generate_all] || false
|
||||||
|
|
||||||
|
if options.has_key?(:generate_plugin_list) || generate_all
|
||||||
|
number_of_pages = options[:generate_plugin_list] || 150
|
||||||
|
|
||||||
|
puts "[+] Generating new most popular plugin list"
|
||||||
|
puts
|
||||||
|
GenerateList.new('plugins', verbose).generate_popular_list(number_of_pages)
|
||||||
|
end
|
||||||
|
|
||||||
|
if options[:generate_full_plugin_list] || generate_all
|
||||||
|
puts "[+] Generating new full plugin list"
|
||||||
|
puts
|
||||||
|
GenerateList.new('plugins', verbose).generate_full_list
|
||||||
|
end
|
||||||
|
|
||||||
|
if options.has_key?(:generate_theme_list) || generate_all
|
||||||
|
number_of_pages = options[:generate_theme_list] || 150
|
||||||
|
|
||||||
|
puts "[+] Generating new most popular theme list"
|
||||||
|
puts
|
||||||
|
GenerateList.new('themes', verbose).generate_popular_list(number_of_pages)
|
||||||
|
end
|
||||||
|
|
||||||
|
if options[:generate_full_theme_list] || generate_all
|
||||||
|
puts "[+] Generating new full theme list"
|
||||||
|
puts
|
||||||
|
GenerateList.new('themes', verbose).generate_full_list
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
#++
|
#++
|
||||||
|
|
||||||
# This Class Parses SVN Repositories via HTTP
|
# This Class Parses SVN Repositories via HTTP
|
||||||
class Svn_Parser
|
class SvnParser
|
||||||
|
|
||||||
attr_accessor :verbose, :svn_root, :keep_empty_dirs
|
attr_accessor :verbose, :svn_root, :keep_empty_dirs
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
require File.expand_path(File.dirname(__FILE__) + '/../common_helper')
|
require File.expand_path(File.dirname(__FILE__) + '/../common_helper')
|
||||||
|
|
||||||
require_files_from_directory(WPSTOOLS_LIB_DIR)
|
require_files_from_directory(WPSTOOLS_LIB_DIR)
|
||||||
|
require_files_from_directory(WPSTOOLS_PLUGINS_DIR, "**/*.rb")
|
||||||
|
|
||||||
def usage()
|
def usage()
|
||||||
script_name = $0
|
script_name = $0
|
||||||
@@ -28,19 +29,19 @@ def usage()
|
|||||||
puts "Examples:"
|
puts "Examples:"
|
||||||
puts
|
puts
|
||||||
puts "- Generate a new 'most popular' plugin list, up to 150 pages ..."
|
puts "- Generate a new 'most popular' plugin list, up to 150 pages ..."
|
||||||
puts "ruby #{script_name} --generate_plugin_list 150"
|
puts "ruby #{script_name} --generate-plugin-list 150"
|
||||||
puts
|
puts
|
||||||
puts "- Generate a new full plugin list"
|
puts "- Generate a new full plugin list"
|
||||||
puts "ruby #{script_name} --generate_full_plugin_list"
|
puts "ruby #{script_name} --generate-full-plugin-list"
|
||||||
puts
|
puts
|
||||||
puts "- Generate a new 'most popular' theme list, up to 150 pages ..."
|
puts "- Generate a new 'most popular' theme list, up to 150 pages ..."
|
||||||
puts "ruby #{script_name} --generate_theme_list 150"
|
puts "ruby #{script_name} --generate-theme-list 150"
|
||||||
puts
|
puts
|
||||||
puts "- Generate a new full theme list"
|
puts "- Generate a new full theme list"
|
||||||
puts "ruby #{script_name} --generate_full_theme_list"
|
puts "ruby #{script_name} --generate-full-theme-list"
|
||||||
puts
|
puts
|
||||||
puts "- Generate all list"
|
puts "- Generate all list"
|
||||||
puts "ruby #{script_name} --generate_all"
|
puts "ruby #{script_name} --generate-all"
|
||||||
puts
|
puts
|
||||||
puts "Locally scan a wordpress installation for vulnerable files or shells"
|
puts "Locally scan a wordpress installation for vulnerable files or shells"
|
||||||
puts "ruby #{script_name} --check-local-vulnerable-files /var/www/wordpress/"
|
puts "ruby #{script_name} --check-local-vulnerable-files /var/www/wordpress/"
|
||||||
@@ -48,24 +49,3 @@ def usage()
|
|||||||
puts "See README for further information."
|
puts "See README for further information."
|
||||||
puts
|
puts
|
||||||
end
|
end
|
||||||
|
|
||||||
def help()
|
|
||||||
puts "Help :"
|
|
||||||
puts
|
|
||||||
puts "--help | -h This help screen."
|
|
||||||
puts "--Verbose | -v Verbose output."
|
|
||||||
puts "--update | -u Update to the latest revision."
|
|
||||||
puts "--generate_plugin_list [number of pages] Generate a new data/plugins.txt file. (supply number of *pages* to parse, default : 150)"
|
|
||||||
puts "--gpl Alias for --generate_plugin_list"
|
|
||||||
puts "--generate_full_plugin_list Generate a new full data/plugins.txt file"
|
|
||||||
puts "--gfpl Alias for --generate_full_plugin_list"
|
|
||||||
puts "--generate_theme_list [number of pages] Generate a new data/themes.txt file. (supply number of *pages* to parse, default : 150)"
|
|
||||||
puts "--gtl Alias for --generate_theme_list"
|
|
||||||
puts "--generate_full_theme_list Generate a new full data/themes.txt file"
|
|
||||||
puts "--gftl Alias for --generate_full_theme_list"
|
|
||||||
puts "--generate_all Generate a new full plugins, full themes, popular plugins and popular themes list"
|
|
||||||
puts "--ga Alias for --generate_all"
|
|
||||||
puts "--check-vuln-ref-urls | --cvru Check all the vulnerabilities reference urls for 404"
|
|
||||||
puts "--check-local-vulnerable-files | --clvf <local directory> Perform a recursive scan in the <local directory> to find vulnerable files or shells"
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
|
|||||||
153
spec/lib/common/custom_option_parser_spec.rb
Normal file
153
spec/lib/common/custom_option_parser_spec.rb
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
describe CustomOptionParser do
|
||||||
|
|
||||||
|
let(:parser) { CustomOptionParser.new }
|
||||||
|
|
||||||
|
describe "#new" do
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "::option_to_symbol" do
|
||||||
|
after :each do
|
||||||
|
if @exception
|
||||||
|
expect { CustomOptionParser::option_to_symbol(@option) }.to raise_error(@exception)
|
||||||
|
else
|
||||||
|
CustomOptionParser::option_to_symbol(@option).should === @expected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "without REQUIRED or OPTIONAL arguments" do
|
||||||
|
context "with short option" do
|
||||||
|
it "should return :test" do
|
||||||
|
@option = ["-t", "--test", "Testing"]
|
||||||
|
@expected = :test
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should :its_a_long_option" do
|
||||||
|
@option = ["-l", "--its-a-long-option", "Testing '-' replacement"]
|
||||||
|
@expected = :its_a_long_option
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "without short option" do
|
||||||
|
it "should return :long" do
|
||||||
|
@option = ["--long", "The method should find the option name ('long')"]
|
||||||
|
@expected = :long
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return :long_option" do
|
||||||
|
@option = ["--long-option", "No short !"]
|
||||||
|
@expected = :long_option
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "without long option" do
|
||||||
|
it "should raise an arror" do
|
||||||
|
@option = ["-v", "The long option is missing there"]
|
||||||
|
@exception = "Could not find the option name for [\"-v\", \"The long option is missing there\"]"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise an error" do
|
||||||
|
@option = ["The long option is missing there"]
|
||||||
|
@exception = "Could not find the option name for [\"The long option is missing there\"]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with multiple long option names (like alias)" do
|
||||||
|
it "should return :check_long and not :cl" do
|
||||||
|
@option = ["--check-long", "--cl"]
|
||||||
|
@expected = :check_long
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with REQUIRED or OPTIONAL arguments" do
|
||||||
|
it "should removed the OPTIONAL argument" do
|
||||||
|
@option = ["-p", "--page [PAGE_NUMBER]"]
|
||||||
|
@expected = :page
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should removed the REQUIRED argument" do
|
||||||
|
@option = ["--url TARGET_URL"]
|
||||||
|
@expected = :url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#add_option" do
|
||||||
|
context "exception throwing if" do
|
||||||
|
after :each do
|
||||||
|
expect { parser.add_option(@option) }.to raise_error(@exception)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "argument passed is not an Array" do
|
||||||
|
@option = "a simple String"
|
||||||
|
@exception = "The option must be an array, String supplied : 'a simple String'"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "option name is already used" do
|
||||||
|
@option = ["-v", "--verbose", "Verbose mode"]
|
||||||
|
parser.add_option(@option)
|
||||||
|
@exception = "The option verbose is already used !"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should have had 2 symbols (:verbose, :url) to @symbols_used" do
|
||||||
|
parser.add_option(["-v", "--verbose"])
|
||||||
|
parser.add_option(["--url TARGET_URL"])
|
||||||
|
|
||||||
|
parser.symbols_used.sort.should === [:url, :verbose]
|
||||||
|
end
|
||||||
|
|
||||||
|
context "parsing" do
|
||||||
|
before :each do
|
||||||
|
parser.add_option(["-u", "--url TARGET_URL", "Set the target url"])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise an error if an unknown option is supplied" do
|
||||||
|
expect { parser.parse!(["--verbose"]) }.to raise_error(OptionParser::InvalidOption)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise an error if an option require an argument which is not supplied" do
|
||||||
|
expect { parser.parse!(["--url"]) }.to raise_error(OptionParser::MissingArgument)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should retrieve the correct argument" do
|
||||||
|
parser.parse!(["-u", "iam_the_target"])
|
||||||
|
parser.results.should === { :url => "iam_the_target" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#add" do
|
||||||
|
it "should raise an error if the argument is not an Array or Array(Array)" do
|
||||||
|
expect { parser.add("Hello") }.to raise_error("Options must be at least an Array, or an Array(Array). String supplied")
|
||||||
|
end
|
||||||
|
|
||||||
|
before :each do
|
||||||
|
parser.add(["-u", "--url TARGET_URL"])
|
||||||
|
end
|
||||||
|
|
||||||
|
context "single option" do
|
||||||
|
it "should add the :url option, and retrieve the correct argument" do
|
||||||
|
parser.symbols_used.should === [ :url ]
|
||||||
|
parser.results(["-u", "target.com"]).should === { :url => "target.com" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "multiple options" do
|
||||||
|
it "should add 2 options, and retrieve the correct arguments" do
|
||||||
|
parser.add([
|
||||||
|
["-v", "--verbose"],
|
||||||
|
["--test [TEST_NUMBER]"]
|
||||||
|
])
|
||||||
|
|
||||||
|
parser.symbols_used.sort.should === [:test, :url, :verbose]
|
||||||
|
parser.results(["-u", "wp.com", "-v", "--test"]).should === { :test => nil, :url => "wp.com", :verbose => true }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
46
spec/lib/common/plugins/plugin_spec.rb
Normal file
46
spec/lib/common/plugins/plugin_spec.rb
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Plugin do
|
||||||
|
subject(:plugin) { Plugin.new }
|
||||||
|
|
||||||
|
describe "#new" do
|
||||||
|
context "with some infos" do
|
||||||
|
subject(:plugin) { Plugin.new(infos) }
|
||||||
|
let(:infos) { {:author => "John"} }
|
||||||
|
|
||||||
|
its(:author) { should === infos[:author] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#run" do
|
||||||
|
it "should raise a NotImplementedError" do
|
||||||
|
expect { plugin.run }.to raise_error(NotImplementedError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#register_options" do
|
||||||
|
after :each do
|
||||||
|
if @exception
|
||||||
|
expect { plugin.register_options(*@options) }.to raise_error(@exception)
|
||||||
|
else
|
||||||
|
plugin.register_options(*@options)
|
||||||
|
plugin.registered_options.sort.should === @expected.sort
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when an option is not an Array" do
|
||||||
|
it "should raise an error" do
|
||||||
|
@options = [["-v", "--verbose", "It's a valid option"], "Not a valid one"]
|
||||||
|
@exception = "Each option must be an array, String supplied"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when options are Arrays" do
|
||||||
|
it "should register the options" do
|
||||||
|
@options = [["-v", "--verbose", "Verbose mode"], ["-u", "--url TARGET_URL"]]
|
||||||
|
@expected = *@options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
95
spec/lib/common/plugins/plugins_spec.rb
Normal file
95
spec/lib/common/plugins/plugins_spec.rb
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
class TestPlugin < Plugin
|
||||||
|
def initialize
|
||||||
|
register_options(["-u", "--url"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AnotherPlugin < Plugin
|
||||||
|
def initialize
|
||||||
|
super(:author => "John")
|
||||||
|
# No Options
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Plugins do
|
||||||
|
subject(:plugins) { Plugins.new }
|
||||||
|
|
||||||
|
let(:test_plugin) { TestPlugin.new }
|
||||||
|
let(:another_plugin) { AnotherPlugin.new }
|
||||||
|
|
||||||
|
describe "#new" do
|
||||||
|
context "without argument" do
|
||||||
|
its(:option_parser) { should be_a CustomOptionParser }
|
||||||
|
|
||||||
|
it "should be an Array" do
|
||||||
|
plugins.should be_an Array
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with an option_parser argument" do
|
||||||
|
subject(:plugin) { Plugins.new(CustomOptionParser.new("the banner")) }
|
||||||
|
|
||||||
|
its(:option_parser) { should be_a CustomOptionParser }
|
||||||
|
its("option_parser.banner") { should === "the banner" }
|
||||||
|
|
||||||
|
it "should raise an eror if the parser is not an instance of CustomOptionParser" do
|
||||||
|
expect { Plugins.new(OptionParser.new) }.to raise_error("The parser must be an instance of CustomOptionParser, OptionParser supplied")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#register_plugin" do
|
||||||
|
after :each do
|
||||||
|
if @exception
|
||||||
|
expect { plugins.register_plugin(@plugin) }.to raise_error(@exception)
|
||||||
|
else
|
||||||
|
plugins.register_plugin(@plugin)
|
||||||
|
plugins.should include(@plugin)
|
||||||
|
plugins.should === @expected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the argument supplied is not an instance of Plugin" do
|
||||||
|
it "should raise an error" do
|
||||||
|
@plugin = "I'am a String"
|
||||||
|
@exception = "The argument must be an instance of Plugin, String supplied"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should register the plugin" do
|
||||||
|
@plugin = TestPlugin.new
|
||||||
|
@expected = [@plugin]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should register 2 plugins (the order is important)" do
|
||||||
|
plugins.register_plugin(test_plugin)
|
||||||
|
|
||||||
|
@plugin = another_plugin
|
||||||
|
@expected = [test_plugin, @plugin]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#register" do
|
||||||
|
after :each do
|
||||||
|
plugins.register(*@plugins_to_register)
|
||||||
|
|
||||||
|
@plugins_to_register.each do |plugin|
|
||||||
|
plugins.should include(plugin)
|
||||||
|
end
|
||||||
|
|
||||||
|
# For the correct order
|
||||||
|
plugins.should === @plugins_to_register
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should register 1 plugin" do
|
||||||
|
@plugins_to_register = [test_plugin]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should register 2 plugins" do
|
||||||
|
@plugins_to_register = [another_plugin, test_plugin]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
require File.expand_path(File.dirname(__FILE__) + "/../../wpstools_helper")
|
||||||
|
|
||||||
|
# TODO
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
require File.expand_path(File.dirname(__FILE__) + "/../../wpstools_helper")
|
||||||
|
|
||||||
|
# TODO
|
||||||
@@ -26,6 +26,7 @@ if RUBY_VERSION >= "1.9"
|
|||||||
add_filter "/spec/"
|
add_filter "/spec/"
|
||||||
add_filter "_helper.rb"
|
add_filter "_helper.rb"
|
||||||
add_filter "environment.rb"
|
add_filter "environment.rb"
|
||||||
|
add_filter "_plugin.rb"
|
||||||
|
|
||||||
# Unused files at this time
|
# Unused files at this time
|
||||||
add_filter "exploit.rb"
|
add_filter "exploit.rb"
|
||||||
|
|||||||
219
wpstools.rb
219
wpstools.rb
@@ -19,223 +19,30 @@
|
|||||||
#++
|
#++
|
||||||
|
|
||||||
$: << '.'
|
$: << '.'
|
||||||
require File.dirname(__FILE__) +'/lib/wpstools/wpstools_helper'
|
require File.dirname(__FILE__) + "/lib/wpstools/wpstools_helper"
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|
||||||
banner()
|
banner()
|
||||||
|
|
||||||
if ARGV.length == 0
|
option_parser = CustomOptionParser.new("Usage: ./wpstools.rb [options]", 60)
|
||||||
raise "No argument supplied\n#{usage()}"
|
option_parser.separator ""
|
||||||
end
|
option_parser.add(["-v", "--verbose", "Verbose output"])
|
||||||
|
|
||||||
# A better way to do that should be to create a wpstools_options.rb file like wpscan_options.rb
|
plugins = Plugins.new(option_parser)
|
||||||
# and a wps_options.rb with common options code
|
plugins.register(
|
||||||
options = GetoptLong.new(
|
CheckerPlugin.new,
|
||||||
["--help", "-h", GetoptLong::NO_ARGUMENT],
|
ListGeneratorPlugin.new
|
||||||
["--verbose", "-v", GetoptLong::NO_ARGUMENT],
|
|
||||||
["--generate_plugin_list", GetoptLong::OPTIONAL_ARGUMENT],
|
|
||||||
["--generate_full_plugin_list", GetoptLong::NO_ARGUMENT],
|
|
||||||
["--generate_theme_list", GetoptLong::OPTIONAL_ARGUMENT],
|
|
||||||
["--generate_full_theme_list", GetoptLong::NO_ARGUMENT],
|
|
||||||
["--generate_all", GetoptLong::NO_ARGUMENT],
|
|
||||||
["--gpl", GetoptLong::OPTIONAL_ARGUMENT], # Alias for --generate_plugin_list
|
|
||||||
["--gfpl", GetoptLong::OPTIONAL_ARGUMENT], # Alias for --generate_full_plugin_list
|
|
||||||
["--gtl", GetoptLong::OPTIONAL_ARGUMENT], # Alias for --generate_theme_list
|
|
||||||
["--gftl", GetoptLong::OPTIONAL_ARGUMENT], # Alias for --generate_full_theme_list
|
|
||||||
["--ga", GetoptLong::OPTIONAL_ARGUMENT], # Alias for --generate_all
|
|
||||||
["--update", "-u", GetoptLong::NO_ARGUMENT],
|
|
||||||
["--check-vuln-ref-urls", GetoptLong::NO_ARGUMENT],
|
|
||||||
["--cvru", GetoptLong::NO_ARGUMENT], # Alias for --check-vuln-ref-urls
|
|
||||||
["--check-local-vulnerable-files", GetoptLong::REQUIRED_ARGUMENT],
|
|
||||||
["--clvf", GetoptLong::REQUIRED_ARGUMENT] # Alias for --check-local-vulnerable-files
|
|
||||||
)
|
)
|
||||||
|
|
||||||
options.each do |option, argument|
|
options = option_parser.results
|
||||||
case option
|
|
||||||
when "--help"
|
|
||||||
help()
|
|
||||||
exit
|
|
||||||
when "--verbose"
|
|
||||||
@verbose = true
|
|
||||||
when "--generate_plugin_list", "--gpl"
|
|
||||||
if argument == ''
|
|
||||||
puts "Number of pages not supplied, defaulting to 150 pages ..."
|
|
||||||
@number_of_pages = 150
|
|
||||||
else
|
|
||||||
@number_of_pages = argument.to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
@generate_plugin_list = true
|
if options.empty?
|
||||||
when "--generate_theme_list", "--gtl"
|
raise "No option supplied\n\n#{option_parser}"
|
||||||
if argument == ''
|
|
||||||
puts "Number of pages not supplied, defaulting to 150 pages ..."
|
|
||||||
@number_of_pages = 150
|
|
||||||
else
|
|
||||||
@number_of_pages = argument.to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
@generate_theme_list = true
|
|
||||||
when "--update"
|
|
||||||
@update = true
|
|
||||||
when "--generate_full_plugin_list", "--gfpl"
|
|
||||||
@generate_full_plugin_list = true
|
|
||||||
when "--generate_full_theme_list", "--gftl"
|
|
||||||
@generate_full_theme_list = true
|
|
||||||
when "--generate_all", "--ga"
|
|
||||||
@generate_plugin_list = true
|
|
||||||
@generate_theme_list = true
|
|
||||||
@number_of_pages = 150
|
|
||||||
@generate_full_theme_list = true
|
|
||||||
@generate_full_plugin_list = true
|
|
||||||
when "--check-vuln-ref-urls", "--cvru"
|
|
||||||
@check_vuln_ref_urls = true
|
|
||||||
when "--check-local-vulnerable-files", "--clvf"
|
|
||||||
@check_local_vulnerable_files = true
|
|
||||||
@dir_to_scan = argument
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if @update
|
plugins.each do |plugin|
|
||||||
unless @updater.nil?
|
plugin.run(options)
|
||||||
puts @updater.update()
|
|
||||||
else
|
|
||||||
puts "Svn / Git not installed, or wpscan has not been installed with one of them."
|
|
||||||
puts "Update aborted"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if @generate_plugin_list
|
|
||||||
puts "[+] Generating new most popular plugin list"
|
|
||||||
puts
|
|
||||||
Generate_List.new('plugins', @verbose).generate_popular_list(@number_of_pages)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @generate_full_plugin_list
|
|
||||||
puts "[+] Generating new full plugin list"
|
|
||||||
puts
|
|
||||||
Generate_List.new('plugins', @verbose).generate_full_list
|
|
||||||
end
|
|
||||||
|
|
||||||
if @generate_theme_list
|
|
||||||
puts "[+] Generating new most popular theme list"
|
|
||||||
puts
|
|
||||||
Generate_List.new('themes', @verbose).generate_popular_list(@number_of_pages)
|
|
||||||
end
|
|
||||||
|
|
||||||
if @generate_full_theme_list
|
|
||||||
puts "[+] Generating new full theme list"
|
|
||||||
puts
|
|
||||||
Generate_List.new('themes', @verbose).generate_full_list
|
|
||||||
end
|
|
||||||
|
|
||||||
# seclists.org redirects to the homepage if the reference does not exist
|
|
||||||
# TODO : the special case above
|
|
||||||
if @check_vuln_ref_urls
|
|
||||||
vuln_ref_files = ["plugin_vulns.xml", "theme_vulns.xml", "wp_vulns.xml"]
|
|
||||||
error_codes = [404, 500, 403]
|
|
||||||
not_found_regexp = %r{No Results Found|error 404|ID Invalid or Not Found}i
|
|
||||||
|
|
||||||
puts "[+] Checking vulnerabilities reference urls"
|
|
||||||
|
|
||||||
vuln_ref_files.each do |vuln_ref_file|
|
|
||||||
xml = Nokogiri::XML(File.open(DATA_DIR + '/' + vuln_ref_file)) do |config|
|
|
||||||
config.noblanks
|
|
||||||
end
|
|
||||||
|
|
||||||
urls = []
|
|
||||||
xml.xpath("//reference").each { |node| urls << node.text }
|
|
||||||
|
|
||||||
urls.uniq!
|
|
||||||
|
|
||||||
dead_urls = []
|
|
||||||
queue_count = 0
|
|
||||||
request_count = 0
|
|
||||||
browser = Browser.instance
|
|
||||||
hydra = browser.hydra
|
|
||||||
number_of_urls = urls.size
|
|
||||||
|
|
||||||
urls.each do |url|
|
|
||||||
request = browser.forge_request(url, { :cache_timeout => 0, :follow_location => true })
|
|
||||||
request_count += 1
|
|
||||||
|
|
||||||
request.on_complete do |response|
|
|
||||||
print "\r [+] Checking #{vuln_ref_file} #{number_of_urls} total ... #{(request_count * 100) / number_of_urls}% complete."
|
|
||||||
|
|
||||||
if error_codes.include?(response.code) or not_found_regexp.match(response.body)
|
|
||||||
dead_urls << url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hydra.queue(request)
|
|
||||||
queue_count += 1
|
|
||||||
|
|
||||||
if queue_count == browser.max_threads
|
|
||||||
hydra.run
|
|
||||||
queue_count = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hydra.run
|
|
||||||
puts
|
|
||||||
unless dead_urls.empty?
|
|
||||||
dead_urls.each { |url| puts " Not Found #{url}" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if @check_local_vulnerable_files
|
|
||||||
if Dir::exist?(@dir_to_scan)
|
|
||||||
xml_file = DATA_DIR + "/local_vulnerable_files.xml"
|
|
||||||
local_hashes = {}
|
|
||||||
file_extension_to_scan = "*.{js,php,swf,html,htm}"
|
|
||||||
|
|
||||||
print "[+] Generating local hashes ... "
|
|
||||||
|
|
||||||
Dir[File::join(@dir_to_scan, "**", file_extension_to_scan)].each do |filename|
|
|
||||||
sha1sum = Digest::SHA1.file(filename).hexdigest
|
|
||||||
|
|
||||||
if local_hashes.has_key?(sha1sum)
|
|
||||||
local_hashes[sha1sum] << filename
|
|
||||||
else
|
|
||||||
local_hashes[sha1sum] = [filename]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
puts "done."
|
|
||||||
|
|
||||||
puts "[+] Checking for vulnerable files ..."
|
|
||||||
|
|
||||||
xml = Nokogiri::XML(File.open(xml_file)) do |config|
|
|
||||||
config.noblanks
|
|
||||||
end
|
|
||||||
|
|
||||||
xml.xpath("//hash").each do |node|
|
|
||||||
sha1sum = node.attribute("sha1").text
|
|
||||||
|
|
||||||
if local_hashes.has_key?(sha1sum)
|
|
||||||
local_filenames = local_hashes[sha1sum]
|
|
||||||
vuln_title = node.search("title").text
|
|
||||||
vuln_filename = node.search("file").text
|
|
||||||
vuln_refrence = node.search("reference").text
|
|
||||||
|
|
||||||
puts " #{vuln_filename} found :"
|
|
||||||
puts " | Location(s):"
|
|
||||||
local_filenames.each do |file|
|
|
||||||
puts " | - #{file}"
|
|
||||||
end
|
|
||||||
puts " |"
|
|
||||||
puts " | Title: #{vuln_title}"
|
|
||||||
puts " | Refrence: #{vuln_refrence}" if !vuln_refrence.empty?
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
puts "done."
|
|
||||||
|
|
||||||
else
|
|
||||||
puts "The supplied directory '#{@dir_to_scan}' does not exist"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue => e
|
rescue => e
|
||||||
|
|||||||
Reference in New Issue
Block a user