Merge pull request #862 from wpscanteam/apiv2

Apiv2
This commit is contained in:
Ryan Dewhurst
2015-09-08 21:00:03 +02:00
41 changed files with 681 additions and 607 deletions

View File

@@ -67,6 +67,7 @@ class WpItems < Array
end
protected
# @return [ Class ]
def item_class
Object.const_get(self.class.to_s.gsub(/.$/, ''))

View File

@@ -32,11 +32,7 @@ class WpItems < Array
progress_bar.progress += 1 if options[:show_progression]
if target_item.exists?(exist_options, response)
unless results.include?(target_item)
if !options[:only_vulnerable] || options[:only_vulnerable] && target_item.vulnerable?
results << target_item
end
end
results << target_item unless results.include?(target_item)
end
end
@@ -53,7 +49,7 @@ class WpItems < Array
# run the remaining requests
hydra.run
results.select!(&:vulnerable?) if options[:only_vulnerable]
results.select!(&:vulnerable?) if options[:type] == :vulnerable
results.sort!
results # can't just return results.sort as it would return an array, and we want a WpItems
@@ -155,15 +151,7 @@ class WpItems < Array
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 = target_items_from_type(wp_target, item_class, vulns_file, options[:type])
targets.uniq! { |t| t.name }
targets.sort_by { rand }
@@ -174,14 +162,25 @@ class WpItems < Array
# @param [ String ] vulns_file
#
# @return [ Array<WpItem> ]
def vulnerable_targets_items(wp_target, item_class, vulns_file)
def target_items_from_type(wp_target, item_class, vulns_file, type)
targets = []
json = json(vulns_file)
[*json].each do |item|
case type
when :vulnerable
items = json.select { |item| !json[item]['vulnerabilities'].empty? }.keys
when :popular
items = json.select { |item| json[item]['popular'] == true }.keys
when :all
items = json.keys
else
raise "Unknown type #{type}"
end
items.each do |item|
targets << create_item(
item_class,
item.keys.inject,
item,
wp_target,
vulns_file
)
@@ -233,6 +232,5 @@ class WpItems < Array
def item_class
Object.const_get(self.to_s.gsub(/.$/, ''))
end
end
end

View File

@@ -2,17 +2,11 @@
class WpPlugins < WpItems
module Detectable
# @return [ String ]
def vulns_file
PLUGINS_VULNS_FILE
PLUGINS_FILE
end
# @return [ String ]
# def item_xpath
# '//plugin'
# end
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
#

View File

@@ -5,13 +5,7 @@ class WpThemes < WpItems
# @return [ String ]
def vulns_file
THEMES_VULNS_FILE
THEMES_FILE
end
# @return [ String ]
# def item_xpath
# '//theme'
# end
end
end

View File

@@ -18,20 +18,15 @@ COMMON_PLUGINS_DIR = File.join(COMMON_LIB_DIR, 'plugins')
WPSCAN_PLUGINS_DIR = File.join(WPSCAN_LIB_DIR, 'plugins') # Not used ATM
# Data files
PLUGINS_FILE = File.join(DATA_DIR, 'plugins.txt')
PLUGINS_FULL_FILE = File.join(DATA_DIR, 'plugins_full.txt')
PLUGINS_VULNS_FILE = File.join(DATA_DIR, 'plugin_vulns.json')
THEMES_FILE = File.join(DATA_DIR, 'themes.txt')
THEMES_FULL_FILE = File.join(DATA_DIR, 'themes_full.txt')
THEMES_VULNS_FILE = File.join(DATA_DIR, 'theme_vulns.json')
WP_VULNS_FILE = File.join(DATA_DIR, 'wp_vulns.json')
WP_VERSIONS_FILE = File.join(DATA_DIR, 'wp_versions.xml')
LOCAL_FILES_FILE = File.join(DATA_DIR, 'local_vulnerable_files.xml')
# VULNS_XSD = File.join(DATA_DIR, 'vuln.xsd')
WP_VERSIONS_XSD = File.join(DATA_DIR, 'wp_versions.xsd')
LOCAL_FILES_XSD = File.join(DATA_DIR, 'local_vulnerable_files.xsd')
USER_AGENTS_FILE = File.join(DATA_DIR, 'user-agents.txt')
LAST_UPDATE_FILE = File.join(DATA_DIR, '.last_update')
WORDPRESSES_FILE = File.join(DATA_DIR, 'wordpresses.json')
PLUGINS_FILE = File.join(DATA_DIR, 'plugins.json')
THEMES_FILE = File.join(DATA_DIR, 'themes.json')
WP_VERSIONS_FILE = File.join(DATA_DIR, 'wp_versions.xml')
LOCAL_FILES_FILE = File.join(DATA_DIR, 'local_vulnerable_files.xml')
WP_VERSIONS_XSD = File.join(DATA_DIR, 'wp_versions.xsd')
LOCAL_FILES_XSD = File.join(DATA_DIR, 'local_vulnerable_files.xsd')
USER_AGENTS_FILE = File.join(DATA_DIR, 'user-agents.txt')
LAST_UPDATE_FILE = File.join(DATA_DIR, '.last_update')
WPSCAN_VERSION = '2.8'

View File

@@ -4,9 +4,8 @@
class DbUpdater
FILES = %w(
local_vulnerable_files.xml local_vulnerable_files.xsd
plugins_full.txt plugins.txt themes_full.txt themes.txt
timthumbs.txt user-agents.txt wp_versions.xml wp_versions.xsd
plugin_vulns.json theme_vulns.json wp_vulns.json LICENSE
wordpresses.json plugins.json themes.json LICENSE
)
attr_reader :repo_directory

View File

@@ -42,11 +42,12 @@ class Vulnerability
# @return [ Vulnerability ]
def self.load_from_json_item(json_item)
references = {}
references['id'] = [json_item['id']]
%w(id url cve secunia osvdb metasploit exploitdb).each do |key|
if json_item[key]
json_item[key] = [json_item[key]] if json_item[key].class != Array
references[key] = json_item[key]
%w(url cve secunia osvdb metasploit exploitdb).each do |key|
if json_item['references'][key]
json_item['references'][key] = [json_item['references'][key]] if json_item['references'][key].class != Array
references[key] = json_item['references'][key]
end
end
@@ -54,7 +55,7 @@ class Vulnerability
json_item['title'],
json_item['type'],
references,
json_item['fixed_in'],
json_item['fixed_in']
)
end

View File

@@ -2,22 +2,22 @@
class Vulnerability
module Output
# output the vulnerability
def output(verbose = false)
puts
puts critical("Title: #{title}")
references.each do |key, urls|
methodname = "url_#{key}"
urls.each do |u|
next unless respond_to?(methodname)
url = send(methodname, u)
puts " Reference: #{url}" if url
end
end
unless fixed_in.nil?
puts notice("Fixed in: #{fixed_in}")
end
puts notice("Fixed in: #{fixed_in}") if fixed_in
end
end
end

View File

@@ -22,7 +22,7 @@ class WpItem
# @return [ Array ]
# Make it private ?
def allowed_options
[:name, :wp_content_dir, :wp_plugins_dir, :path, :version, :vulns_file]
[:name, :wp_content_dir, :wp_plugins_dir, :path, :version, :db_file]
end
# @param [ URI ] target_base_uri
@@ -30,7 +30,6 @@ class WpItem
#
# @return [ WpItem ]
def initialize(target_base_uri, options = {})
options[:wp_content_dir] ||= 'wp-content'
options[:wp_plugins_dir] ||= options[:wp_content_dir] + '/plugins'
@@ -38,6 +37,27 @@ class WpItem
forge_uri(target_base_uri)
end
def identifier
@identifier ||= name
end
# @return [ Hash ]
def db_data
@db_data ||= json(db_file)[identifier] || {}
end
def latest_version
db_data['latest_version']
end
def last_updated
db_data['last_ipdated']
end
def popular?
db_data['popular']
end
# @param [ Hash ] options
#
# @return [ void ]

View File

@@ -5,12 +5,17 @@ class WpItem
# @return [ Void ]
def output(verbose = false)
outdated = VersionCompare.lesser?(version, latest_version) if latest_version
puts
puts info("Name: #{self}") #this will also output the version number if detected
puts " | Latest version: #{latest_version} (up to date)" if latest_version && !outdated
puts " | Last updated: #{last_updated}" if last_updated
puts " | Location: #{url}"
#puts " | WordPress: #{wordpress_url}" if wordpress_org_item?
puts " | Readme: #{readme_url}" if has_readme?
puts " | Changelog: #{changelog_url}" if has_changelog?
puts warning("The version is out of date, the latest version is #{latest_version}") if latest_version && outdated
puts warning("Directory listing is enabled: #{url}") if has_directory_listing?
puts warning("An error_log file has been found: #{error_log_url}") if has_error_log?

View File

@@ -2,30 +2,23 @@
class WpItem
module Vulnerable
attr_accessor :vulns_file, :identifier
attr_accessor :db_file, :identifier
# Get the vulnerabilities associated to the WpItem
# Filters out already fixed vulnerabilities
#
# @return [ Vulnerabilities ]
def vulnerabilities
json = json(vulns_file)
vulnerabilities = Vulnerabilities.new
return @vulnerabilities if @vulnerabilities
json.each do |item|
asset = item[identifier]
@vulnerabilities = Vulnerabilities.new
next unless asset
asset['vulnerabilities'].each do |vulnerability|
vulnerability = Vulnerability.load_from_json_item(vulnerability)
vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
end
break # No need to iterate any further
[*db_data['vulnerabilities']].each do |vulnerability|
vulnerability = Vulnerability.load_from_json_item(vulnerability)
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
end
vulnerabilities
@vulnerabilities
end
def vulnerable?

View File

@@ -1,10 +1,6 @@
# encoding: UTF-8
require 'wp_plugin/vulnerable'
class WpPlugin < WpItem
include WpPlugin::Vulnerable
# Sets the @uri
#
# @param [ URI ] target_base_uri The URI of the wordpress blog
@@ -14,4 +10,7 @@ class WpPlugin < WpItem
@uri = target_base_uri.merge(URI.encode(wp_plugins_dir + '/' + name + '/'))
end
def db_file
@db_file ||= PLUGINS_FILE
end
end

View File

@@ -1,20 +0,0 @@
# encoding: UTF-8
class WpPlugin < WpItem
module Vulnerable
# @return [ String ] The path to the file containing vulnerabilities
def vulns_file
unless @vulns_file
@vulns_file = PLUGINS_VULNS_FILE
end
@vulns_file
end
# @return [ String ]
def identifier
@name
end
end
end

View File

@@ -2,7 +2,6 @@
require 'wp_theme/findable'
require 'wp_theme/versionable'
require 'wp_theme/vulnerable'
require 'wp_theme/info'
require 'wp_theme/output'
require 'wp_theme/childtheme'
@@ -10,7 +9,6 @@ require 'wp_theme/childtheme'
class WpTheme < WpItem
extend WpTheme::Findable
include WpTheme::Versionable
include WpTheme::Vulnerable
include WpTheme::Info
include WpTheme::Output
include WpTheme::Childtheme
@@ -33,4 +31,7 @@ class WpTheme < WpItem
@uri.merge('style.css').to_s
end
def db_file
@db_file ||= THEMES_FILE
end
end

View File

@@ -1,19 +0,0 @@
# encoding: UTF-8
class WpTheme < WpItem
module Vulnerable
# @return [ String ] The path to the file containing vulnerabilities
def vulns_file
unless @vulns_file
@vulns_file = THEMES_VULNS_FILE
end
@vulns_file
end
# @return [ String ]
def identifier
@name
end
end
end

View File

@@ -1,13 +1,10 @@
# 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
# The version number
@@ -17,6 +14,14 @@ class WpVersion < WpItem
# @return [ Array ]
def allowed_options; super << :number << :found_from end
def identifier
@identifier ||= number
end
def db_file
@db_file ||= WORDPRESSES_FILE
end
# @param [ WpVersion ] other
#
# @return [ Boolean ]

View File

@@ -1,19 +0,0 @@
# encoding: UTF-8
class WpVersion < WpItem
module Vulnerable
# @return [ String ] The path to the file containing vulnerabilities
def vulns_file
unless @vulns_file
@vulns_file = WP_VULNS_FILE
end
@vulns_file
end
# @return [ String ]
def identifier
@number
end
end
end

View File

@@ -11,8 +11,8 @@ class VersionCompare
# @return [ Boolean ]
def self.lesser_or_equal?(version1, version2)
# Prepend a '0' if the version starts with a '.'
version1 = "0#{version1}" if version1 && version1[0,1] == '.'
version2 = "0#{version2}" if version2 && version2[0,1] == '.'
version1 = prepend_zero(version1)
version2 = prepend_zero(version2)
return true if (version1 == version2)
# Both versions must be set
@@ -27,4 +27,36 @@ class VersionCompare
end
return false
end
# Compares two version strings. Returns true if version1 < version2
# and false otherwise
#
# @param [ String ] version1
# @param [ String ] version2
#
# @return [ Boolean ]
def self.lesser?(version1, version2)
# Prepend a '0' if the version starts with a '.'
version1 = prepend_zero(version1)
version2 = prepend_zero(version2)
return false if (version1 == version2)
# Both versions must be set
return false unless (version1 and version2)
return false if (version1.empty? or version2.empty?)
begin
return true if (Gem::Version.new(version1) < Gem::Version.new(version2))
rescue ArgumentError => e
# Example: ArgumentError: Malformed version number string a
return false if e.message =~ /Malformed version number string/
raise
end
return false
end
# @return [ String ]
def self.prepend_zero(version)
return nil if version.nil?
version[0,1] == '.' ? "0#{version}" : version
end
end

View File

@@ -31,7 +31,7 @@ begin
require 'pathname'
# Third party libs
require 'typhoeus'
require 'json'
require 'yajl/json_gem'
require 'nokogiri'
require 'terminal-table'
require 'ruby-progressbar'