New enumeration system
This commit is contained in:
8
lib/common/collections/vulnerabilities.rb
Normal file
8
lib/common/collections/vulnerabilities.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'common/collections/vulnerabilities/output'
|
||||
|
||||
class Vulnerabilities < Array
|
||||
include Vulnerabilities::Output
|
||||
|
||||
end
|
||||
13
lib/common/collections/vulnerabilities/output.rb
Normal file
13
lib/common/collections/vulnerabilities/output.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class Vulnerabilities < Array
|
||||
module Output
|
||||
|
||||
def output
|
||||
self.each do |v|
|
||||
v.output
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
10
lib/common/collections/wp_items.rb
Executable file
10
lib/common/collections/wp_items.rb
Executable file
@@ -0,0 +1,10 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'common/collections/wp_items/detectable'
|
||||
require 'common/collections/wp_items/output'
|
||||
|
||||
class WpItems < Array
|
||||
extend WpItems::Detectable
|
||||
include WpItems::Output
|
||||
|
||||
end
|
||||
154
lib/common/collections/wp_items/detectable.rb
Executable file
154
lib/common/collections/wp_items/detectable.rb
Executable file
@@ -0,0 +1,154 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpItems < Array
|
||||
|
||||
module Detectable
|
||||
|
||||
# The default request parameters
|
||||
def request_params; { cache_ttl: 0, followlocation: true } end
|
||||
|
||||
# options:
|
||||
# option name - default - description
|
||||
# show_progress - false - Output a progress bar
|
||||
# only_vulnerable - nil - Only check for vulnerable items
|
||||
# exclude_content - nil -
|
||||
def aggressive_detection(wp_target, options = {})
|
||||
queue_count = 0
|
||||
request_count = 0
|
||||
browser = Browser.instance
|
||||
hydra = browser.hydra
|
||||
targets = targets_items(wp_target, options)
|
||||
targets_size = targets.size
|
||||
show_progression = options[:show_progression] || false
|
||||
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_count += 1
|
||||
|
||||
request.on_complete do |response|
|
||||
|
||||
print "\rChecking for #{targets_size} total ... #{(request_count * 100) / targets_size}% complete." if show_progression
|
||||
|
||||
if target_item.exists?(exist_options, response)
|
||||
if !results.include?(target_item)
|
||||
results << target_item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
hydra.queue(request)
|
||||
queue_count += 1
|
||||
|
||||
if queue_count == browser.max_threads
|
||||
hydra.run
|
||||
queue_count = 0
|
||||
end
|
||||
end
|
||||
|
||||
hydra.run
|
||||
results.sort!
|
||||
results # can't just return results.sort because the #sort returns an array, and we want a WpItems
|
||||
end
|
||||
|
||||
def passive_detection(wp_target, options = {})
|
||||
results = new
|
||||
item_class = self.item_class
|
||||
type = self.to_s.gsub(/Wp/, '').downcase
|
||||
response = Browser.instance.get(wp_target.url)
|
||||
item_options = {
|
||||
wp_content_dir: wp_target.wp_content_dir,
|
||||
wp_plugins_dir: wp_target.wp_plugins_dir,
|
||||
vulns_file: vulns_file
|
||||
}
|
||||
|
||||
regex1 = %r{(?:[^=:]+)\s?(?:=|:)\s?(?:"|')[^"']+\\?/}
|
||||
regex2 = %r{\\?/}
|
||||
regex3 = %r{\\?/([^/\\"']+)\\?(?:/|"|')}
|
||||
|
||||
names = response.body.scan(/#{regex1}#{Regexp.escape(wp_target.wp_content_dir)}#{regex2}#{Regexp.escape(type)}#{regex3}/i)
|
||||
|
||||
names.flatten.uniq.each do |name|
|
||||
results << item_class.new(wp_target.uri, item_options.merge(name: name))
|
||||
end
|
||||
|
||||
results.sort!
|
||||
results
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
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
|
||||
|
||||
def vulnerable_targets_items(wp_target, item_class, vulns_file)
|
||||
targets = []
|
||||
xml = xml(vulns_file)
|
||||
|
||||
xml.xpath(item_xpath).each do |node|
|
||||
targets << create_item(
|
||||
item_class,
|
||||
node.attribute('name').text,
|
||||
wp_target,
|
||||
vulns_file
|
||||
)
|
||||
end
|
||||
targets
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
11
lib/common/collections/wp_items/output.rb
Normal file
11
lib/common/collections/wp_items/output.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpItems < Array
|
||||
module Output
|
||||
|
||||
def output
|
||||
self.each { |item| item.output }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
8
lib/common/collections/wp_plugins.rb
Executable file
8
lib/common/collections/wp_plugins.rb
Executable file
@@ -0,0 +1,8 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'common/collections/wp_plugins/detectable'
|
||||
|
||||
class WpPlugins < WpItems
|
||||
extend WpPlugins::Detectable
|
||||
|
||||
end
|
||||
18
lib/common/collections/wp_plugins/detectable.rb
Normal file
18
lib/common/collections/wp_plugins/detectable.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpPlugins < WpItems
|
||||
module Detectable
|
||||
|
||||
def vulns_file
|
||||
unless @vulns_file
|
||||
@vulns_file = PLUGINS_VULNS_FILE
|
||||
end
|
||||
@vulns_file
|
||||
end
|
||||
|
||||
def item_xpath
|
||||
'//plugin'
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
8
lib/common/collections/wp_themes.rb
Executable file
8
lib/common/collections/wp_themes.rb
Executable file
@@ -0,0 +1,8 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'common/collections/wp_themes/detectable'
|
||||
|
||||
class WpThemes < WpItems
|
||||
extend WpThemes::Detectable
|
||||
|
||||
end
|
||||
18
lib/common/collections/wp_themes/detectable.rb
Normal file
18
lib/common/collections/wp_themes/detectable.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpThemes < WpItems
|
||||
module Detectable
|
||||
|
||||
def vulns_file
|
||||
unless @vulns_file
|
||||
@vulns_file = THEMES_VULNS_FILE
|
||||
end
|
||||
@vulns_file
|
||||
end
|
||||
|
||||
def item_xpath
|
||||
'//theme'
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
8
lib/common/collections/wp_timthumbs.rb
Executable file
8
lib/common/collections/wp_timthumbs.rb
Executable file
@@ -0,0 +1,8 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'common/collections/wp_timthumbs/detectable'
|
||||
|
||||
class WpTimthumbs < WpItems
|
||||
extend WpTimthumbs::Detectable
|
||||
|
||||
end
|
||||
56
lib/common/collections/wp_timthumbs/detectable.rb
Normal file
56
lib/common/collections/wp_timthumbs/detectable.rb
Normal file
@@ -0,0 +1,56 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTimthumbs < WpItems
|
||||
module Detectable
|
||||
|
||||
# No passive detection
|
||||
# @return [ WpTimthumbs ]
|
||||
def passive_detection(wp_target, topns = {})
|
||||
new
|
||||
end
|
||||
|
||||
def targets_items(wp_target, options = {})
|
||||
unless options[:file]
|
||||
raise 'A file must be supplied'
|
||||
end
|
||||
|
||||
targets = options[:theme_name] ? theme_timthumbs(options[:theme_name], wp_target) : []
|
||||
|
||||
File.open(options[:file], 'r') do |f|
|
||||
f.readlines.collect do |path|
|
||||
targets << create_item(wp_target, path.strip)
|
||||
end
|
||||
end
|
||||
|
||||
targets.uniq { |i| i.url }
|
||||
end
|
||||
|
||||
# @return [ WpTimthumb Array ]
|
||||
def theme_timthumbs(theme_name, wp_target)
|
||||
targets = []
|
||||
wp_timthumb = create_item(wp_target)
|
||||
|
||||
%w{
|
||||
timthumb.php lib/timthumb.php inc/timthumb.php includes/timthumb.php
|
||||
scripts/timthumb.php tools/timthumb.php functions/timthumb.php
|
||||
}.each do |path|
|
||||
wp_timthumb.path = "$wp-content$/themes/#{theme_name}/#{path}"
|
||||
|
||||
targets << wp_timthumb.dup
|
||||
end
|
||||
targets
|
||||
end
|
||||
|
||||
# @return [ WpTimthumb ]
|
||||
def create_item(wp_target, path = nil)
|
||||
options = {
|
||||
wp_content_dir: wp_target.wp_content_dir,
|
||||
wp_plugins_dir: wp_target.wp_plugins_dir
|
||||
}
|
||||
|
||||
options.merge!(path: path) if path
|
||||
|
||||
WpTimthumb.new(wp_target.uri, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
10
lib/common/collections/wp_users.rb
Executable file
10
lib/common/collections/wp_users.rb
Executable file
@@ -0,0 +1,10 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'common/collections/wp_users/detectable'
|
||||
require 'common/collections/wp_users/output'
|
||||
|
||||
class WpUsers < WpItems
|
||||
extend WpUsers::Detectable
|
||||
include WpUsers::Output
|
||||
|
||||
end
|
||||
27
lib/common/collections/wp_users/detectable.rb
Executable file
27
lib/common/collections/wp_users/detectable.rb
Executable file
@@ -0,0 +1,27 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpUsers < WpItems
|
||||
module Detectable
|
||||
|
||||
def request_params; {} end
|
||||
|
||||
# options:
|
||||
# :range - default 1..10
|
||||
def targets_items(wp_target, options = {})
|
||||
range = options[:range] || (1..10)
|
||||
targets = []
|
||||
|
||||
range.each do |user_id|
|
||||
targets << WpUser.new(wp_target.uri, id: user_id)
|
||||
end
|
||||
targets
|
||||
end
|
||||
|
||||
# No passive detection
|
||||
# @return [ WpUsers ]
|
||||
def passive_detection(wp_target, options = {})
|
||||
new
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
29
lib/common/collections/wp_users/output.rb
Normal file
29
lib/common/collections/wp_users/output.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpUsers < WpItems
|
||||
module Output
|
||||
|
||||
# TODO : create a generic method to output tabs
|
||||
def output(left_margin = '')
|
||||
max_id_length = self.sort { |a, b| a.id.to_s.length <=> b.id.to_s.length }.last.id.to_s.length
|
||||
max_login_length = self.sort { |a, b| a.login.length <=> b.login.length }.last.login.length
|
||||
max_display_name_length = self.sort { |a, b| a.display_name.length <=> b.display_name.length }.last.display_name.length
|
||||
|
||||
inner_space = 2
|
||||
id_length = (max_id_length + inner_space * 2) /2 *2
|
||||
login_length = max_login_length + inner_space * 2
|
||||
display_name_length = max_display_name_length + inner_space * 2
|
||||
|
||||
puts left_margin + '+' * (id_length + login_length + display_name_length + 4)
|
||||
puts left_margin + '|' + 'id'.center(id_length) + '|' + 'login'.center(login_length) + '|' + 'display name'.center(display_name_length) + '|'
|
||||
puts left_margin + '|' + '+' * (id_length + login_length + display_name_length + 2) + '|'
|
||||
|
||||
self.each do |u|
|
||||
puts left_margin + '|' + u.id.to_s.center(id_length) + '|' + u.login.center(login_length) + '|' + u.display_name.center(display_name_length) + '|'
|
||||
end
|
||||
|
||||
puts left_margin + '+' * (id_length + login_length + display_name_length + 4)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -26,7 +26,11 @@ WPSCAN_LIB_DIR = LIB_DIR + '/wpscan'
|
||||
WPSTOOLS_LIB_DIR = LIB_DIR + '/wpstools'
|
||||
UPDATER_LIB_DIR = LIB_DIR + '/updater'
|
||||
COMMON_LIB_DIR = LIB_DIR + '/common'
|
||||
MODELS_LIB_DIR = COMMON_LIB_DIR + '/models'
|
||||
COLLECTIONS_LIB_DIR = COMMON_LIB_DIR + '/collections'
|
||||
|
||||
LOG_FILE = ROOT_DIR + '/log.txt'
|
||||
|
||||
# Plugins directories
|
||||
COMMON_PLUGINS_DIR = COMMON_LIB_DIR + '/plugins'
|
||||
WPSCAN_PLUGINS_DIR = WPSCAN_LIB_DIR + '/plugins' # Not used ATM
|
||||
@@ -49,6 +53,7 @@ LOCAL_FILES_XSD = DATA_DIR + '/local_vulnerable_files.xsd'
|
||||
WPSCAN_VERSION = '2.1'
|
||||
|
||||
$LOAD_PATH.unshift(LIB_DIR)
|
||||
$LOAD_PATH.unshift(MODELS_LIB_DIR)
|
||||
|
||||
require 'environment'
|
||||
|
||||
@@ -75,31 +80,6 @@ def add_trailing_slash(url)
|
||||
url =~ /\/$/ ? url : "#{url}/"
|
||||
end
|
||||
|
||||
# Gets the string all elements in stringarray ends with
|
||||
def get_equal_string_end(stringarray = [''])
|
||||
already_found = ''
|
||||
looping = true
|
||||
counter = -1
|
||||
if stringarray.kind_of? Array and stringarray.length > 1
|
||||
base = stringarray[0]
|
||||
while looping
|
||||
character = base[counter, 1]
|
||||
stringarray.each do |s|
|
||||
if s[counter, 1] != character
|
||||
looping = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if looping == false or (counter * -1) > base.length
|
||||
break
|
||||
end
|
||||
already_found = "#{character if character}#{already_found}"
|
||||
counter -= 1
|
||||
end
|
||||
end
|
||||
already_found
|
||||
end
|
||||
|
||||
# loading the updater
|
||||
require_files_from_directory(UPDATER_LIB_DIR)
|
||||
@updater = UpdaterFactory.get_updater(ROOT_DIR)
|
||||
@@ -138,12 +118,6 @@ def green(text)
|
||||
colorize(text, 32)
|
||||
end
|
||||
|
||||
def get_metasploit_url(module_path)
|
||||
# remove leading slash
|
||||
module_path = module_path.sub(/^\//, '')
|
||||
"http://www.metasploit.com/modules/#{module_path}"
|
||||
end
|
||||
|
||||
def xml(file)
|
||||
Nokogiri::XML(File.open(file)) do |config|
|
||||
config.noblanks
|
||||
|
||||
26
lib/common/models/vulnerability.rb
Executable file
26
lib/common/models/vulnerability.rb
Executable file
@@ -0,0 +1,26 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'vulnerability/output'
|
||||
|
||||
class Vulnerability
|
||||
include Vulnerability::Output
|
||||
|
||||
attr_accessor :title, :references, :type, :metasploit_modules
|
||||
|
||||
def initialize(title, type, references, metasploit_modules = [])
|
||||
@title = title
|
||||
@type = type
|
||||
@references = references
|
||||
@metasploit_modules = metasploit_modules
|
||||
end
|
||||
|
||||
def self.load_from_xml_node(xml_node)
|
||||
new(
|
||||
xml_node.search('title').text,
|
||||
xml_node.search('type').text,
|
||||
xml_node.search('reference').map(&:text),
|
||||
xml_node.search('metasploit').map(&:text)
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
25
lib/common/models/vulnerability/output.rb
Normal file
25
lib/common/models/vulnerability/output.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class Vulnerability
|
||||
module Output
|
||||
|
||||
# output the vulnerability
|
||||
def output
|
||||
puts ' |'
|
||||
puts ' | ' + red("* Title: #{title}")
|
||||
references.each do |r|
|
||||
puts ' | ' + red("* Reference: #{r}")
|
||||
end
|
||||
metasploit_modules.each do |m|
|
||||
puts ' | ' + red("* Metasploit module: #{metasploit_module_url(m)}")
|
||||
end
|
||||
end
|
||||
|
||||
def self.metasploit_module_url(module_path)
|
||||
# remove leading slash
|
||||
module_path = module_path.sub(/^\//, '')
|
||||
"http://www.metasploit.com/modules/#{module_path}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
80
lib/common/models/wp_item.rb
Executable file
80
lib/common/models/wp_item.rb
Executable file
@@ -0,0 +1,80 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'wp_item/findable'
|
||||
require 'wp_item/versionable'
|
||||
require 'wp_item/vulnerable'
|
||||
require 'wp_item/existable'
|
||||
require 'wp_item/infos'
|
||||
require 'wp_item/output'
|
||||
|
||||
class WpItem
|
||||
|
||||
extend WpItem::Findable
|
||||
include WpItem::Versionable
|
||||
include WpItem::Vulnerable
|
||||
include WpItem::Existable
|
||||
include WpItem::Infos
|
||||
include WpItem::Output
|
||||
|
||||
attr_reader :path
|
||||
attr_accessor :name, :wp_content_dir, :wp_plugins_dir
|
||||
|
||||
def allowed_options
|
||||
[:name, :wp_content_dir, :wp_plugins_dir, :path, :version, :vulns_file]
|
||||
end
|
||||
|
||||
# options :
|
||||
# See allowed_options
|
||||
def initialize(target_base_uri, options = {})
|
||||
|
||||
options[:wp_content_dir] ||= 'wp-content'
|
||||
options[:wp_plugins_dir] ||= options[:wp_content_dir] + '/plugins'
|
||||
|
||||
set_options(options)
|
||||
forge_uri(target_base_uri)
|
||||
end
|
||||
|
||||
def set_options(options)
|
||||
allowed_options.each do |allowed_option|
|
||||
if options.has_key?(allowed_option)
|
||||
method = :"#{allowed_option}="
|
||||
|
||||
if self.respond_to?(method)
|
||||
self.send(method, options[allowed_option])
|
||||
else
|
||||
raise "#{self.class} does not respond to #{method}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
private :set_options
|
||||
|
||||
def forge_uri(target_base_uri)
|
||||
@uri = target_base_uri
|
||||
end
|
||||
|
||||
def uri
|
||||
return path ? @uri.merge(path) : @uri
|
||||
end
|
||||
|
||||
def url; uri.to_s end
|
||||
|
||||
def path=(path)
|
||||
@path = URI.encode(
|
||||
path.gsub(/\$wp-plugins\$/i, wp_plugins_dir).gsub(/\$wp-content\$/i, wp_content_dir)
|
||||
)
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
name <=> other.name
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
name === other.name
|
||||
end
|
||||
|
||||
def ===(other)
|
||||
self == other && version === other.version
|
||||
end
|
||||
|
||||
end
|
||||
55
lib/common/models/wp_item/existable.rb
Executable file
55
lib/common/models/wp_item/existable.rb
Executable file
@@ -0,0 +1,55 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
# HACK
|
||||
module Typhoeus
|
||||
class Response
|
||||
|
||||
# Compare the body hash to error_404_hash and homepage_hash
|
||||
# returns true if they are different, false otherwise
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def has_valid_hash?(error_404_hash, homepage_hash)
|
||||
body_hash = Digest::MD5.hexdigest(self.body)
|
||||
|
||||
body_hash != error_404_hash && body_hash != homepage_hash
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class WpItem
|
||||
module Existable
|
||||
|
||||
def exists?(options = {}, response = nil)
|
||||
unless response
|
||||
response = Browser.instance.get(url)
|
||||
end
|
||||
exists_from_response?(response, options)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# options:
|
||||
# :error_404_hash
|
||||
# :homepage_hash
|
||||
# :exclude_content REGEXP
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def exists_from_response?(response, options = {})
|
||||
# FIXME : The response is supposed to follow locations, so we should not have 301 or 302.
|
||||
# However, due to an issue with Typhoeus or Webmock, the location is not followed in specs
|
||||
if [200, 301, 302, 401, 403].include?(response.code)
|
||||
if response.has_valid_hash?(options[:error_404_hash], options[:homepage_hash])
|
||||
if options[:exclude_content]
|
||||
unless response.body.match(options[:exclude_content])
|
||||
return true
|
||||
end
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
15
lib/common/models/wp_item/findable.rb
Executable file
15
lib/common/models/wp_item/findable.rb
Executable file
@@ -0,0 +1,15 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpItem
|
||||
attr_reader :found_from
|
||||
|
||||
#def allowed_options; super << :found_from end
|
||||
|
||||
def found_from=(method)
|
||||
@found_from = method[%r{find_from_(.*)}, 1].gsub('_', ' ')
|
||||
end
|
||||
|
||||
module Findable
|
||||
|
||||
end
|
||||
end
|
||||
58
lib/common/models/wp_item/infos.rb
Normal file
58
lib/common/models/wp_item/infos.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpItem
|
||||
module Infos
|
||||
|
||||
# @return [ Boolean ]
|
||||
def has_readme?
|
||||
Browser.instance.get(readme_url).code == 200 ? true : false
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def readme_url
|
||||
@uri.merge('readme.txt').to_s
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def wordpress_url
|
||||
|
||||
end
|
||||
|
||||
def wordpress_org_item?
|
||||
|
||||
end
|
||||
|
||||
# @return [ Boolean ]
|
||||
def has_changelog?
|
||||
Browser.instance.get(changelog_url).code == 200 ? true : false
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def changelog_url
|
||||
@uri.merge('changelog.txt').to_s
|
||||
end
|
||||
|
||||
# @return [ Boolean ]
|
||||
def has_directory_listing?
|
||||
Browser.instance.get(@uri.to_s).body[%r{<title>Index of}] ? true : false
|
||||
end
|
||||
|
||||
# Discover any error_log files created by WordPress
|
||||
# These are created by the WordPress error_log() function
|
||||
# They are normally found in the /plugins/ directory,
|
||||
# however can also be found in their specific plugin dir.
|
||||
# http://www.exploit-db.com/ghdb/3714/
|
||||
#
|
||||
# @return [ Boolean ]
|
||||
def has_error_log?
|
||||
response_body = Browser.instance.get(error_log_url, headers: {'range' => 'bytes=0-700'}).body
|
||||
response_body[%r{PHP Fatal error}i] ? true : false
|
||||
end
|
||||
|
||||
# @return [ String ]
|
||||
def error_log_url
|
||||
@uri.merge('error_log').to_s
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
24
lib/common/models/wp_item/output.rb
Normal file
24
lib/common/models/wp_item/output.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpItem
|
||||
module Output
|
||||
|
||||
# @return [ Void ]
|
||||
def output
|
||||
puts
|
||||
puts " | Name: #{self}" #this will also output the version number if detected
|
||||
puts " | Location: #{url}"
|
||||
#puts " | WordPress: #{wordpress_url}" if wordpress_org_item?
|
||||
puts ' | Directory listing enabled: Yes' if has_directory_listing?
|
||||
puts " | Readme: #{readme_url}" if has_readme?
|
||||
puts " | Changelog: #{changelog_url}" if has_changelog?
|
||||
|
||||
vulnerabilities.output
|
||||
|
||||
if has_error_log?
|
||||
puts ' | ' + red('[!]') + " An error_log file has been found : #{error_log_url}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
25
lib/common/models/wp_item/versionable.rb
Executable file
25
lib/common/models/wp_item/versionable.rb
Executable file
@@ -0,0 +1,25 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpItem
|
||||
attr_writer :version
|
||||
|
||||
#def allowed_options; super << :version end
|
||||
|
||||
module Versionable
|
||||
|
||||
# Get the version from the readme.txt
|
||||
def version
|
||||
unless @version
|
||||
response = Browser.instance.get(readme_url)
|
||||
@version = response.body[%r{stable tag: #{WpVersion.version_pattern}}i, 1]
|
||||
end
|
||||
@version
|
||||
end
|
||||
|
||||
def to_s
|
||||
item_version = self.version
|
||||
"#@name#{' v' + item_version.strip if item_version}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
26
lib/common/models/wp_item/vulnerable.rb
Executable file
26
lib/common/models/wp_item/vulnerable.rb
Executable file
@@ -0,0 +1,26 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpItem
|
||||
|
||||
# moved this into the module ?
|
||||
def vulns_file=(file)
|
||||
if File.exists?(file)
|
||||
@vulns_file = file
|
||||
else
|
||||
raise "The file #{file} does not exist"
|
||||
end
|
||||
end
|
||||
|
||||
module Vulnerable
|
||||
# @return [ Vulnerabilities ]
|
||||
def vulnerabilities
|
||||
xml = xml(vulns_file)
|
||||
vulnerabilities = Vulnerabilities.new
|
||||
|
||||
xml.xpath(vulns_xpath).each do |node|
|
||||
vulnerabilities << Vulnerability.load_from_xml_node(node)
|
||||
end
|
||||
vulnerabilities
|
||||
end
|
||||
end
|
||||
end
|
||||
10
lib/common/models/wp_plugin.rb
Executable file
10
lib/common/models/wp_plugin.rb
Executable file
@@ -0,0 +1,10 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpPlugin < WpItem
|
||||
include WpPlugin::Vulnerable
|
||||
|
||||
def forge_uri(target_base_uri)
|
||||
@uri = target_base_uri.merge(URI.encode(wp_plugins_dir) + '/' + URI.encode(name) + '/')
|
||||
end
|
||||
|
||||
end
|
||||
20
lib/common/models/wp_plugin/vulnerable.rb
Normal file
20
lib/common/models/wp_plugin/vulnerable.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpPlugin < WpItem
|
||||
|
||||
def vulns_file
|
||||
unless @vulns_file
|
||||
@vulns_file = PLUGINS_VULNS_FILE
|
||||
end
|
||||
@vulns_file
|
||||
end
|
||||
|
||||
def vulns_xpath
|
||||
"//plugin[@name='#{@name}']/vulnerability"
|
||||
end
|
||||
|
||||
module Vulnerable
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
26
lib/common/models/wp_theme.rb
Executable file
26
lib/common/models/wp_theme.rb
Executable file
@@ -0,0 +1,26 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'wp_theme/findable'
|
||||
require 'wp_theme/versionable'
|
||||
|
||||
class WpTheme < WpItem
|
||||
extend WpTheme::Findable
|
||||
include WpTheme::Versionable
|
||||
include WpTheme::Vulnerable
|
||||
|
||||
attr_writer :style_url
|
||||
|
||||
def allowed_options; super << :style_url end
|
||||
|
||||
def forge_uri(target_base_uri)
|
||||
@uri = target_base_uri.merge(URI.encode(wp_content_dir + '/themes/' + name + '/')) # make suer that this last / is present (spec)
|
||||
end
|
||||
|
||||
def style_url
|
||||
unless @style_url
|
||||
@style_url = uri.merge('style.css').to_s
|
||||
end
|
||||
@style_url
|
||||
end
|
||||
|
||||
end
|
||||
60
lib/common/models/wp_theme/findable.rb
Executable file
60
lib/common/models/wp_theme/findable.rb
Executable file
@@ -0,0 +1,60 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTheme < WpItem
|
||||
module Findable
|
||||
|
||||
# Find the main theme of the blog
|
||||
# returns a WpTheme object or nil
|
||||
def find(target_uri)
|
||||
methods.grep(/find_from_/).each do |method|
|
||||
if wp_theme = self.send(method, target_uri)
|
||||
wp_theme.found_from = method
|
||||
|
||||
return wp_theme
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
# Discover the wordpress theme name by parsing the css link rel
|
||||
def find_from_css_link(target_uri)
|
||||
response = Browser.instance.get_and_follow_location(target_uri.to_s)
|
||||
|
||||
# https + domain is optional because of relative links
|
||||
matches = %r{(?:https?://[^"']+)?/([^/]+)/themes/([^"']+)/style.css}i.match(response.body)
|
||||
if matches
|
||||
return new(
|
||||
target_uri,
|
||||
{
|
||||
name: matches[2],
|
||||
style_url: matches[0],
|
||||
wp_content_dir: matches[1]
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# http://code.google.com/p/wpscan/issues/detail?id=141
|
||||
def find_from_wooframework(target_uri)
|
||||
body = Browser.instance.get(target_uri.to_s).body
|
||||
regexp = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?" />\s+<meta name="generator" content="WooFramework\s?([^"]+)?" />}
|
||||
|
||||
matches = regexp.match(body)
|
||||
if matches
|
||||
woo_theme_name = matches[1]
|
||||
woo_theme_version = matches[2]
|
||||
woo_framework_version = matches[3] # Not used at this time
|
||||
|
||||
return new(
|
||||
target_uri,
|
||||
{
|
||||
name: woo_theme_name,
|
||||
version: woo_theme_version
|
||||
#path: woo_theme_name
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
19
lib/common/models/wp_theme/versionable.rb
Executable file
19
lib/common/models/wp_theme/versionable.rb
Executable file
@@ -0,0 +1,19 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTheme < WpItem
|
||||
module Versionable
|
||||
|
||||
def version
|
||||
unless @version
|
||||
@version = Browser.instance.get(style_url).body[%r{Version:\s([^\s]+)}i, 1]
|
||||
|
||||
# Get Version from readme.txt
|
||||
unless @version
|
||||
@version = super
|
||||
end
|
||||
end
|
||||
@version
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
20
lib/common/models/wp_theme/vulnerable.rb
Normal file
20
lib/common/models/wp_theme/vulnerable.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTheme < WpItem
|
||||
|
||||
def vulns_file
|
||||
unless @vulns_file
|
||||
@vulns_file = THEMES_VULNS_FILE
|
||||
end
|
||||
@vulns_file
|
||||
end
|
||||
|
||||
def vulns_xpath
|
||||
"//theme[@name='#{@name}']/vulnerability"
|
||||
end
|
||||
|
||||
module Vulnerable
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
12
lib/common/models/wp_timthumb.rb
Executable file
12
lib/common/models/wp_timthumb.rb
Executable file
@@ -0,0 +1,12 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'wp_timthumb/versionable'
|
||||
require 'wp_timthumb/existable'
|
||||
require 'wp_timthumb/output'
|
||||
|
||||
class WpTimthumb < WpItem
|
||||
include WpTimthumb::Versionable
|
||||
include WpTimthumb::Existable
|
||||
include WpTimthumb::Output
|
||||
|
||||
end
|
||||
11
lib/common/models/wp_timthumb/existable.rb
Normal file
11
lib/common/models/wp_timthumb/existable.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTimthumb < WpItem
|
||||
module Existable
|
||||
|
||||
def exists_from_response?(response, options = {})
|
||||
response.code == 400 && response.body =~ /no image specified/i ? true : false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
11
lib/common/models/wp_timthumb/output.rb
Normal file
11
lib/common/models/wp_timthumb/output.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTimthumb < WpItem
|
||||
module Output
|
||||
|
||||
def output
|
||||
puts ' | ' + red('[!]') + " #{url}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
13
lib/common/models/wp_timthumb/versionable.rb
Executable file
13
lib/common/models/wp_timthumb/versionable.rb
Executable file
@@ -0,0 +1,13 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpTimthumb < WpItem
|
||||
module Versionable
|
||||
|
||||
# Get the version from the body of an invalid request
|
||||
# See https://code.google.com/p/timthumb/source/browse/trunk/timthumb.php#426
|
||||
def version
|
||||
response = Browser.instance.get(url)
|
||||
response.body[%r{TimThumb version\s*: ([^<]+)} , 1]
|
||||
end
|
||||
end
|
||||
end
|
||||
33
lib/common/models/wp_user.rb
Executable file
33
lib/common/models/wp_user.rb
Executable file
@@ -0,0 +1,33 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'wp_user/existable'
|
||||
|
||||
class WpUser < WpItem
|
||||
|
||||
include WpUser::Existable
|
||||
|
||||
attr_accessor :id, :login, :display_name, :password
|
||||
|
||||
def allowed_options; [:id, :login, :display_name, :password] end
|
||||
|
||||
def uri
|
||||
if id
|
||||
return @uri.merge("?author=#{id}")
|
||||
else
|
||||
raise 'The id is nil'
|
||||
end
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
id <=> other.id
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
self === (other)
|
||||
end
|
||||
|
||||
def ===(other)
|
||||
id === other.id && login === other.login
|
||||
end
|
||||
|
||||
end
|
||||
51
lib/common/models/wp_user/existable.rb
Executable file
51
lib/common/models/wp_user/existable.rb
Executable file
@@ -0,0 +1,51 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpUser < WpItem
|
||||
module Existable
|
||||
|
||||
def exists_from_response?(response, options = {})
|
||||
load_login_from_response(response)
|
||||
|
||||
@login ? true : false
|
||||
end
|
||||
|
||||
def load_login_from_response(response)
|
||||
if response.code == 301 # login in location?
|
||||
location = response.headers_hash['Location']
|
||||
|
||||
@login = WpUser::Existable.login_from_author_pattern(location)
|
||||
@display_name = WpUser::Existable.display_name_from_body(
|
||||
Browser.instance.get(location).body
|
||||
)
|
||||
elsif response.code == 200 # login in body?
|
||||
@login = WpUser::Existable.login_from_body(response.body)
|
||||
@display_name = WpUser::Existable.display_name_from_body(response.body)
|
||||
end
|
||||
end
|
||||
|
||||
def self.login_from_author_pattern(text)
|
||||
text[%r{/author/([^/\b]+)/?}i, 1]
|
||||
end
|
||||
|
||||
def self.login_from_body(body)
|
||||
# Feed URL with Permalinks
|
||||
login = WpUser::Existable.login_from_author_pattern(body)
|
||||
|
||||
unless login
|
||||
# No Permalinks
|
||||
login = body[%r{<body class="archive author author-([^\s]+) author-(\d+)}i, 1]
|
||||
end
|
||||
|
||||
login
|
||||
end
|
||||
|
||||
def self.display_name_from_body(body)
|
||||
if title_tag = body[%r{<title>([^<]+)</title>}i, 1]
|
||||
title_tag.sub!('|', '|')
|
||||
|
||||
return title_tag[%r{([^|]+) }, 1]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
32
lib/common/models/wp_version.rb
Executable file
32
lib/common/models/wp_version.rb
Executable file
@@ -0,0 +1,32 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
require 'wp_version/findable'
|
||||
require 'wp_version/vulnerable'
|
||||
require 'wp_version/output'
|
||||
|
||||
class WpVersion < WpItem
|
||||
|
||||
extend WpVersion::Findable
|
||||
include WpVersion::Vulnerable
|
||||
include WpVersion::Output
|
||||
|
||||
@@version_xml =
|
||||
|
||||
# The version number
|
||||
attr_accessor :number
|
||||
|
||||
def allowed_options; super << :number << :found_from end
|
||||
|
||||
def self.version_xml
|
||||
@@version_xml
|
||||
end
|
||||
|
||||
def self.version_xml=(xml)
|
||||
if File.exists?(xml)
|
||||
@@version_xml = xml
|
||||
else
|
||||
raise "The file #{xml} does not exist"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
162
lib/common/models/wp_version/findable.rb
Executable file
162
lib/common/models/wp_version/findable.rb
Executable file
@@ -0,0 +1,162 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpVersion < WpItem
|
||||
module Findable
|
||||
|
||||
# Find the version of the wp_target blog
|
||||
# returns a WpVersion object or nil
|
||||
def find(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
methods.grep(/find_from_/).each do |method|
|
||||
if version = send(method, target_uri, wp_content_dir, wp_plugins_dir)
|
||||
|
||||
return new(target_uri, number: version, found_from: method)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the first match of <pattern> in the body of the url
|
||||
def scan_url(target_uri, pattern, path = nil)
|
||||
url = path ? target_uri.merge(path).to_s : target_uri.to_s
|
||||
response = Browser.instance.get_and_follow_location(url)
|
||||
|
||||
response.body[pattern, 1]
|
||||
end
|
||||
|
||||
#
|
||||
# DO NOT Change the order of the following methods
|
||||
# unless you know what you are doing
|
||||
# See WpVersion.find
|
||||
#
|
||||
|
||||
# Attempts to find the wordpress version from,
|
||||
# the generator meta tag in the html source.
|
||||
#
|
||||
# The meta tag can be removed however it seems,
|
||||
# that it is reinstated on upgrade.
|
||||
def find_from_meta_generator(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
scan_url(
|
||||
target_uri,
|
||||
%r{name="generator" content="wordpress #{version_pattern}"}i
|
||||
)
|
||||
end
|
||||
|
||||
# Attempts to find the WordPress version from,
|
||||
# the generator tag in the RSS feed source.
|
||||
def find_from_rss_generator(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
scan_url(
|
||||
target_uri,
|
||||
%r{<generator>http://wordpress.org/\?v=#{version_pattern}</generator>}i,
|
||||
'feed/'
|
||||
)
|
||||
end
|
||||
|
||||
# Attempts to find WordPress version from,
|
||||
# the generator tag in the RDF feed source.
|
||||
def find_from_rdf_generator(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
scan_url(
|
||||
target_uri,
|
||||
%r{<admin:generatorAgent rdf:resource="http://wordpress.org/\?v=#{version_pattern}" />}i,
|
||||
'feed/rdf/'
|
||||
)
|
||||
end
|
||||
|
||||
# Attempts to find the WordPress version from,
|
||||
# the generator tag in the RSS2 feed source.
|
||||
#
|
||||
# Have not been able to find an example of this - Ryan
|
||||
#def find_from_rss2_generator(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
# scan_url(
|
||||
# target_uri,
|
||||
# %r{<generator>http://wordpress.org/?v=(#{WpVersion.version_pattern})</generator>}i,
|
||||
# 'feed/rss/'
|
||||
# )
|
||||
#end
|
||||
|
||||
# Attempts to find the WordPress version from,
|
||||
# the generator tag in the Atom source.
|
||||
def find_from_atom_generator(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
scan_url(
|
||||
target_uri,
|
||||
%r{<generator uri="http://wordpress.org/" version="#{version_pattern}">WordPress</generator>}i,
|
||||
'feed/atom/'
|
||||
)
|
||||
end
|
||||
|
||||
# Attempts to find the WordPress version from,
|
||||
# the generator tag in the comment rss source.
|
||||
#
|
||||
# Have not been able to find an example of this - Ryan
|
||||
#def find_from_comments_rss_generator(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
# scan_url(
|
||||
# target_uri,
|
||||
# %r{<!-- generator="WordPress/#{WpVersion.version_pattern}" -->}i,
|
||||
# 'comments/feed/'
|
||||
# )
|
||||
#end
|
||||
|
||||
# Uses data/wp_versions.xml to try to identify a
|
||||
# wordpress version.
|
||||
#
|
||||
# It does this by using client side file hashing
|
||||
#
|
||||
# /!\ Warning : this method might return false positive if the file used for fingerprinting is part of a theme (they can be updated)
|
||||
#
|
||||
def find_from_advanced_fingerprinting(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
xml = xml(version_xml)
|
||||
# This wp_item will take care of encoding the path
|
||||
# and replace variables like $wp-content$ and $wp-plugins$
|
||||
wp_item = WpItem.new(target_uri,
|
||||
wp_content_dir: wp_content_dir,
|
||||
wp_plugins_dir: wp_plugins_dir)
|
||||
|
||||
xml.xpath('//file').each do |node|
|
||||
wp_item.path = node.attribute('src').text
|
||||
|
||||
response = Browser.instance.get(wp_item.url)
|
||||
md5sum = Digest::MD5.hexdigest(response.body)
|
||||
|
||||
node.search('hash').each do |hash|
|
||||
if hash.attribute('md5').text == md5sum
|
||||
return hash.search('version').text
|
||||
end
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Attempts to find the WordPress version from the readme.html file.
|
||||
def find_from_readme(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
scan_url(
|
||||
target_uri,
|
||||
%r{<br />\sversion #{version_pattern}}i,
|
||||
'readme.html'
|
||||
)
|
||||
end
|
||||
|
||||
# Attempts to find the WordPress version from the sitemap.xml file.
|
||||
#
|
||||
# See: http://code.google.com/p/wpscan/issues/detail?id=109
|
||||
def find_from_sitemap_generator(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
scan_url(
|
||||
target_uri,
|
||||
%r{generator="wordpress/#{version_pattern}"}i,
|
||||
'sitemap.xml'
|
||||
)
|
||||
end
|
||||
|
||||
# Attempts to find the WordPress version from the p-links-opml.php file.
|
||||
def find_from_links_opml(target_uri, wp_content_dir, wp_plugins_dir)
|
||||
scan_url(
|
||||
target_uri,
|
||||
%r{generator="wordpress/#{version_pattern}"}i,
|
||||
'wp-links-opml.php'
|
||||
)
|
||||
end
|
||||
|
||||
# Used to check if the version is correct: must contain at least one dot.
|
||||
def version_pattern
|
||||
'([^\r\n"\']+\.[^\r\n"\']+)'
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
20
lib/common/models/wp_version/output.rb
Normal file
20
lib/common/models/wp_version/output.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpVersion < WpItem
|
||||
module Output
|
||||
|
||||
def output
|
||||
puts green('[+]') + " WordPress version #{self.number} identified from #{self.found_from}"
|
||||
|
||||
vulnerabilities = self.vulnerabilities
|
||||
|
||||
unless vulnerabilities.empty?
|
||||
puts
|
||||
puts red('[!]') + " We have identified #{vulnerabilities.size} vulnerabilities from the version number :"
|
||||
|
||||
vulnerabilities.output
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
19
lib/common/models/wp_version/vulnerable.rb
Normal file
19
lib/common/models/wp_version/vulnerable.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
# encoding: UTF-8
|
||||
|
||||
class WpVersion < WpItem
|
||||
|
||||
def vulns_file
|
||||
unless @vulns_file
|
||||
@vulns_file = WP_VULNS_FILE
|
||||
end
|
||||
@vulns_file
|
||||
end
|
||||
|
||||
def vulns_xpath
|
||||
"//wordpress[@version='#{@number}']/vulnerability"
|
||||
end
|
||||
|
||||
module Vulnerable
|
||||
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user