First commit for more generic enumerating and scanning
This commit is contained in:
66
lib/wpscan/modules/wp_item.rb
Normal file
66
lib/wpscan/modules/wp_item.rb
Normal file
@@ -0,0 +1,66 @@
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#++
|
||||
|
||||
module WpItem
|
||||
attr_accessor :path, :base_url, :wp_content_dir
|
||||
@version = nil
|
||||
|
||||
def get_url
|
||||
URI.parse("#{@base_url.to_s}#@wp_content_dir/#@path")
|
||||
end
|
||||
|
||||
def version
|
||||
unless @version
|
||||
response = Browser.instance.get(get_url.merge("readme.txt").to_s)
|
||||
@version = response.body[%r{stable tag: #{WpVersion.version_pattern}}i, 1]
|
||||
end
|
||||
@version
|
||||
end
|
||||
|
||||
# Is directory listing enabled?
|
||||
def directory_listing?
|
||||
# Need to remove to file part from the url
|
||||
Browser.instance.get(location_uri_from_file_url(get_url.to_s)).body[%r{<title>Index of}] ? true : false
|
||||
end
|
||||
|
||||
def extract_name_from_url(url)
|
||||
url.to_s[%r{^(https?://.*/([^/]+)/)}i, 2]
|
||||
end
|
||||
|
||||
def to_s
|
||||
item_version = version
|
||||
"#@name#{' v' + item_version if item_version}"
|
||||
end
|
||||
|
||||
def ==(item)
|
||||
item.name == @name
|
||||
end
|
||||
|
||||
def <=>(item)
|
||||
item.name <=> @name
|
||||
end
|
||||
|
||||
def location_uri_from_file_url(location_url)
|
||||
valid_location_url = location_url[%r{^(https?://.*/)[^.]+\.[^/]+$}, 1]
|
||||
unless valid_location_url
|
||||
valid_location_url = add_trailing_slash(location_url)
|
||||
end
|
||||
URI.parse(valid_location_url)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -37,11 +37,8 @@ module WpLoginProtection
|
||||
plugin_name = symbol_to_call[@@login_protection_method_pattern, 1].gsub('_', '-')
|
||||
|
||||
return @login_protection_plugin = WpPlugin.new(
|
||||
WpPlugin::create_location_url_from_name(
|
||||
plugin_name,
|
||||
@uri.to_s
|
||||
),
|
||||
:name => plugin_name
|
||||
:name => plugin_name,
|
||||
:base_url => @uri.to_s
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,92 +19,18 @@
|
||||
module WpPlugins
|
||||
|
||||
# Enumerate installed plugins.
|
||||
# Available options : see #targets_url
|
||||
# :show_progress_bar - default false
|
||||
#
|
||||
# return array of WpPlugin
|
||||
def plugins_from_aggressive_detection(options = {})
|
||||
browser = Browser.instance
|
||||
hydra = browser.hydra
|
||||
found_plugins = options[:only_vulnerable_ones] ? [] : plugins_from_passive_detection()
|
||||
request_count = 0
|
||||
queue_count = 0
|
||||
local_404_hash = error_404_hash()
|
||||
valid_response_codes = WpPlugins.valid_response_codes()
|
||||
targets_url = plugins_targets_url(options)
|
||||
show_progress_bar = options[:show_progress_bar] || false
|
||||
|
||||
targets_url.each do |target_url|
|
||||
request = browser.forge_request(target_url, :cache_timeout => 0, :follow_location => true)
|
||||
request_count += 1
|
||||
|
||||
request.on_complete do |response|
|
||||
print "\rChecking for #{targets_url.size} total plugins... #{(request_count * 100) / targets_url.size}% complete." if show_progress_bar
|
||||
|
||||
if valid_response_codes.include?(response.code)
|
||||
if Digest::MD5.hexdigest(response.body) != local_404_hash
|
||||
found_plugins << WpPlugin.new(target_url)
|
||||
end
|
||||
end
|
||||
def plugins_from_aggressive_detection(options)
|
||||
options[:file] = "#{DATA_DIR}/plugins.txt"
|
||||
options[:vulns_file] = "#{DATA_DIR}/plugin_vulns.xml"
|
||||
options[:vulns_xpath] = "//plugin[@name='#{@name}']/vulnerability"
|
||||
options[:type] = "plugins"
|
||||
result = WpDetector.aggressive_detection(options)
|
||||
result
|
||||
end
|
||||
|
||||
hydra.queue(request)
|
||||
queue_count += 1
|
||||
|
||||
if queue_count == browser.max_threads
|
||||
hydra.run
|
||||
queue_count = 0
|
||||
end
|
||||
end
|
||||
|
||||
hydra.run
|
||||
|
||||
found_plugins
|
||||
end
|
||||
|
||||
def self.valid_response_codes
|
||||
[200, 403, 301, 302]
|
||||
end
|
||||
|
||||
# Available options :
|
||||
# :only_vulnerable_ones - default false
|
||||
# :plugins_file - default DATA_DIR/plugins.txt
|
||||
# :plugin_vulns_file - default DATA_DIR/plugin_vulns.xml
|
||||
#
|
||||
# @return Array of String
|
||||
def plugins_targets_url(options = {})
|
||||
only_vulnerable = options[:only_vulnerable_ones] || false
|
||||
plugins_file = options[:plugins_file] || "#{DATA_DIR}/plugins.txt"
|
||||
plugin_vulns_file = options[:plugin_vulns_file] || "#{DATA_DIR}/plugin_vulns.xml"
|
||||
targets_url = []
|
||||
|
||||
if only_vulnerable == false
|
||||
# Open and parse the 'most popular' plugin list...
|
||||
File.open(plugins_file, 'r') do |file|
|
||||
file.readlines.collect do |line|
|
||||
targets_url << WpPlugin.create_url_from_raw(line.chomp, @uri)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xml = Nokogiri::XML(File.open(plugin_vulns_file)) do |config|
|
||||
config.noblanks
|
||||
end
|
||||
|
||||
# We check if the plugin name from the plugin_vulns_file is already in targets, otherwise we add it
|
||||
xml.xpath("//plugin").each do |node|
|
||||
plugin_name = node.attribute('name').text
|
||||
|
||||
if targets_url.grep(%r{/#{plugin_name}/}).empty?
|
||||
targets_url << WpPlugin.create_location_url_from_name(plugin_name, url())
|
||||
end
|
||||
end
|
||||
|
||||
targets_url.flatten!
|
||||
targets_url.uniq!
|
||||
# randomize the plugins array to *maybe* help in some crappy IDS/IPS/WAF detection
|
||||
targets_url.sort_by! { rand }
|
||||
end
|
||||
private
|
||||
|
||||
# http://code.google.com/p/wpscan/issues/detail?id=42
|
||||
# plugins can be found in the source code :
|
||||
@@ -112,18 +38,16 @@ module WpPlugins
|
||||
# <link rel='stylesheet' href='http://example.com/wp-content/plugins/wp-minify/..' type='text/css' media='screen'/>
|
||||
# ...
|
||||
# return array of WpPlugin
|
||||
def plugins_from_passive_detection
|
||||
def plugins_from_passive_detection(wp_content_dir)
|
||||
plugins = []
|
||||
response = Browser.instance.get(url())
|
||||
plugins_names = response.body.scan(%r{(?:[^=:]+)\s?(?:=|:)\s?(?:"|')[^"']+\\?/wp-content\\?/plugins\\?/([^/\\"']+)\\?(?:/|"|')}i)
|
||||
temp = WpDetector.passive_detection(url(), "plugins", wp_content_dir)
|
||||
|
||||
plugins_names.flatten!
|
||||
plugins_names.uniq!
|
||||
|
||||
plugins_names.each do |plugin_name|
|
||||
temp.each do |item|
|
||||
plugins << WpPlugin.new(
|
||||
WpPlugin.create_location_url_from_name(plugin_name, url()),
|
||||
:name => plugin_name
|
||||
:base_url => item[:base_url],
|
||||
:name => item[:name],
|
||||
:path => item[:path],
|
||||
:wp_content_dir => wp_content_dir
|
||||
)
|
||||
end
|
||||
plugins
|
||||
|
||||
57
lib/wpscan/wp_detector.rb
Normal file
57
lib/wpscan/wp_detector.rb
Normal file
@@ -0,0 +1,57 @@
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#++
|
||||
|
||||
class WpDetector
|
||||
|
||||
def self.aggressive_detection(options, items = [])
|
||||
WpOptions.check_options(options)
|
||||
|
||||
result = items
|
||||
unless items == nil or items.length == 0
|
||||
result = passive_detection(options[:url], options[:type], options[:wp_content_dir])
|
||||
end
|
||||
|
||||
enum_results = WpEnumerator.enumerate(options)
|
||||
enum_results.each do |enum_result|
|
||||
result << enum_result
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
# plugins and themes can be found in the source code :
|
||||
# <script src='http://example.com/wp-content/plugins/s2member/...' />
|
||||
# <link rel='stylesheet' href='http://example.com/wp-content/plugins/wp-minify/..' type='text/css' media='screen'/>
|
||||
# ...
|
||||
def self.passive_detection(url, type, wp_content_dir)
|
||||
items = []
|
||||
response = Browser.instance.get(url)
|
||||
regex1 = %r{(?:[^=:]+)\s?(?:=|:)\s?(?:"|')[^"']+\\?/}
|
||||
regex2 = %r{\\?/}
|
||||
regex3 = %r{\\?/([^/\\"']+)\\?(?:/|"|')}
|
||||
# Custom wp-content dir is now used in this regex
|
||||
names = response.body.scan(/#{regex1}#{wp_content_dir}#{regex2}#{type}#{regex3}/i)
|
||||
|
||||
names.flatten!
|
||||
names.uniq!
|
||||
|
||||
names.each do |item|
|
||||
items << { :base_url => url, :name => item, :path => "#{type}/#{item}" }
|
||||
end
|
||||
items
|
||||
end
|
||||
end
|
||||
118
lib/wpscan/wp_enumerator.rb
Normal file
118
lib/wpscan/wp_enumerator.rb
Normal file
@@ -0,0 +1,118 @@
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#++
|
||||
|
||||
# Enumerate over a given set of items and check if they exist
|
||||
class WpEnumerator
|
||||
|
||||
# Enumerate the given Targets
|
||||
#
|
||||
# ==== Attributes
|
||||
#
|
||||
# * +targets+ - targets to enumerate
|
||||
# * * +:base_url+ - Base URL
|
||||
# * * +:wp_content+ - wp-content directory
|
||||
# * * +:path+ - Path to plugin
|
||||
# * +type+ - "plugins" or "themes", item to enumerate
|
||||
# * +filename+ - filename in the data directory with paths
|
||||
# * +show_progress_bar+ - Show a progress bar during enumeration
|
||||
def self.enumerate(options = {})
|
||||
|
||||
WpOptions.check_options(options)
|
||||
|
||||
targets = self.generate_items(options)
|
||||
|
||||
found = []
|
||||
queue_count = 0
|
||||
request_count = 0
|
||||
enum_browser = Browser.instance
|
||||
enum_hydra = enum_browser.hydra
|
||||
enumerate_size = targets.size
|
||||
|
||||
targets.each do |target|
|
||||
url = target.get_url
|
||||
request = enum_browser.forge_request(url, :cache_timeout => 0, :follow_location => true)
|
||||
request_count += 1
|
||||
|
||||
request.on_complete do |response|
|
||||
if options[:show_progress_bar]
|
||||
print "\rChecking for #{enumerate_size} total #{options[:type]}... #{(request_count * 100) / enumerate_size}% complete."
|
||||
end
|
||||
if WpTarget.valid_response_codes.include?(response.code)
|
||||
if Digest::MD5.hexdigest(response.body) != options[:error_404_hash]
|
||||
found << target
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
enum_hydra.queue(request)
|
||||
queue_count += 1
|
||||
|
||||
if queue_count == enum_browser.max_threads
|
||||
enum_hydra.run
|
||||
queue_count = 0
|
||||
end
|
||||
end
|
||||
|
||||
enum_hydra.run
|
||||
found
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.generate_items(options = {})
|
||||
only_vulnerable = options[:only_vulnerable_ones]
|
||||
plugins_file = options[:file] || "#{DATA_DIR}/plugins.txt"
|
||||
plugin_vulns_file = options[:vulns_file] || "#{DATA_DIR}/plugin_vulns.xml"
|
||||
wp_content_dir = options[:wp_content_dir]
|
||||
url = options[:base_url]
|
||||
type = options[:type]
|
||||
targets_url = []
|
||||
|
||||
if only_vulnerable == false
|
||||
# Open and parse the 'most popular' plugin list...
|
||||
File.open(plugins_file, 'r') do |file|
|
||||
file.readlines.collect do |line|
|
||||
targets_url << WpPlugin.new(:base_url => url, :path => line.strip, :wp_content_dir => wp_content_dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xml = Nokogiri::XML(File.open(plugin_vulns_file)) do |config|
|
||||
config.noblanks
|
||||
end
|
||||
|
||||
# We check if the plugin name from the plugin_vulns_file is already in targets, otherwise we add it
|
||||
xml.xpath("//plugin").each do |node|
|
||||
plugin_name = node.attribute('name').text
|
||||
|
||||
if targets_url.grep(%r{/#{plugin_name}/}).empty?
|
||||
targets_url << WpPlugin.new(
|
||||
:base_url => url,
|
||||
:path => "#{type}/#{plugin_name}",
|
||||
:wp_content_dir => wp_content_dir,
|
||||
:name => plugin_name
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
targets_url.flatten!
|
||||
targets_url.uniq!
|
||||
# randomize the plugins array to *maybe* help in some crappy IDS/IPS/WAF detection
|
||||
targets_url.sort_by! { rand }
|
||||
end
|
||||
end
|
||||
50
lib/wpscan/wp_options.rb
Normal file
50
lib/wpscan/wp_options.rb
Normal file
@@ -0,0 +1,50 @@
|
||||
#--
|
||||
# WPScan - WordPress Security Scanner
|
||||
# Copyright (C) 2012
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#++
|
||||
|
||||
class WpOptions
|
||||
def self.get_empty_options
|
||||
options = {
|
||||
:url => "",
|
||||
:only_vulnerable_ones => true,
|
||||
:file => "",
|
||||
:vulns_file => "",
|
||||
:vulns_xpath => "",
|
||||
:wp_content_dir => "",
|
||||
:show_progress_bar => true,
|
||||
:error_404_hash => "",
|
||||
:type => ""
|
||||
}
|
||||
options
|
||||
end
|
||||
|
||||
def self.check_options(options)
|
||||
raise("url must be set") unless options[:url]
|
||||
raise("only_vulnerable_ones must be set") unless options[:only_vulnerable_ones]
|
||||
raise("file must be set") unless options[:file]
|
||||
raise("vulns_file must be set") unless options[:vulns_file]
|
||||
raise("vulns_xpath must be set") unless options[:vulns_xpath]
|
||||
raise("wp_content_dir must be set") unless options[:wp_content_dir]
|
||||
raise("show_progress_bar must be set") unless options[:show_progress_bar]
|
||||
raise("error_404_hash must be set") unless options[:error_404_hash]
|
||||
raise("type must be set") unless options[:type]
|
||||
unless options[:type] =~ /plugins/i or options[:type] =~ /themes/i
|
||||
raise("Unknown type #{options[:type]}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -19,38 +19,22 @@
|
||||
require "#{WPSCAN_LIB_DIR}/vulnerable"
|
||||
|
||||
class WpPlugin < Vulnerable
|
||||
@@location_url_pattern = %r{^(https?://.*/([^/]+)/)}i
|
||||
include WpItem
|
||||
|
||||
attr_reader :name
|
||||
|
||||
def initialize(location_url, options = {})
|
||||
@location_uri = WpPlugin.location_uri_from_url(location_url)
|
||||
@name = options[:name] || WpPlugin.extract_name_from_location_url(location_url)
|
||||
def initialize(options = {})
|
||||
@base_url = options[:base_url]
|
||||
@path = options[:path]
|
||||
@wp_content_dir = options[:wp_content_dir]
|
||||
@name = options[:name] || extract_name_from_url(get_url)
|
||||
@vulns_xml = options[:vulns_xml] || DATA_DIR + '/plugin_vulns.xml'
|
||||
@vulns_xpath = "//plugin[@name='#{@name}']/vulnerability"
|
||||
end
|
||||
@vulns_xpath = "//plugin[@name='#@name']/vulnerability"
|
||||
@version = nil
|
||||
|
||||
def location_url
|
||||
@location_uri.to_s
|
||||
end
|
||||
|
||||
def ==(plugin)
|
||||
plugin.name == @name
|
||||
end
|
||||
|
||||
def <=>(plugin)
|
||||
plugin.name <=> @name
|
||||
end
|
||||
|
||||
# http://code.google.com/p/wpscan/issues/detail?id=97
|
||||
def version
|
||||
response = Browser.instance.get(@location_uri.merge("readme.txt").to_s)
|
||||
response.body[%r{stable tag: #{WpVersion.version_pattern}}i, 1]
|
||||
end
|
||||
|
||||
def to_s
|
||||
version = version()
|
||||
"#{@name}#{' v' + version if version}"
|
||||
raise("base_url not set") unless @base_url
|
||||
raise("path not set") unless @path
|
||||
raise("wp_content_dir not set") unless @wp_content_dir
|
||||
raise("name not set") unless @name
|
||||
raise("vulns_xml not set") unless @vulns_xml
|
||||
end
|
||||
|
||||
# Discover any error_log files created by WordPress
|
||||
@@ -64,39 +48,7 @@ class WpPlugin < Vulnerable
|
||||
end
|
||||
|
||||
def error_log_url
|
||||
@location_uri.merge("error_log").to_s
|
||||
get_url.merge("error_log").to_s
|
||||
end
|
||||
|
||||
# Is directory listing enabled?
|
||||
# WordPress denies directory listing however,
|
||||
# forgets about the plugin directory.
|
||||
def directory_listing?
|
||||
Browser.instance.get(location_url()).body[%r{<title>Index of}] ? true : false
|
||||
end
|
||||
|
||||
def self.create_location_url_from_name(name, target_uri)
|
||||
if target_uri.is_a?(String)
|
||||
target_uri = URI.parse(target_uri)
|
||||
end
|
||||
target_uri.merge(URI.escape("$wp-plugins$/#{name}/")).to_s
|
||||
end
|
||||
|
||||
def self.create_url_from_raw(raw, target_uri)
|
||||
target_uri.merge(URI.escape("$wp-plugins$/#{raw}")).to_s
|
||||
end
|
||||
|
||||
protected
|
||||
def self.extract_name_from_location_url(location_url)
|
||||
location_url[@@location_url_pattern, 2]
|
||||
end
|
||||
|
||||
def self.location_uri_from_url(location_url)
|
||||
valid_location_url = location_url[%r{^(https?://.*/)[^.]+\.[^/]+$}, 1]
|
||||
|
||||
unless valid_location_url
|
||||
valid_location_url = add_trailing_slash(location_url)
|
||||
end
|
||||
|
||||
URI.parse(valid_location_url)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,7 +50,7 @@ class WpTarget
|
||||
url = @uri.merge("wp-login.php").to_s
|
||||
|
||||
# Let's check if the login url is redirected (to https url for example)
|
||||
if redirection = redirection(url)
|
||||
if redirection == redirection(url)
|
||||
url = redirection
|
||||
end
|
||||
|
||||
@@ -70,6 +70,11 @@ class WpTarget
|
||||
@error_404_hash
|
||||
end
|
||||
|
||||
# Valid HTTP return codes
|
||||
def self.valid_response_codes
|
||||
[200, 403, 301, 302]
|
||||
end
|
||||
|
||||
# return WpTheme
|
||||
def theme
|
||||
WpTheme.find(@uri)
|
||||
|
||||
@@ -44,6 +44,11 @@ describe WpPlugin do
|
||||
@expected_uri_string = "http://example.com/wp-content/plugins/example/"
|
||||
end
|
||||
|
||||
it "should return the uri without the file" do
|
||||
@url = "https://sub.example.com/path/to/dir/wp-content/plugins/example/readme.txt"
|
||||
@expected_uri_string = "https://sub.example.com/path/to/dir/wp-content/plugins/example/"
|
||||
end
|
||||
|
||||
it "should return the same uri" do
|
||||
@url = "http://example.com/wp-content/plugins/hello-world/"
|
||||
@expected_uri_string = @url
|
||||
@@ -65,6 +70,10 @@ describe WpPlugin do
|
||||
it "should return 'example-plugin'" do
|
||||
WpPlugin.extract_name_from_location_url('http://example.com/wp-content/plugins/example-plugin/').should === 'example-plugin'
|
||||
end
|
||||
|
||||
it "should return 'example-plugin'" do
|
||||
WpPlugin.extract_name_from_location_url('https://sub.example.com/path/to/a/wp-content/plugins/example-plugin/').should === 'example-plugin'
|
||||
end
|
||||
end
|
||||
|
||||
describe "#create_location_url_from_name" do
|
||||
|
||||
62
wpscan.rb
62
wpscan.rb
@@ -78,9 +78,7 @@ begin
|
||||
end
|
||||
end
|
||||
|
||||
if wp_content_dir = wp_target.wp_content_dir()
|
||||
Browser.instance.variables_to_replace_in_url = {"$wp-content$" => wp_content_dir, "$wp-plugins$" => wp_target.wp_plugins_dir()}
|
||||
else
|
||||
unless wp_target.wp_content_dir
|
||||
raise "The wp_content_dir has not been found, please supply it with --wp-content-dir"
|
||||
end
|
||||
|
||||
@@ -89,7 +87,7 @@ begin
|
||||
puts "| Started on #{Time.now.asctime}"
|
||||
puts
|
||||
|
||||
if wp_theme = wp_target.theme
|
||||
if wp_theme == wp_target.theme
|
||||
theme_version = wp_theme.version
|
||||
puts "[!] The WordPress theme in use is #{wp_theme}"
|
||||
|
||||
@@ -98,8 +96,8 @@ begin
|
||||
puts "[+] We have identified #{theme_vulnerabilities.size} vulnerabilities for this theme :"
|
||||
theme_vulnerabilities.each do |vulnerability|
|
||||
puts
|
||||
puts " | * Title: " + vulnerability.title
|
||||
puts " | * Reference: " + vulnerability.reference
|
||||
puts " | * Title: #{vulnerability.title}"
|
||||
puts " | * Reference: #{vulnerability.reference}"
|
||||
end
|
||||
puts
|
||||
end
|
||||
@@ -132,7 +130,7 @@ begin
|
||||
puts
|
||||
end
|
||||
|
||||
if wp_version = wp_target.version
|
||||
if wp_version == wp_target.version
|
||||
puts "[!] WordPress version #{wp_version.number} identified from #{wp_version.discovery_method}"
|
||||
|
||||
version_vulnerabilities = wp_version.vulnerabilities
|
||||
@@ -142,33 +140,33 @@ begin
|
||||
puts "[+] We have identified #{version_vulnerabilities.size} vulnerabilities from the version number :"
|
||||
version_vulnerabilities.each do |vulnerability|
|
||||
puts
|
||||
puts " | * Title: " + vulnerability.title
|
||||
puts " | * Reference: " + vulnerability.reference
|
||||
puts " | * Title: #{vulnerability.title}"
|
||||
puts " | * Reference: #{vulnerability.reference}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if wpscan_options.enumerate_plugins == nil and wpscan_options.enumerate_only_vulnerable_plugins == nil
|
||||
puts
|
||||
print "[+] Enumerating plugins from passive detection ... "
|
||||
puts "[+] Enumerating plugins from passive detection ... "
|
||||
|
||||
plugins = wp_target.plugins_from_passive_detection
|
||||
unless plugins.empty?
|
||||
print "#{plugins.size} found :\n"
|
||||
puts "#{plugins.size} found :"
|
||||
|
||||
plugins.each do |plugin|
|
||||
puts
|
||||
puts " | Name: " + plugin.name
|
||||
puts " | Location: " + plugin.location_url.gsub("$wp-plugins$", wp_target.wp_plugins_dir()) #Hotfix
|
||||
puts " | Name: #{plugin.name}"
|
||||
puts " | Location: #{plugin.get_url}"
|
||||
|
||||
plugin.vulnerabilities.each do |vulnerability|
|
||||
puts " |"
|
||||
puts " | [!] " + vulnerability.title
|
||||
puts " | * Reference: " + vulnerability.reference
|
||||
puts " | [!] #{vulnerability.title}"
|
||||
puts " | * Reference: #{vulnerability.reference}"
|
||||
end
|
||||
end
|
||||
else
|
||||
print "No plugins found :(\n"
|
||||
puts "No plugins found :("
|
||||
end
|
||||
end
|
||||
|
||||
@@ -178,20 +176,22 @@ begin
|
||||
puts "[+] Enumerating installed plugins #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_plugins} ..."
|
||||
puts
|
||||
|
||||
plugins = wp_target.plugins_from_aggressive_detection(
|
||||
:only_vulnerable_ones => wpscan_options.enumerate_only_vulnerable_plugins,
|
||||
:show_progress_bar => true
|
||||
)
|
||||
options = WpOptions.get_empty_options
|
||||
options[:base_url] = wp_target.uri
|
||||
options[:only_vulnerable_ones] = wpscan_options.enumerate_only_vulnerable_plugins,
|
||||
options[:show_progress_bar] = true,
|
||||
options[:wp_content_dir] = wp_target.wp_content_dir
|
||||
|
||||
plugins = wp_target.plugins_from_aggressive_detection(options)
|
||||
unless plugins.empty?
|
||||
puts
|
||||
puts
|
||||
puts "[+] We found " + plugins.size.to_s + " plugins:"
|
||||
puts "[+] 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.location_url.gsub("$wp-plugins$", wp_target.wp_plugins_dir()) #Hotfix
|
||||
|
||||
puts " | Location: #{plugin.get_url}"
|
||||
puts " | Directory listing enabled? #{plugin.directory_listing? ? "Yes." : "No."}"
|
||||
|
||||
plugin.vulnerabilities.each do |vulnerability|
|
||||
@@ -199,8 +199,8 @@ begin
|
||||
#vulnerability['vulnerability'][0]['postdata'] == nil ? "" : postdata = CGI.unescapeHTML(vulnerability['vulnerability'][0]['postdata']) # postdata
|
||||
|
||||
puts " |"
|
||||
puts " | [!] " + vulnerability.title
|
||||
puts " | * Reference: " + vulnerability.reference
|
||||
puts " | [!] #{vulnerability.title}"
|
||||
puts " | * Reference: #{vulnerability.reference}"
|
||||
|
||||
# This has been commented out as MSF are moving from
|
||||
# XML-RPC to MessagePack.
|
||||
@@ -212,7 +212,7 @@ begin
|
||||
end
|
||||
|
||||
if plugin.error_log?
|
||||
puts " | [!] A WordPress error_log file has been found : " + plugin.error_log_url
|
||||
puts " | [!] A WordPress error_log file has been found : #{plugin.error_log_url}"
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -230,11 +230,11 @@ begin
|
||||
timthumbs = wp_target.timthumbs
|
||||
|
||||
puts
|
||||
puts "[+] We found " + timthumbs.size.to_s + " timthumb file/s :"
|
||||
puts "[+] We found #{timthumbs.size.to_s} timthumb file/s :"
|
||||
puts
|
||||
|
||||
timthumbs.each do |file_url|
|
||||
puts " | [!] " + file_url
|
||||
puts " | [!] #{file_url}"
|
||||
end
|
||||
puts
|
||||
puts " * Reference: http://www.exploit-db.com/exploits/17602/"
|
||||
@@ -259,10 +259,10 @@ begin
|
||||
exit(1)
|
||||
else
|
||||
puts
|
||||
puts "We found the following " + usernames.length.to_s + " username/s :"
|
||||
puts "We found the following #{usernames.length.to_s} username/s :"
|
||||
puts
|
||||
|
||||
usernames.each {|username| puts " " + username}
|
||||
usernames.each {|username| puts " #{username}"}
|
||||
end
|
||||
|
||||
else
|
||||
@@ -296,7 +296,7 @@ begin
|
||||
end
|
||||
|
||||
puts
|
||||
puts '[+] Finished at ' + Time.now.asctime
|
||||
puts "[+] Finished at #{Time.now.asctime}"
|
||||
exit() # must exit!
|
||||
rescue => e
|
||||
puts "[ERROR] #{e.message}"
|
||||
|
||||
Reference in New Issue
Block a user