From b0dd9ba98966345fb20fdd725f77c26752f2b32a Mon Sep 17 00:00:00 2001 From: erwanlr Date: Thu, 24 Jan 2013 17:04:45 +0100 Subject: [PATCH] WPSTools updated to respect ruby standards according to rubocop --- lib/browser.rb | 44 +++-- lib/cache_file_store.rb | 10 +- lib/common/custom_option_parser.rb | 2 + lib/common/plugins/plugin.rb | 2 + lib/common/plugins/plugins.rb | 2 + lib/common_helper.rb | 93 +++++------ lib/environment.rb | 7 +- lib/updater/git_updater.rb | 4 +- lib/updater/svn_updater.rb | 3 +- lib/updater/updater.rb | 1 + lib/updater/updater_factory.rb | 1 + .../plugins/checker/checker_plugin.rb | 44 ++--- .../plugins/list_generator/generate_list.rb | 55 +++--- .../list_generator/list_generator_plugin.rb | 24 +-- .../plugins/list_generator/svn_parser.rb | 15 +- lib/wpstools/wpstools_helper.rb | 19 ++- wpscan.rb | 156 +++++++++--------- wpstools.rb | 10 +- 18 files changed, 261 insertions(+), 231 deletions(-) diff --git a/lib/browser.rb b/lib/browser.rb index 2fe039d6..29d28edc 100644 --- a/lib/browser.rb +++ b/lib/browser.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -45,7 +46,11 @@ class Browser override_config_with_options(options) end - @hydra = Typhoeus::Hydra.new(:max_concurrency => @max_threads, :timeout => @request_timeout) + @hydra = Typhoeus::Hydra.new( + max_concurrency: @max_threads, + timeout: @request_timeout + ) + # TODO : add an option for the cache dir instead of using a constant @cache = CacheFileStore.new(CACHE_DIR + '/browser') @@ -69,12 +74,13 @@ class Browser end def user_agent_mode=(ua_mode) - ua_mode ||= "static" + ua_mode ||= 'static' if USER_AGENT_MODES.include?(ua_mode) @user_agent_mode = ua_mode - # For semi-static user agent mode, the user agent has to be nil the first time (it will be set with the getter) - @user_agent = nil if ua_mode === "semi-static" + # For semi-static user agent mode, the user agent has to + # be nil the first time (it will be set with the getter) + @user_agent = nil if ua_mode === 'semi-static' else raise "Unknow user agent mode : '#{ua_mode}'" end @@ -83,12 +89,12 @@ class Browser # return the user agent, according to the user_agent_mode def user_agent case @user_agent_mode - when "semi-static" - unless @user_agent - @user_agent = @available_user_agents.sample - end - when "random" + when 'semi-static' + unless @user_agent @user_agent = @available_user_agents.sample + end + when 'random' + @user_agent = @available_user_agents.sample end @user_agent end @@ -109,21 +115,25 @@ class Browser @proxy_auth = auth elsif auth.is_a?(String) if matches = %r{([^:]+):(.*)}.match(auth) - @proxy_auth = {:proxy_username => matches[1], :proxy_password => matches[2]} + @proxy_auth = { + proxy_username: matches[1], + proxy_password: matches[2] + } else - raise_invalid_proxy_format() + raise_invalid_proxy_auth_format() end else - raise_invalid_proxy_format() + raise_invalid_proxy_auth_format() end end end - def raise_invalid_proxy_format - raise "Invalid proxy auth format, expected username:password or {:proxy_username => username, :proxy_password => password}" + def raise_invalid_proxy_auth_format + raise 'Invalid proxy auth format, expected username:password or {proxy_username: username, proxy_password: password}' end - # TODO reload hydra (if the .load_config is called on a browser object, hydra will not have the new @max_threads and @request_timeout) + # TODO reload hydra (if the .load_config is called on a browser object, + # hydra will not have the new @max_threads and @request_timeout) def load_config(config_file = nil) @config_file = config_file || @config_file @@ -146,7 +156,9 @@ class Browser end @hydra.cache_getter do |request| - @cache.read_entry(Browser.generate_cache_key_from_request(request)) rescue nil + @cache.read_entry( + Browser.generate_cache_key_from_request(request) + ) rescue nil end end diff --git a/lib/cache_file_store.rb b/lib/cache_file_store.rb index 6bb49954..be136f67 100644 --- a/lib/cache_file_store.rb +++ b/lib/cache_file_store.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -18,7 +19,8 @@ # # => @todo take consideration of the cache_timeout : -# -> create 2 files per key : one for the data storage (key.store ?) and the other for the cache timeout (key.expiration, key.timeout ?) +# -> create 2 files per key : one for the data storage (key.store ?) +# and the other for the cache timeout (key.expiration, key.timeout ?) # or 1 file for all timeouts ? # -> 2 dirs : 1 for storage, the other for cache_timeout ? # @@ -28,14 +30,16 @@ require 'yaml' class CacheFileStore attr_reader :storage_path, :serializer - # The serializer must have the 2 methods .load and .dump (Marshal and YAML have them) + # The serializer must have the 2 methods .load and .dump + # (Marshal and YAML have them) # YAML is Human Readable, contrary to Marshal which store in a binary format # Marshal does not need any "require" def initialize(storage_path, serializer = Marshal) @storage_path = File.expand_path(storage_path) @serializer = serializer - # File.directory? for ruby <= 1.9 otherwise, it makes more sense to do Dir.exist? :/ + # File.directory? for ruby <= 1.9 otherwise, + # it makes more sense to do Dir.exist? :/ unless File.directory?(@storage_path) Dir.mkdir(@storage_path) end diff --git a/lib/common/custom_option_parser.rb b/lib/common/custom_option_parser.rb index 4a20358a..f996d7ba 100644 --- a/lib/common/custom_option_parser.rb +++ b/lib/common/custom_option_parser.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 +# # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 # diff --git a/lib/common/plugins/plugin.rb b/lib/common/plugins/plugin.rb index 97c259c7..71e42b1f 100644 --- a/lib/common/plugins/plugin.rb +++ b/lib/common/plugins/plugin.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 +# # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 # diff --git a/lib/common/plugins/plugins.rb b/lib/common/plugins/plugins.rb index 5c1791e8..614798f6 100644 --- a/lib/common/plugins/plugins.rb +++ b/lib/common/plugins/plugins.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 +# # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 # diff --git a/lib/common_helper.rb b/lib/common_helper.rb index 949b791d..8b921175 100644 --- a/lib/common_helper.rb +++ b/lib/common_helper.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -18,39 +19,39 @@ LIB_DIR = File.dirname(__FILE__) ROOT_DIR = File.expand_path(LIB_DIR + '/..') # expand_path is used to get "wpscan/" instead of "wpscan/lib/../" -DATA_DIR = ROOT_DIR + "/data" -CONF_DIR = ROOT_DIR + "/conf" -CACHE_DIR = ROOT_DIR + "/cache" -WPSCAN_LIB_DIR = LIB_DIR + "/wpscan" -WPSTOOLS_LIB_DIR = LIB_DIR + "/wpstools" -UPDATER_LIB_DIR = LIB_DIR + "/updater" -COMMON_LIB_DIR = LIB_DIR + "/common" -LOG_FILE = ROOT_DIR + "/log.txt" +DATA_DIR = ROOT_DIR + '/data' +CONF_DIR = ROOT_DIR + '/conf' +CACHE_DIR = ROOT_DIR + '/cache' +WPSCAN_LIB_DIR = LIB_DIR + '/wpscan' +WPSTOOLS_LIB_DIR = LIB_DIR + '/wpstools' +UPDATER_LIB_DIR = LIB_DIR + '/updater' +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" +COMON_PLUGINS_DIR = COMMON_LIB_DIR + '/plugins' +WPSCAN_PLUGINS_DIR = WPSCAN_LIB_DIR + '/plugins' +WPSTOOLS_PLUGINS_DIR = WPSTOOLS_LIB_DIR + '/plugins' # Data files -PLUGINS_FILE = DATA_DIR + "/plugins.txt" -PLUGINS_FULL_FILE = DATA_DIR + "/plugins_full.txt" -PLUGINS_VULNS_FILE = DATA_DIR + "/plugin_vulns.xml" -THEMES_FILE = DATA_DIR + "/themes.txt" -THEMES_FULL_FILE = DATA_DIR + "/themes_full.txt" -THEMES_VULNS_FILE = DATA_DIR + "/theme_vulns.xml" -WP_VULNS_FILE = DATA_DIR + "/wp_vulns.xml" -WP_VERSIONS_FILE = DATA_DIR + "/wp_versions.xml" -LOCAL_FILES_FILE = DATA_DIR + "/local_vulnerable_files.xml" -VULNS_XSD = DATA_DIR + "/vuln.xsd" -WP_VERSIONS_XSD = DATA_DIR + "/wp_versions.xsd" -LOCAL_FILES_XSD = DATA_DIR + "/local_vulnerable_files.xsd" +PLUGINS_FILE = DATA_DIR + '/plugins.txt' +PLUGINS_FULL_FILE = DATA_DIR + '/plugins_full.txt' +PLUGINS_VULNS_FILE = DATA_DIR + '/plugin_vulns.xml' +THEMES_FILE = DATA_DIR + '/themes.txt' +THEMES_FULL_FILE = DATA_DIR + '/themes_full.txt' +THEMES_VULNS_FILE = DATA_DIR + '/theme_vulns.xml' +WP_VULNS_FILE = DATA_DIR + '/wp_vulns.xml' +WP_VERSIONS_FILE = DATA_DIR + '/wp_versions.xml' +LOCAL_FILES_FILE = DATA_DIR + '/local_vulnerable_files.xml' +VULNS_XSD = DATA_DIR + '/vuln.xsd' +WP_VERSIONS_XSD = DATA_DIR + '/wp_versions.xsd' +LOCAL_FILES_XSD = DATA_DIR + '/local_vulnerable_files.xsd' -WPSCAN_VERSION = "2.0" +WPSCAN_VERSION = '2.0' require "#{LIB_DIR}/environment" # TODO : add an exclude pattern ? -def require_files_from_directory(absolute_dir_path, files_pattern = "*.rb") +def require_files_from_directory(absolute_dir_path, files_pattern = '*.rb') Dir[File.join(absolute_dir_path, files_pattern)].sort.each do |f| f = File.expand_path(f) require f @@ -59,7 +60,7 @@ def require_files_from_directory(absolute_dir_path, files_pattern = "*.rb") end #require_files_from_directory(COMMON_LIB_DIR) -require_files_from_directory(COMMON_LIB_DIR, "**/*.rb") +require_files_from_directory(COMMON_LIB_DIR, '**/*.rb') # Add protocol def add_http_protocol(url) @@ -71,8 +72,8 @@ def add_trailing_slash(url) end # Gets the string all elements in stringarray ends with -def get_equal_string_end(stringarray = [""]) - already_found = "" +def get_equal_string_end(stringarray = ['']) + already_found = '' looping = true counter = -1 if stringarray.kind_of? Array and stringarray.length > 1 @@ -97,15 +98,15 @@ end # Since ruby 1.9.2, URI::escape is obsolete # See http://rosettacode.org/wiki/URL_encoding#Ruby and http://www.ruby-forum.com/topic/207489 -if RUBY_VERSION >= "1.9.2" +if RUBY_VERSION >= '1.9.2' module URI def self.escape(str) - URI.encode_www_form_component(str).gsub("+", "%20") + URI.encode_www_form_component(str).gsub('+', '%20') end end end -if RUBY_VERSION < "1.9" +if RUBY_VERSION < '1.9' class Array # Fix for grep with symbols in ruby <= 1.8.7 def _grep_(regexp) @@ -128,25 +129,25 @@ require_files_from_directory(UPDATER_LIB_DIR) if @updater REVISION = @updater.local_revision_number() else - REVISION = "NA" + REVISION = 'NA' end # our 1337 banner -def banner() +def banner puts '____________________________________________________' - puts " __ _______ _____ " - puts " \\ \\ / / __ \\ / ____| " - puts " \\ \\ /\\ / /| |__) | (___ ___ __ _ _ __ " - puts " \\ \\/ \\/ / | ___/ \\___ \\ / __|/ _` | '_ \\ " - puts " \\ /\\ / | | ____) | (__| (_| | | | |" + puts ' __ _______ _____ ' + puts ' \\ \\ / / __ \\ / ____| ' + puts ' \\ \\ /\\ / /| |__) | (___ ___ __ _ _ __ ' + puts ' \\ \\/ \\/ / | ___/ \\___ \\ / __|/ _` | \'_ \\ ' + puts ' \\ /\\ / | | ____) | (__| (_| | | | |' puts " \\/ \\/ |_| |_____/ \\___|\\__,_|_| |_| v#{WPSCAN_VERSION}r#{REVISION}" puts - puts " WordPress Security Scanner by the WPScan Team" - puts " Sponsored by the RandomStorm Open Source Initiative" + puts ' WordPress Security Scanner by the WPScan Team' + puts ' Sponsored by the RandomStorm Open Source Initiative' puts '_____________________________________________________' puts - if RUBY_VERSION < "1.9" - puts "[WARNING] Ruby < 1.9 not officially supported, please upgrade." + if RUBY_VERSION < '1.9' + puts '[WARNING] Ruby < 1.9 not officially supported, please upgrade.' puts end end @@ -165,16 +166,16 @@ end def get_metasploit_url(module_path) # remove leading slash - module_path = module_path.sub(/^\//, "") + module_path = module_path.sub(/^\//, '') "http://www.metasploit.com/modules/#{module_path}" end # Override for puts to enable logging -def puts(o = "") +def puts(o = '') # remove color for logging - if o.respond_to?("gsub") + if o.respond_to?('gsub') temp = o.gsub(/\e\[\d+m(.*)?\e\[0m/, '\1') - File.open(LOG_FILE, "a+") { |f| f.puts(temp) } + File.open(LOG_FILE, 'a+') { |f| f.puts(temp) } end super(o) end diff --git a/lib/environment.rb b/lib/environment.rb index be1b58c7..ad680cf3 100644 --- a/lib/environment.rb +++ b/lib/environment.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -47,7 +48,7 @@ rescue LoadError => e if missing_gem if missing_gem =~ /nokogiri/i puts - puts "Nokogiri needs some packets, please run 'sudo apt-get install libxml2 libxml2-dev libxslt1-dev' to install them. Then run the command below" + puts 'Nokogiri needs some packets, please run \'sudo apt-get install libxml2 libxml2-dev libxslt1-dev\' to install them. Then run the command below' puts end puts "[TIP] Try to run 'gem install #{missing_gem}' or 'gem install --user-install #{missing_gem}'. If you still get an error, Please see README file or https://github.com/wpscanteam/wpscan" @@ -55,7 +56,7 @@ rescue LoadError => e exit(1) end -if Typhoeus::VERSION == "0.4.0" - puts "Typhoeus 0.4.0 detected, please update the gem otherwise wpscan will not work correctly" +if Typhoeus::VERSION == '0.4.0' + puts 'Typhoeus 0.4.0 detected, please update the gem otherwise wpscan will not work correctly' exit(1) end diff --git a/lib/updater/git_updater.rb b/lib/updater/git_updater.rb index 0afb4336..4f4c4815 100644 --- a/lib/updater/git_updater.rb +++ b/lib/updater/git_updater.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -24,7 +25,8 @@ class GitUpdater < Updater %x[git #{repo_directory_arguments()} status 2>&1] =~ /On branch/ ? true : false end - # Git has not a revsion number like SVN, so we will take the 7 first chars of the last commit hash + # Git has not a revsion number like SVN, + # so we will take the 7 first chars of the last commit hash def local_revision_number git_log = %x[git #{repo_directory_arguments()} log -1 2>&1] git_log[/commit ([0-9a-z]{7})/i, 1].to_s diff --git a/lib/updater/svn_updater.rb b/lib/updater/svn_updater.rb index cfedcf6e..b5678d88 100644 --- a/lib/updater/svn_updater.rb +++ b/lib/updater/svn_updater.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -21,7 +22,7 @@ require File.expand_path(File.dirname(__FILE__) + '/updater') class SvnUpdater < Updater REVISION_PATTERN = /revision="(\d+)"/i - TRUNK_URL = "https://github.com/wpscanteam/wpscan" + TRUNK_URL = 'https://github.com/wpscanteam/wpscan' def is_installed? %x[svn info "#@repo_directory" --xml 2>&1] =~ /revision=/ ? true : false diff --git a/lib/updater/updater.rb b/lib/updater/updater.rb index 9e471cbe..c838278d 100644 --- a/lib/updater/updater.rb +++ b/lib/updater/updater.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 diff --git a/lib/updater/updater_factory.rb b/lib/updater/updater_factory.rb index bb736487..8634784f 100644 --- a/lib/updater/updater_factory.rb +++ b/lib/updater/updater_factory.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 diff --git a/lib/wpstools/plugins/checker/checker_plugin.rb b/lib/wpstools/plugins/checker/checker_plugin.rb index d888f9c5..974f3039 100644 --- a/lib/wpstools/plugins/checker/checker_plugin.rb +++ b/lib/wpstools/plugins/checker/checker_plugin.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 +# # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 # @@ -18,13 +20,11 @@ class CheckerPlugin < Plugin def initialize - super( - :author => "@wpscanteam - @erwanlr" - ) + 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"] + ['--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 @@ -39,11 +39,11 @@ class CheckerPlugin < Plugin end def check_vuln_ref_urls - vuln_ref_files = [ PLUGINS_VULNS_FILE , THEMES_VULNS_FILE, WP_VULNS_FILE ] + vuln_ref_files = [PLUGINS_VULNS_FILE, THEMES_VULNS_FILE, WP_VULNS_FILE] 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" + puts '[+] Checking vulnerabilities reference urls' vuln_ref_files.each do |vuln_ref_file| xml = Nokogiri::XML(File.open(vuln_ref_file)) do |config| @@ -51,7 +51,7 @@ class CheckerPlugin < Plugin end urls = [] - xml.xpath("//reference").each { |node| urls << node.text } + xml.xpath('//reference').each { |node| urls << node.text } urls.uniq! @@ -63,7 +63,7 @@ class CheckerPlugin < Plugin number_of_urls = urls.size urls.each do |url| - request = browser.forge_request(url, { :cache_timeout => 0, :follow_location => true }) + request = browser.forge_request(url, { cache_timeout: 0, follow_location: true }) request_count += 1 request.on_complete do |response| @@ -95,11 +95,11 @@ class CheckerPlugin < Plugin if Dir::exist?(dir_to_scan) xml_file = LOCAL_FILES_FILE local_hashes = {} - file_extension_to_scan = "*.{js,php,swf,html,htm}" + file_extension_to_scan = '*.{js,php,swf,html,htm}' - print "[+] Generating local hashes ... " + print '[+] Generating local hashes ... ' - Dir[File::join(dir_to_scan, "**", file_extension_to_scan)].each do |filename| + 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) @@ -109,36 +109,36 @@ class CheckerPlugin < Plugin end end - puts "done." + puts 'done.' - puts "[+] Checking for vulnerable files ..." + 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 + 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 + 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):" + puts ' | Location(s):' local_filenames.each do |file| puts " | - #{file}" end - puts " |" + puts ' |' puts " | Title: #{vuln_title}" puts " | Refrence: #{vuln_refrence}" if !vuln_refrence.empty? puts end end - puts "done." + puts 'done.' else puts "The supplied directory '#{dir_to_scan}' does not exist" diff --git a/lib/wpstools/plugins/list_generator/generate_list.rb b/lib/wpstools/plugins/list_generator/generate_list.rb index 8ee482c7..bb9f1395 100644 --- a/lib/wpstools/plugins/list_generator/generate_list.rb +++ b/lib/wpstools/plugins/list_generator/generate_list.rb @@ -1,5 +1,4 @@ -#!/usr/bin/env ruby - +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -26,14 +25,14 @@ class GenerateList # type = themes | plugins def initialize(type, verbose) if type =~ /plugins/i - @type = "plugin" - @svn_url = "http://plugins.svn.wordpress.org/" - @popular_url = "http://wordpress.org/extend/plugins/browse/popular/" + @type = 'plugin' + @svn_url = 'http://plugins.svn.wordpress.org/' + @popular_url = 'http://wordpress.org/extend/plugins/browse/popular/' @popular_regex = %r{

.+

}i elsif type =~ /themes/i - @type = "theme" - @svn_url = "http://themes.svn.wordpress.org/" - @popular_url = "http://wordpress.org/extend/themes/browse/popular/" + @type = 'theme' + @svn_url = 'http://themes.svn.wordpress.org/' + @popular_url = 'http://wordpress.org/extend/themes/browse/popular/' @popular_regex = %r{

.+

}i else raise "Type #{type} not defined" @@ -45,24 +44,24 @@ class GenerateList def set_file_name(type) case @type - when "plugin" - case type - when :full - @file_name = PLUGINS_FULL_FILE - when :popular - @file_name = PLUGINS_FILE - else - raise "Unknown type" - end - when "theme" - case type - when :full - @file_name = THEMES_FULL_FILE - when :popular - @file_name = THEMES_FILE - else - raise "Unknown type" - end + when 'plugin' + case type + when :full + @file_name = PLUGINS_FULL_FILE + when :popular + @file_name = PLUGINS_FILE + else + raise 'Unknown type' + end + when 'theme' + case type + when :full + @file_name = THEMES_FULL_FILE + when :popular + @file_name = THEMES_FILE + else + raise 'Unknown type' + end else raise "Unknown type #@type" end @@ -87,7 +86,7 @@ class GenerateList page_count = 1 queue_count = 0 - (1...(pages.to_i+1)).each do |page| + (1...(pages.to_i + 1)).each do |page| # First page has another URL url = (page == 1) ? @popular_url : @popular_url + 'page/' + page.to_s + '/' request = @browser.forge_request(url) @@ -95,7 +94,7 @@ class GenerateList queue_count += 1 request.on_complete do |response| - puts "[+] Parsing page " + page_count.to_s if @verbose + puts "[+] Parsing page #{page_count}" if @verbose page_count += 1 response.body.scan(@popular_regex).each do |item| puts "[+] Found popular #@type: #{item}" if @verbose diff --git a/lib/wpstools/plugins/list_generator/list_generator_plugin.rb b/lib/wpstools/plugins/list_generator/list_generator_plugin.rb index eaa96347..7a702a90 100644 --- a/lib/wpstools/plugins/list_generator/list_generator_plugin.rb +++ b/lib/wpstools/plugins/list_generator/list_generator_plugin.rb @@ -1,3 +1,5 @@ +# encoding: UTF-8 +# # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 # @@ -18,18 +20,16 @@ class ListGeneratorPlugin < Plugin def initialize - super( - :author => "WPScanTeam - @FireFart" - ) + 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-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-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"], + ['--generate-all', '--ga', 'Generate a new full plugins, full themes, popular plugins and popular themes list'] ) end @@ -40,13 +40,13 @@ class ListGeneratorPlugin < Plugin 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 '[+] 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 '[+] Generating new full plugin list' puts GenerateList.new('plugins', verbose).generate_full_list end @@ -54,13 +54,13 @@ class ListGeneratorPlugin < Plugin 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 '[+] 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 '[+] Generating new full theme list' puts GenerateList.new('themes', verbose).generate_full_list end diff --git a/lib/wpstools/plugins/list_generator/svn_parser.rb b/lib/wpstools/plugins/list_generator/svn_parser.rb index 63f45c41..7f7193b6 100644 --- a/lib/wpstools/plugins/list_generator/svn_parser.rb +++ b/lib/wpstools/plugins/list_generator/svn_parser.rb @@ -1,5 +1,4 @@ -#!/usr/bin/env ruby - +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -24,12 +23,12 @@ class SvnParser attr_accessor :verbose, :svn_root, :keep_empty_dirs def initialize(svn_root) - @svn_root = svn_root - @svn_browser = Browser.instance - @svn_hydra = @svn_browser.hydra + @svn_root = svn_root + @svn_browser = Browser.instance + @svn_hydra = @svn_browser.hydra end - def parse() + def parse get_root_directories end @@ -38,11 +37,13 @@ class SvnParser # Gets all directories in the SVN root def get_root_directories - dirs = [] + dirs = [] rootindex = @svn_browser.get(@svn_root).body + rootindex.scan(%r{
  • (.+)/
  • }i).each do |dir| dirs << dir[0] end + dirs.sort! dirs.uniq end diff --git a/lib/wpstools/wpstools_helper.rb b/lib/wpstools/wpstools_helper.rb index 206697f2..f45232f8 100644 --- a/lib/wpstools/wpstools_helper.rb +++ b/lib/wpstools/wpstools_helper.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 #-- # WPScan - WordPress Security Scanner # Copyright (C) 2012-2013 @@ -19,33 +20,33 @@ require File.expand_path(File.dirname(__FILE__) + '/../common_helper') require_files_from_directory(WPSTOOLS_LIB_DIR) -require_files_from_directory(WPSTOOLS_PLUGINS_DIR, "**/*.rb") +require_files_from_directory(WPSTOOLS_PLUGINS_DIR, '**/*.rb') -def usage() +def usage script_name = $0 puts - puts "-h for further help." + puts '-h for further help.' puts - puts "Examples:" + puts 'Examples:' puts puts "- Generate a new 'most popular' plugin list, up to 150 pages ..." puts "ruby #{script_name} --generate-plugin-list 150" puts - puts "- Generate a new full plugin list" + puts '- Generate a new full plugin list' puts "ruby #{script_name} --generate-full-plugin-list" puts puts "- Generate a new 'most popular' theme list, up to 150 pages ..." puts "ruby #{script_name} --generate-theme-list 150" puts - puts "- Generate a new full theme list" + puts '- Generate a new full theme list' puts "ruby #{script_name} --generate-full-theme-list" puts - puts "- Generate all list" + puts '- Generate all list' puts "ruby #{script_name} --generate-all" 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 - puts "See README for further information." + puts 'See README for further information.' puts end diff --git a/wpscan.rb b/wpscan.rb index 4d182c08..d5b78a26 100755 --- a/wpscan.rb +++ b/wpscan.rb @@ -19,17 +19,17 @@ #++ $: << '.' -require File.dirname(__FILE__) +'/lib/wpscan/wpscan_helper' +require File.dirname(__FILE__) + '/lib/wpscan/wpscan_helper' def output_vulnerabilities(vulns) vulns.each do |vulnerability| puts - puts " | " + red("* Title: #{vulnerability.title}") + puts ' | ' + red("* Title: #{vulnerability.title}") vulnerability.references.each do |r| - puts " | " + red("* Reference: #{r}") + puts ' | ' + red("* Reference: #{r}") end vulnerability.metasploit_modules.each do |m| - puts " | " + red("* Metasploit module: #{get_metasploit_url(m)}") + puts ' | ' + red("* Metasploit module: #{get_metasploit_url(m)}") end end end @@ -61,8 +61,8 @@ begin end puts @updater.update() else - puts "Svn / Git not installed, or wpscan has not been installed with one of them." - puts "Update aborted" + puts 'Svn / Git not installed, or wpscan has not been installed with one of them.' + puts 'Update aborted' end exit(1) end @@ -89,37 +89,37 @@ begin puts else puts "The remote host tried to redirect us to #{redirection}" - puts "Do you want follow the redirection ? [y/n]" + puts 'Do you want follow the redirection ? [y/n]' end if wpscan_options.follow_redirection or Readline.readline =~ /^y/i wpscan_options.url = redirection wp_target = WpTarget.new(redirection, wpscan_options.to_h) else - puts "Scan aborted" + puts 'Scan aborted' exit end end if wp_target.has_basic_auth? && wpscan_options.basic_auth.nil? - raise "A basic authentification is required, please provide it with --basic-auth " + raise 'A basic authentification is required, please provide it with --basic-auth ' end # Remote website is wordpress? unless wpscan_options.force unless wp_target.wordpress? - raise "The remote website is up, but does not seem to be running WordPress." + raise 'The remote website is up, but does not seem to be running WordPress.' end end unless wp_target.wp_content_dir - raise "The wp_content_dir has not been found, please supply it with --wp-content-dir" + raise 'The wp_content_dir has not been found, please supply it with --wp-content-dir' end unless wp_target.wp_plugins_dir_exists? puts "The plugins directory '#{wp_target.wp_plugins_dir}' does not exist." - puts "You can specify one per command line option (don't forget to include the wp-content directory if needed)" - puts "Continue? [y/n]" + puts 'You can specify one per command line option (don\'t forget to include the wp-content directory if needed)' + puts 'Continue? [y/n]' unless Readline.readline =~ /^y/i exit end @@ -134,33 +134,33 @@ begin wp_theme = wp_target.theme if wp_theme # Theme version is handled in wp_item.to_s - puts green("[+]") + " The WordPress theme in use is #{wp_theme}" + puts green('[+]') + " The WordPress theme in use is #{wp_theme}" puts puts " | Name: #{wp_theme}" #this will also output the version number if detected puts " | Location: #{wp_theme.get_url_without_filename}" puts " | WordPress: #{wp_theme.wp_org_url}" if wp_theme.wp_org_item? - puts " | Directory listing enabled: Yes" if wp_theme.directory_listing? + puts ' | Directory listing enabled: Yes' if wp_theme.directory_listing? puts " | Readme: #{wp_theme.readme_url}" if wp_theme.has_readme? puts " | Changelog: #{wp_theme.changelog_url}" if wp_theme.has_changelog? theme_vulnerabilities = wp_theme.vulnerabilities unless theme_vulnerabilities.empty? - puts red("[!]") + " We have identified #{theme_vulnerabilities.size} vulnerabilities for this theme :" + puts red('[!]') + " We have identified #{theme_vulnerabilities.size} vulnerabilities for this theme :" output_vulnerabilities(theme_vulnerabilities) end puts end if wp_target.has_readme? - puts red("[!]") + " The WordPress '#{wp_target.readme_url}' file exists" + puts red('[!]') + " The WordPress '#{wp_target.readme_url}' file exists" end if wp_target.has_full_path_disclosure? - puts red("[!]") + " Full Path Disclosure (FPD) in '#{wp_target.full_path_disclosure_url}'" + puts red('[!]') + " Full Path Disclosure (FPD) in '#{wp_target.full_path_disclosure_url}'" end if wp_target.has_debug_log? - puts red("[!]") + " Debug log file found : #{wp_target.debug_log_url}" + puts red('[!]') + " Debug log file found : #{wp_target.debug_log_url}" end wp_target.config_backup.each do |file_url| @@ -172,46 +172,46 @@ begin end if wp_target.is_multisite? - puts green("[+]") + " This site seems to be a multisite (http://codex.wordpress.org/Glossary#Multisite)" + puts green('[+]') + ' This site seems to be a multisite (http://codex.wordpress.org/Glossary#Multisite)' end if wp_target.registration_enabled? - puts green("[+]") + " User registration is enabled" + puts green('[+]') + ' User registration is enabled' end if wp_target.has_xml_rpc? - puts green("[+]") + " XML-RPC Interface available under #{wp_target.xml_rpc_url}" + puts green('[+]') + " XML-RPC Interface available under #{wp_target.xml_rpc_url}" end if wp_target.has_malwares? malwares = wp_target.malwares - puts red("[!]") + " #{malwares.size} malware(s) found :" + puts red('[!]') + " #{malwares.size} malware(s) found :" malwares.each do |malware_url| puts - puts " | " + red("#{malware_url}") + puts ' | ' + red("#{malware_url}") end puts end wp_version = wp_target.version if wp_version - puts green("[+]") + " WordPress version #{wp_version.number} identified from #{wp_version.discovery_method}" + puts green('[+]') + " WordPress version #{wp_version.number} identified from #{wp_version.discovery_method}" version_vulnerabilities = wp_version.vulnerabilities unless version_vulnerabilities.empty? puts - puts red("[!]") + " We have identified #{version_vulnerabilities.size} vulnerabilities from the version number :" + puts red('[!]') + " We have identified #{version_vulnerabilities.size} vulnerabilities from the version number :" output_vulnerabilities(version_vulnerabilities) end end if wpscan_options.enumerate_plugins == nil and wpscan_options.enumerate_only_vulnerable_plugins == nil puts - puts green("[+]") + " Enumerating plugins from passive detection ... " + puts green('[+]') + ' Enumerating plugins from passive detection ... ' - plugins = wp_target.plugins_from_passive_detection(:base_url => wp_target.uri, :wp_content_dir => wp_target.wp_content_dir) + plugins = wp_target.plugins_from_passive_detection(base_url: wp_target.uri, wp_content_dir: wp_target.wp_content_dir) unless plugins.empty? puts "#{plugins.size} found :" @@ -224,84 +224,84 @@ begin output_vulnerabilities(plugin.vulnerabilities) end else - puts "No plugins found :(" + puts 'No plugins found :(' end end # Enumerate the installed plugins if wpscan_options.enumerate_plugins or wpscan_options.enumerate_only_vulnerable_plugins or wpscan_options.enumerate_all_plugins puts - puts green("[+]") + " Enumerating installed plugins #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_plugins} ..." + puts green('[+]') + " Enumerating installed plugins #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_plugins} ..." puts options = { - :base_url => wp_target.uri, - :only_vulnerable_ones => wpscan_options.enumerate_only_vulnerable_plugins || false, - :show_progression => true, - :wp_content_dir => wp_target.wp_content_dir, - :error_404_hash => wp_target.error_404_hash, - :homepage_hash => wp_target.homepage_hash, - :wp_plugins_dir => wp_target.wp_plugins_dir, - :full => wpscan_options.enumerate_all_plugins, - :exclude_content_based => wpscan_options.exclude_content_based + base_url: wp_target.uri, + only_vulnerable_ones: wpscan_options.enumerate_only_vulnerable_plugins || false, + show_progression: true, + wp_content_dir: wp_target.wp_content_dir, + error_404_hash: wp_target.error_404_hash, + homepage_hash: wp_target.homepage_hash, + wp_plugins_dir: wp_target.wp_plugins_dir, + full: wpscan_options.enumerate_all_plugins, + exclude_content_based: wpscan_options.exclude_content_based } plugins = wp_target.plugins_from_aggressive_detection(options) unless plugins.empty? puts puts - puts green("[+]") + " We found #{plugins.size.to_s} plugins:" + puts green('[+]') + " We found #{plugins.size.to_s} plugins:" plugins.each do |plugin| puts puts " | Name: #{plugin}" #this will also output the version number if detected puts " | Location: #{plugin.get_url_without_filename}" puts " | WordPress: #{plugin.wp_org_url}" if plugin.wp_org_item? - puts " | Directory listing enabled: Yes" if plugin.directory_listing? + puts ' | Directory listing enabled: Yes' if plugin.directory_listing? puts " | Readme: #{plugin.readme_url}" if plugin.has_readme? puts " | Changelog: #{plugin.changelog_url}" if plugin.has_changelog? output_vulnerabilities(plugin.vulnerabilities) if plugin.error_log? - puts " | " + red("[!]") + " A WordPress error_log file has been found : #{plugin.error_log_url}" + puts ' | ' + red('[!]') + " A WordPress error_log file has been found : #{plugin.error_log_url}" end end else puts - puts "No plugins found :(" + puts 'No plugins found :(' end end # Enumerate installed themes if wpscan_options.enumerate_themes or wpscan_options.enumerate_only_vulnerable_themes or wpscan_options.enumerate_all_themes puts - puts green("[+]") + " Enumerating installed themes #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_themes} ..." + puts green('[+]') + " Enumerating installed themes #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_themes} ..." puts options = { - :base_url => wp_target.uri, - :only_vulnerable_ones => wpscan_options.enumerate_only_vulnerable_themes || false, - :show_progression => true, - :wp_content_dir => wp_target.wp_content_dir, - :error_404_hash => wp_target.error_404_hash, - :homepage_hash => wp_target.homepage_hash, - :full => wpscan_options.enumerate_all_themes, - :exclude_content_based => wpscan_options.exclude_content_based + base_url: wp_target.uri, + only_vulnerable_ones: wpscan_options.enumerate_only_vulnerable_themes || false, + show_progression: true, + wp_content_dir: wp_target.wp_content_dir, + error_404_hash: wp_target.error_404_hash, + homepage_hash: wp_target.homepage_hash, + full: wpscan_options.enumerate_all_themes, + exclude_content_based: wpscan_options.exclude_content_based } themes = wp_target.themes_from_aggressive_detection(options) unless themes.empty? puts puts - puts green("[+]") + " We found #{themes.size.to_s} themes:" + puts green('[+]') + " We found #{themes.size.to_s} themes:" themes.each do |theme| puts puts " | Name: #{theme}" #this will also output the version number if detected puts " | Location: #{theme.get_url_without_filename}" puts " | WordPress: #{theme.wp_org_url}" if theme.wp_org_item? - puts " | Directory listing enabled: Yes" if theme.directory_listing? + puts ' | Directory listing enabled: Yes' if theme.directory_listing? puts " | Readme: #{theme.readme_url}" if theme.has_readme? puts " | Changelog: #{theme.changelog_url}" if theme.has_changelog? @@ -309,22 +309,22 @@ begin end else puts - puts "No themes found :(" + puts 'No themes found :(' end end if wpscan_options.enumerate_timthumbs puts - puts green("[+]") + " Enumerating timthumb files ..." + puts green('[+]') + ' Enumerating timthumb files ...' puts options = { - :base_url => wp_target.uri, - :show_progression => true, - :wp_content_dir => wp_target.wp_content_dir, - :error_404_hash => wp_target.error_404_hash, - :homepage_hash => wp_target.homepage_hash, - :exclude_content_based => wpscan_options.exclude_content_based + base_url: wp_target.uri, + show_progression: true, + wp_content_dir: wp_target.wp_content_dir, + error_404_hash: wp_target.error_404_hash, + homepage_hash: wp_target.homepage_hash, + exclude_content_based: wpscan_options.exclude_content_based } theme_name = wp_theme ? wp_theme.name : nil @@ -332,36 +332,36 @@ begin timthumbs = wp_target.timthumbs puts - puts green("[+]") + " We found #{timthumbs.size.to_s} timthumb file/s :" + puts green('[+]') + " We found #{timthumbs.size.to_s} timthumb file/s :" puts timthumbs.each do |t| - puts " | " + red("[!]") + " #{t.get_full_url.to_s}" + puts ' | ' + red('[!]') + " #{t.get_full_url.to_s}" end puts - puts red(" * Reference: http://www.exploit-db.com/exploits/17602/") + puts red(' * Reference: http://www.exploit-db.com/exploits/17602/') else puts - puts "No timthumb files found :(" + puts 'No timthumb files found :(' end end # If we haven't been supplied a username, enumerate them... if !wpscan_options.username and wpscan_options.wordlist or wpscan_options.enumerate_usernames puts - puts green("[+]") + " Enumerating usernames ..." + puts green('[+]') + ' Enumerating usernames ...' - usernames = wp_target.usernames(:range => wpscan_options.enumerate_usernames_range) + usernames = wp_target.usernames(range: wpscan_options.enumerate_usernames_range) if usernames.empty? puts - puts "We did not enumerate any usernames :(" - puts "Try supplying your own username with the --username option" + puts 'We did not enumerate any usernames :(' + puts 'Try supplying your own username with the --username option' puts exit(1) else puts - puts green("[+]") + " We found the following #{usernames.length.to_s} username/s :" + puts green('[+]') + " We found the following #{usernames.length.to_s} username/s :" puts max_id_length = usernames.sort { |a, b| a.id.to_s.length <=> b.id.to_s.length }.last.id.to_s.length @@ -378,7 +378,7 @@ begin end else - usernames = [WpUser.new(wpscan_options.username, -1, "empty")] + usernames = [WpUser.new(wpscan_options.username, -1, 'empty')] end # Start the brute forcer @@ -390,19 +390,19 @@ begin puts puts "The plugin #{protection_plugin.name} has been detected. It might record the IP and timestamp of every failed login. Not a good idea for brute forcing !" - puts "[?] Do you want to start the brute force anyway ? [y/n]" + puts '[?] Do you want to start the brute force anyway ? [y/n]' bruteforce = false if Readline.readline !~ /^y/i end if bruteforce puts - puts green("[+]") + " Starting the password brute forcer" + puts green('[+]') + ' Starting the password brute forcer' puts - wp_target.brute_force(usernames, wpscan_options.wordlist, {:show_progression => true}) + wp_target.brute_force(usernames, wpscan_options.wordlist, {show_progression: true}) else puts - puts "Brute forcing aborted" + puts 'Brute forcing aborted' end end @@ -410,10 +410,10 @@ begin puts puts green("[+] Finished at #{stop_time.asctime}") elapsed = stop_time - start_time - puts green("[+] Elapsed time: #{Time.at(elapsed).utc.strftime("%H:%M:%S")}") + puts green("[+] Elapsed time: #{Time.at(elapsed).utc.strftime('%H:%M:%S')}") exit() # must exit! rescue => e puts red("[ERROR] #{e.message}") - puts red("Trace :") + puts red('Trace :') puts red(e.backtrace.join("\n")) end diff --git a/wpstools.rb b/wpstools.rb index b67c8a41..ea3515ae 100755 --- a/wpstools.rb +++ b/wpstools.rb @@ -19,15 +19,15 @@ #++ $: << '.' -require File.dirname(__FILE__) + "/lib/wpstools/wpstools_helper" +require File.dirname(__FILE__) + '/lib/wpstools/wpstools_helper' begin banner() - option_parser = CustomOptionParser.new("Usage: ./wpstools.rb [options]", 60) - option_parser.separator "" - option_parser.add(["-v", "--verbose", "Verbose output"]) + option_parser = CustomOptionParser.new('Usage: ./wpstools.rb [options]', 60) + option_parser.separator '' + option_parser.add(['-v', '--verbose', 'Verbose output']) plugins = Plugins.new(option_parser) plugins.register( @@ -47,6 +47,6 @@ begin rescue => e puts "[ERROR] #{e.message}" - puts "Trace :" + puts 'Trace :' puts e.backtrace.join("\n") end