Files
wpscan/lib/common/collections/wp_items/detectable.rb
2014-07-31 11:14:22 +02:00

205 lines
5.9 KiB
Ruby
Executable File

# encoding: UTF-8
class WpItems < Array
module Detectable
attr_reader :vulns_file, :item_xpath
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
# @option options [ Boolean ] :show_progression Whether or not output the progress bar
# @option options [ Boolean ] :only_vulnerable Only check for vulnerable items
# @option options [ String ] :exclude_content
#
# @return [ WpItems ]
def aggressive_detection(wp_target, options = {})
browser = Browser.instance
hydra = browser.hydra
targets = targets_items(wp_target, options)
progress_bar = progress_bar(targets.size, options)
queue_count = 0
exist_options = {
error_404_hash: wp_target.error_404_hash,
homepage_hash: wp_target.homepage_hash,
exclude_content: options[:exclude_content] ? %r{#{options[:exclude_content]}} : nil
}
# If we only want the vulnerable ones, the passive detection is ignored
# Otherwise, a passive detection is performed, and results will be merged
results = options[:only_vulnerable] ? new : passive_detection(wp_target, options)
targets.each do |target_item|
request = browser.forge_request(target_item.url, request_params)
request.on_complete do |response|
progress_bar.progress += 1 if options[:show_progression]
if target_item.exists?(exist_options, response)
if !results.include?(target_item)
if !options[:only_vulnerable] || options[:only_vulnerable] && target_item.vulnerable?
results << target_item
end
end
end
end
hydra.queue(request)
queue_count += 1
if queue_count >= browser.max_threads
hydra.run
queue_count = 0
puts "Sent #{browser.max_threads} requests ..." if options[:verbose]
end
end
# run the remaining requests
hydra.run
results.sort!
results # can't just return results.sort because the #sort returns an array, and we want a WpItems
end
# @param [ Integer ] targets_size
# @param [ Hash ] options
#
# @return [ ProgressBar ]
# :nocov:
def progress_bar(targets_size, options)
if options[:show_progression]
ProgressBar.create(
format: '%t %a <%B> (%c / %C) %P%% %e',
title: ' ', # Used to craete a left margin
total: targets_size
)
end
end
# :nocov:
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
#
# @return [ WpItems ]
def passive_detection(wp_target, options = {})
results = new(wp_target)
body = Browser.get(wp_target.url).body
# improves speed
body = remove_base64_images_from_html(body)
names = body.scan(passive_detection_pattern(wp_target))
names.flatten.uniq.each { |name| results.add(name) }
results.sort!
results
end
protected
# @param [ WpTarget ] wp_target
#
# @return [ Regex ]
def passive_detection_pattern(wp_target)
type = self.to_s.gsub(/Wp/, '').downcase
regex1 = %r{(?:[^=:\(]+)\s?(?:=|:|\()\s?(?:"|')[^"']+\\?/}
regex2 = %r{\\?/}
regex3 = %r{\\?/([^/\\"']+)\\?(?:/|"|')}
/#{regex1}#{Regexp.escape(wp_target.wp_content_dir)}#{regex2}#{Regexp.escape(type)}#{regex3}/i
end
# The default request parameters
#
# @return [ Hash ]
def request_params; { cache_ttl: 0, followlocation: true } end
# @param [ WpTarget ] wp_target
# @param [ options ] options
# @option options [ Boolean ] :only_vulnerable
# @option options [ String ] :file The path to the file containing the targets
#
# @return [ Array<WpItem> ]
def targets_items(wp_target, options = {})
item_class = self.item_class
vulns_file = self.vulns_file
targets = vulnerable_targets_items(wp_target, item_class, vulns_file)
unless options[:only_vulnerable]
unless options[:file]
raise 'A file must be supplied'
end
targets += targets_items_from_file(options[:file], wp_target, item_class, vulns_file)
end
targets.uniq! { |t| t.name }
targets.sort_by { rand }
end
# @param [ WpTarget ] wp_target
# @param [ Class ] item_class
# @param [ String ] vulns_file
#
# @return [ Array<WpItem> ]
def vulnerable_targets_items(wp_target, item_class, vulns_file)
targets = []
json = json(vulns_file)
[*json].each do |item|
targets << create_item(
item_class,
item.keys.inject,
wp_target,
vulns_file
)
end
targets
end
# @param [ Class ] klass
# @param [ String ] name
# @param [ WpTarget ] wp_target
# @option [ String ] vulns_file
#
# @return [ WpItem ]
def create_item(klass, name, wp_target, vulns_file = nil)
klass.new(
wp_target.uri,
name: name,
vulns_file: vulns_file,
wp_content_dir: wp_target.wp_content_dir,
wp_plugins_dir: wp_target.wp_plugins_dir
)
end
# @param [ String ] file
# @param [ WpTarget ] wp_target
# @param [ Class ] item_class
# @param [ String ] vulns_file
#
# @return [ Array<WpItem> ]
def targets_items_from_file(file, wp_target, item_class, vulns_file)
targets = []
File.open(file, 'r') do |f|
f.readlines.collect do |item_name|
targets << create_item(
item_class,
item_name.strip,
wp_target,
vulns_file
)
end
end
targets
end
# @return [ Class ]
def item_class
Object.const_get(self.to_s.gsub(/.$/, ''))
end
end
end