drop ruby 1.9 support, whitespaces

This commit is contained in:
Christian Mehlmauer
2016-02-23 18:07:20 +01:00
parent a78a13bf3f
commit 816b18b604
29 changed files with 675 additions and 717 deletions

View File

@@ -2,8 +2,6 @@ language: ruby
sudo: false
cache: bundler
rvm:
- 1.9.2
- 1.9.3
- 2.0.0
- 2.1.0
- 2.1.1
@@ -23,9 +21,6 @@ script: bundle exec rspec
notifications:
email:
- team@wpscan.org
matrix:
allow_failures:
- rvm: 1.9.2
# do not build gh-pages branch
branches:
except:

View File

@@ -92,7 +92,7 @@ WPScan comes pre-installed on the following Linux distributions:
Prerequisites:
- Ruby >= 1.9.2 - Recommended: 2.3.0
- Ruby >= 2.0.0 - Recommended: 2.3.0
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
- RubyGems - Recommended: latest
- Git
@@ -156,8 +156,8 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc
rvm install 2.2.4
rvm use 2.2.4 --default
rvm install 2.3.0
rvm use 2.3.0 --default
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler
git clone https://github.com/wpscanteam/wpscan.git
@@ -192,7 +192,7 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
Then, open the directory of the readline gem (you have to locate it)
cd ~/.rvm/src/ruby-1.9.2-p180/ext/readline
cd ~/.rvm/src/ruby-XXXX/ext/readline
ruby extconf.rb
make
make install

View File

@@ -23,9 +23,7 @@ class CacheFileStore
@storage_path = File.expand_path(File.join(storage_path, storage_dir))
@serializer = serializer
# File.directory? for ruby <= 1.9 otherwise,
# it makes more sense to do Dir.exist? :/
unless File.directory?(@storage_path)
unless Dir.exist?(@storage_path)
FileUtils.mkdir_p(@storage_path)
end
end

View File

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

View File

@@ -1,8 +1,8 @@
# encoding: UTF-8
require 'common/collections/wp_plugins/detectable'
class WpPlugins < WpItems
extend WpPlugins::Detectable
end
# encoding: UTF-8
require 'common/collections/wp_plugins/detectable'
class WpPlugins < WpItems
extend WpPlugins::Detectable
end

View File

@@ -1,8 +1,8 @@
# encoding: UTF-8
require 'common/collections/wp_themes/detectable'
class WpThemes < WpItems
extend WpThemes::Detectable
end
# encoding: UTF-8
require 'common/collections/wp_themes/detectable'
class WpThemes < WpItems
extend WpThemes::Detectable
end

View File

@@ -1,8 +1,8 @@
# encoding: UTF-8
require 'common/collections/wp_timthumbs/detectable'
class WpTimthumbs < WpItems
extend WpTimthumbs::Detectable
end
# encoding: UTF-8
require 'common/collections/wp_timthumbs/detectable'
class WpTimthumbs < WpItems
extend WpTimthumbs::Detectable
end

View File

@@ -1,11 +1,11 @@
# encoding: UTF-8
require 'common/collections/wp_users/detectable'
require 'common/collections/wp_users/output'
require 'common/collections/wp_users/brute_forcable'
class WpUsers < WpItems
extend WpUsers::Detectable
include WpUsers::Output
include WpUsers::BruteForcable
end
# encoding: UTF-8
require 'common/collections/wp_users/detectable'
require 'common/collections/wp_users/output'
require 'common/collections/wp_users/brute_forcable'
class WpUsers < WpItems
extend WpUsers::Detectable
include WpUsers::Output
include WpUsers::BruteForcable
end

View File

@@ -1,34 +1,34 @@
# encoding: UTF-8
class WpUsers < WpItems
module Detectable
# @return [ Hash ]
def request_params; {} end
# No passive detection
#
# @return [ WpUsers ]
def passive_detection(wp_target, options = {})
new
end
protected
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
# @option options [ Range ] :range ((1..10))
#
# @return [ Array<WpUser> ]
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
end
end
# encoding: UTF-8
class WpUsers < WpItems
module Detectable
# @return [ Hash ]
def request_params; {} end
# No passive detection
#
# @return [ WpUsers ]
def passive_detection(wp_target, options = {})
new
end
protected
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
# @option options [ Range ] :range ((1..10))
#
# @return [ Array<WpUser> ]
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
end
end

View File

@@ -266,3 +266,7 @@ end
def directory_listing_enabled?(url)
Browser.get(url.to_s).body[%r{<title>Index of}] ? true : false
end
def url_encode(str)
CGI.escape(str).gsub("+", "%20")
end

View File

@@ -1,35 +1,5 @@
# encoding: UTF-8
# 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'
module URI
extend self
def escape(str)
URI::Parser.new.escape(str)
end
alias :encode :escape
end
end
if RUBY_VERSION < '1.9'
class Array
# Fix for grep with symbols in ruby <= 1.8.7
def _grep_(regexp)
matches = []
self.each do |value|
value = value.to_s
matches << value if value.match(regexp)
end
matches
end
alias_method :grep, :_grep_
end
end
# This is used in WpItem::Existable
module Typhoeus
class Response

View File

@@ -1,62 +1,62 @@
# encoding: UTF-8
require 'vulnerability/output'
require 'vulnerability/urls'
class Vulnerability
include Vulnerability::Output
include Vulnerability::Urls
attr_accessor :title, :references, :type, :fixed_in
#
# @param [ String ] title The title of the vulnerability
# @param [ String ] type The type of the vulnerability
# @param [ Hash ] references References
# @param [ String ] fixed_in Vuln fixed in Version X
#
# @return [ Vulnerability ]
def initialize(title, type, references = {}, fixed_in = '')
@title = title
@type = type
@references = references
@fixed_in = fixed_in
end
# @param [ Vulnerability ] other
#
# @return [ Boolean ]
# :nocov:
def ==(other)
title == other.title &&
type == other.type &&
references == other.references &&
fixed_in == other.fixed_in
end
# :nocov:
# Create the Vulnerability from the json_item
#
# @param [ Hash ] json_item
#
# @return [ Vulnerability ]
def self.load_from_json_item(json_item)
references = {}
references['id'] = [json_item['id']]
%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
new(
json_item['title'],
json_item['type'],
references,
json_item['fixed_in']
)
end
end
# encoding: UTF-8
require 'vulnerability/output'
require 'vulnerability/urls'
class Vulnerability
include Vulnerability::Output
include Vulnerability::Urls
attr_accessor :title, :references, :type, :fixed_in
#
# @param [ String ] title The title of the vulnerability
# @param [ String ] type The type of the vulnerability
# @param [ Hash ] references References
# @param [ String ] fixed_in Vuln fixed in Version X
#
# @return [ Vulnerability ]
def initialize(title, type, references = {}, fixed_in = '')
@title = title
@type = type
@references = references
@fixed_in = fixed_in
end
# @param [ Vulnerability ] other
#
# @return [ Boolean ]
# :nocov:
def ==(other)
title == other.title &&
type == other.type &&
references == other.references &&
fixed_in == other.fixed_in
end
# :nocov:
# Create the Vulnerability from the json_item
#
# @param [ Hash ] json_item
#
# @return [ Vulnerability ]
def self.load_from_json_item(json_item)
references = {}
references['id'] = [json_item['id']]
%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
new(
json_item['title'],
json_item['type'],
references,
json_item['fixed_in']
)
end
end

View File

@@ -1,123 +1,121 @@
# 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
# @return [ Array ]
# Make it private ?
def allowed_options
[:name, :wp_content_dir, :wp_plugins_dir, :path, :version, :db_file]
end
# @param [ URI ] target_base_uri
# @param [ Hash ] options See allowed_option
#
# @return [ WpItem ]
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 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 ]
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
# @param [ URI ] target_base_uri
#
# @return [ void ]
def forge_uri(target_base_uri)
@uri = target_base_uri
end
# @return [ URI ] The uri to the WpItem, with the path if present
def uri
path ? @uri.merge(path) : @uri
end
# @return [ String ] The url to the WpItem
def url; uri.to_s end
# Sets the path
#
# Variable, such as $wp-plugins$ and $wp-content$ can be used
# and will be replace by their value
#
# @param [ String ] path
#
# @return [ void ]
def path=(path)
@path = URI.encode(
path.gsub(/\$wp-plugins\$/i, wp_plugins_dir).gsub(/\$wp-content\$/i, wp_content_dir)
)
end
# @param [ WpItem ] other
def <=>(other)
name <=> other.name
end
# @param [ WpItem ] other
def ==(other)
name === other.name
end
# @param [ WpItem ] other
def ===(other)
self == other && version === other.version
end
end
# 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
# @return [ Array ]
# Make it private ?
def allowed_options
[:name, :wp_content_dir, :wp_plugins_dir, :path, :version, :db_file]
end
# @param [ URI ] target_base_uri
# @param [ Hash ] options See allowed_option
#
# @return [ WpItem ]
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 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 ]
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
# @param [ URI ] target_base_uri
#
# @return [ void ]
def forge_uri(target_base_uri)
@uri = target_base_uri
end
# @return [ URI ] The uri to the WpItem, with the path if present
def uri
path ? @uri.merge(path) : @uri
end
# @return [ String ] The url to the WpItem
def url; uri.to_s end
# Sets the path
#
# Variable, such as $wp-plugins$ and $wp-content$ can be used
# and will be replace by their value
#
# @param [ String ] path
#
# @return [ void ]
def path=(path)
@path = path.gsub(/\$wp-plugins\$/i, wp_plugins_dir).gsub(/\$wp-content\$/i, wp_content_dir)
end
# @param [ WpItem ] other
def <=>(other)
name <=> other.name
end
# @param [ WpItem ] other
def ==(other)
name === other.name
end
# @param [ WpItem ] other
def ===(other)
self == other && version === other.version
end
end

View File

@@ -1,50 +1,50 @@
# encoding: UTF-8
class WpItem
module Existable
# Check the existence of the WpItem
# If the response is supplied, it's used for the verification
# Otherwise a new request is done
#
# @param [ Hash ] options See exists_from_response?
# @param [ Typhoeus::Response ] response
#
# @return [ Boolean ]
def exists?(options = {}, response = nil)
unless response
response = Browser.get(url)
end
exists_from_response?(response, options)
end
protected
# @param [ Typhoeus::Response ] response
# @param [ options ] options
#
# @option options [ Hash ] :error_404_hash The hash of the error 404 page
# @option options [ Hash ] :homepage_hash The hash of the homepage
# @option options [ Hash ] :exclude_content A regexp with the pattern to exclude from the body of the response
#
# @return [ Boolean ]
def exists_from_response?(response, options = {})
# 301 included as some items do a self-redirect
# Redirects to the 404 and homepage should be ignored (unless dynamic content is used)
# by the page hashes (error_404_hash & homepage_hash)
if [200, 401, 403, 301].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
# encoding: UTF-8
class WpItem
module Existable
# Check the existence of the WpItem
# If the response is supplied, it's used for the verification
# Otherwise a new request is done
#
# @param [ Hash ] options See exists_from_response?
# @param [ Typhoeus::Response ] response
#
# @return [ Boolean ]
def exists?(options = {}, response = nil)
unless response
response = Browser.get(url)
end
exists_from_response?(response, options)
end
protected
# @param [ Typhoeus::Response ] response
# @param [ options ] options
#
# @option options [ Hash ] :error_404_hash The hash of the error 404 page
# @option options [ Hash ] :homepage_hash The hash of the homepage
# @option options [ Hash ] :exclude_content A regexp with the pattern to exclude from the body of the response
#
# @return [ Boolean ]
def exists_from_response?(response, options = {})
# 301 included as some items do a self-redirect
# Redirects to the 404 and homepage should be ignored (unless dynamic content is used)
# by the page hashes (error_404_hash & homepage_hash)
if [200, 401, 403, 301].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

View File

@@ -1,19 +1,19 @@
# encoding: UTF-8
class WpItem
attr_reader :found_from
# Sets the found_from attribute
#
# @param [ String ] method The method which found the WpItem
#
# @return [ void ]
def found_from=(method)
found = method[%r{find_from_(.*)}, 1]
@found_from = found.gsub('_', ' ') if found
end
module Findable
end
end
# encoding: UTF-8
class WpItem
attr_reader :found_from
# Sets the found_from attribute
#
# @param [ String ] method The method which found the WpItem
#
# @return [ void ]
def found_from=(method)
found = method[%r{find_from_(.*)}, 1]
@found_from = found.gsub('_', ' ') if found
end
module Findable
end
end

View File

@@ -1,44 +1,44 @@
# encoding: UTF-8
class WpItem
module Vulnerable
attr_accessor :db_file, :identifier
# Get the vulnerabilities associated to the WpItem
# Filters out already fixed vulnerabilities
#
# @return [ Vulnerabilities ]
def vulnerabilities
return @vulnerabilities if @vulnerabilities
@vulnerabilities = Vulnerabilities.new
[*db_data['vulnerabilities']].each do |vulnerability|
vulnerability = Vulnerability.load_from_json_item(vulnerability)
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
end
@vulnerabilities
end
def vulnerable?
vulnerabilities.empty? ? false : true
end
# Checks if a item is vulnerable to a specific vulnerability
#
# @param [ Vulnerability ] vuln Vulnerability to check the item against
#
# @return [ Boolean ]
def vulnerable_to?(vuln)
if version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
unless VersionCompare::lesser_or_equal?(vuln.fixed_in, version)
return true
end
else
return true
end
return false
end
end
end
# encoding: UTF-8
class WpItem
module Vulnerable
attr_accessor :db_file, :identifier
# Get the vulnerabilities associated to the WpItem
# Filters out already fixed vulnerabilities
#
# @return [ Vulnerabilities ]
def vulnerabilities
return @vulnerabilities if @vulnerabilities
@vulnerabilities = Vulnerabilities.new
[*db_data['vulnerabilities']].each do |vulnerability|
vulnerability = Vulnerability.load_from_json_item(vulnerability)
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
end
@vulnerabilities
end
def vulnerable?
vulnerabilities.empty? ? false : true
end
# Checks if a item is vulnerable to a specific vulnerability
#
# @param [ Vulnerability ] vuln Vulnerability to check the item against
#
# @return [ Boolean ]
def vulnerable_to?(vuln)
if version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
unless VersionCompare::lesser_or_equal?(vuln.fixed_in, version)
return true
end
else
return true
end
return false
end
end
end

View File

@@ -1,16 +1,16 @@
# encoding: UTF-8
class WpPlugin < WpItem
# Sets the @uri
#
# @param [ URI ] target_base_uri The URI of the wordpress blog
#
# @return [ void ]
def forge_uri(target_base_uri)
@uri = target_base_uri.merge(URI.encode(wp_plugins_dir + '/' + name + '/'))
end
def db_file
@db_file ||= PLUGINS_FILE
end
end
# encoding: UTF-8
class WpPlugin < WpItem
# Sets the @uri
#
# @param [ URI ] target_base_uri The URI of the wordpress blog
#
# @return [ void ]
def forge_uri(target_base_uri)
@uri = target_base_uri.merge("#{wp_plugins_dir}/#{url_encode(name)}/")
end
def db_file
@db_file ||= PLUGINS_FILE
end
end

View File

@@ -1,37 +1,37 @@
# encoding: UTF-8
require 'wp_theme/findable'
require 'wp_theme/versionable'
require 'wp_theme/info'
require 'wp_theme/output'
require 'wp_theme/childtheme'
class WpTheme < WpItem
extend WpTheme::Findable
include WpTheme::Versionable
include WpTheme::Info
include WpTheme::Output
include WpTheme::Childtheme
attr_accessor :referenced_url
def allowed_options; super << :referenced_url end
# Sets the @uri
#
# @param [ URI ] target_base_uri The URI of the wordpress blog
#
# @return [ void ]
def forge_uri(target_base_uri)
@uri = target_base_uri.merge(URI.encode(wp_content_dir + '/themes/' + name + '/'))
end
# @return [ String ] The url to the theme stylesheet
def style_url
@uri.merge('style.css').to_s
end
def db_file
@db_file ||= THEMES_FILE
end
end
# encoding: UTF-8
require 'wp_theme/findable'
require 'wp_theme/versionable'
require 'wp_theme/info'
require 'wp_theme/output'
require 'wp_theme/childtheme'
class WpTheme < WpItem
extend WpTheme::Findable
include WpTheme::Versionable
include WpTheme::Info
include WpTheme::Output
include WpTheme::Childtheme
attr_accessor :referenced_url
def allowed_options; super << :referenced_url end
# Sets the @uri
#
# @param [ URI ] target_base_uri The URI of the wordpress blog
#
# @return [ void ]
def forge_uri(target_base_uri)
@uri = target_base_uri.merge("#{wp_content_dir}/themes/#{url_encode(name)}/")
end
# @return [ String ] The url to the theme stylesheet
def style_url
@uri.merge('style.css').to_s
end
def db_file
@db_file ||= THEMES_FILE
end
end

View File

@@ -1,64 +1,64 @@
# encoding: UTF-8
class WpTheme < WpItem
module Findable
# Find the main theme of the blog
#
# @param [ URI ] target_uri
#
# @return [ WpTheme ]
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
nil
end
protected
# Discover the wordpress theme by parsing the css link rel
#
# @param [ URI ] target_uri
#
# @return [ WpTheme ]
def find_from_css_link(target_uri)
response = Browser.get_and_follow_location(target_uri.to_s)
# https + domain is optional because of relative links
return unless response.body =~ %r{(?:https?://[^"']+/)?([^/\s]+)/themes/([^"'/]+)[^"']*/style.css}i
new(
target_uri,
name: Regexp.last_match[2],
referenced_url: Regexp.last_match[0],
wp_content_dir: Regexp.last_match[1]
)
end
# @param [ URI ] target_uri
#
# @return [ WpTheme ]
def find_from_wooframework(target_uri)
body = Browser.get(target_uri.to_s).body
regexp = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?" />\s+<meta name="generator" content="WooFramework\s?([^"]+)?" />}
if matches = regexp.match(body)
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
)
end
end
end
end
# encoding: UTF-8
class WpTheme < WpItem
module Findable
# Find the main theme of the blog
#
# @param [ URI ] target_uri
#
# @return [ WpTheme ]
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
nil
end
protected
# Discover the wordpress theme by parsing the css link rel
#
# @param [ URI ] target_uri
#
# @return [ WpTheme ]
def find_from_css_link(target_uri)
response = Browser.get_and_follow_location(target_uri.to_s)
# https + domain is optional because of relative links
return unless response.body =~ %r{(?:https?://[^"']+/)?([^/\s]+)/themes/([^"'/]+)[^"']*/style.css}i
new(
target_uri,
name: Regexp.last_match[2],
referenced_url: Regexp.last_match[0],
wp_content_dir: Regexp.last_match[1]
)
end
# @param [ URI ] target_uri
#
# @return [ WpTheme ]
def find_from_wooframework(target_uri)
body = Browser.get(target_uri.to_s).body
regexp = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?" />\s+<meta name="generator" content="WooFramework\s?([^"]+)?" />}
if matches = regexp.match(body)
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
)
end
end
end
end

View File

@@ -1,9 +1,9 @@
# encoding: UTF-8
class WpTheme < WpItem
module Versionable
def version
@version ||= Browser.get(style_url).body[%r{Version:\s*(?!trunk)([0-9a-z\.-]+)}i, 1]
end
end
end
# encoding: UTF-8
class WpTheme < WpItem
module Versionable
def version
@version ||= Browser.get(style_url).body[%r{Version:\s*(?!trunk)([0-9a-z\.-]+)}i, 1]
end
end
end

View File

@@ -1,20 +1,20 @@
# encoding: UTF-8
require 'wp_timthumb/versionable'
require 'wp_timthumb/existable'
require 'wp_timthumb/output'
require 'wp_timthumb/vulnerable'
class WpTimthumb < WpItem
include WpTimthumb::Versionable
include WpTimthumb::Existable
include WpTimthumb::Output
include WpTimthumb::Vulnerable
# @param [ WpTimthumb ] other
#
# @return [ Boolean ]
def ==(other)
url == other.url
end
end
# encoding: UTF-8
require 'wp_timthumb/versionable'
require 'wp_timthumb/existable'
require 'wp_timthumb/output'
require 'wp_timthumb/vulnerable'
class WpTimthumb < WpItem
include WpTimthumb::Versionable
include WpTimthumb::Existable
include WpTimthumb::Output
include WpTimthumb::Vulnerable
# @param [ WpTimthumb ] other
#
# @return [ Boolean ]
def ==(other)
url == other.url
end
end

View File

@@ -1,24 +1,24 @@
# 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
#
# @return [ String ] The version
def version
unless @version
response = Browser.get(url)
@version = response.body[%r{TimThumb version\s*: ([^<]+)} , 1]
end
@version
end
# @return [ String ]
def to_s
"#{url}#{ ' v' + version if version}"
end
end
end
# 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
#
# @return [ String ] The version
def version
unless @version
response = Browser.get(url)
@version = response.body[%r{TimThumb version\s*: ([^<]+)} , 1]
end
@version
end
# @return [ String ]
def to_s
"#{url}#{ ' v' + version if version}"
end
end
end

View File

@@ -1,86 +1,86 @@
# encoding: UTF-8
class WpUser < WpItem
module Existable
# @param [ Typhoeus::Response ] response
# @param [ Hash ] options
#
# @return [ Boolean ]
def exists_from_response?(response, options = {})
load_from_response(response)
@login ? true : false
end
# Load the login and display_name from the response
#
# @param [ Typhoeus::Response ] response
#
# @return [ void ]
def load_from_response(response)
if response.code == 301 # login in location?
location = response.headers_hash['Location']
return if location.nil? || location.empty?
@login = Existable.login_from_author_pattern(location)
@display_name = Existable.display_name_from_body(
Browser.get(location).body
)
elsif response.code == 200 # login in body?
@login = Existable.login_from_body(response.body)
@display_name = Existable.display_name_from_body(response.body)
end
end
private :load_from_response
# @param [ String ] text
#
# @return [ String ] The login
def self.login_from_author_pattern(text)
return unless text =~ %r{/author/([^/\b"']+)/?}i
Regexp.last_match[1].force_encoding('UTF-8')
end
# @param [ String ] body
#
# @return [ String ] The login
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]+)[ "]}i, 1]
login ? login.force_encoding('UTF-8') : nil
end
login
end
# @note Some bodies are encoded in ASCII-8BIT, and Nokogiri doesn't support it
# So it's forced to UTF-8 when this encoding is detected
#
# @param [ String ] body
#
# @return [ String ] The display_name
def self.display_name_from_body(body)
if title_tag = body[%r{<title>([^<]+)</title>}i, 1]
title_tag.force_encoding('UTF-8') if title_tag.encoding == Encoding::ASCII_8BIT
title_tag = Nokogiri::HTML::DocumentFragment.parse(title_tag).to_s
# &amp; are not decoded with Nokogiri
title_tag.gsub!('&amp;', '&')
# replace UTF chars like &#187; with dummy character
title_tag.gsub!(/&#(\d+);/, '|')
name = title_tag[%r{([^|«»]+) }, 1]
return name.strip if name
end
end
end
end
# encoding: UTF-8
class WpUser < WpItem
module Existable
# @param [ Typhoeus::Response ] response
# @param [ Hash ] options
#
# @return [ Boolean ]
def exists_from_response?(response, options = {})
load_from_response(response)
@login ? true : false
end
# Load the login and display_name from the response
#
# @param [ Typhoeus::Response ] response
#
# @return [ void ]
def load_from_response(response)
if response.code == 301 # login in location?
location = response.headers_hash['Location']
return if location.nil? || location.empty?
@login = Existable.login_from_author_pattern(location)
@display_name = Existable.display_name_from_body(
Browser.get(location).body
)
elsif response.code == 200 # login in body?
@login = Existable.login_from_body(response.body)
@display_name = Existable.display_name_from_body(response.body)
end
end
private :load_from_response
# @param [ String ] text
#
# @return [ String ] The login
def self.login_from_author_pattern(text)
return unless text =~ %r{/author/([^/\b"']+)/?}i
Regexp.last_match[1].force_encoding('UTF-8')
end
# @param [ String ] body
#
# @return [ String ] The login
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]+)[ "]}i, 1]
login ? login.force_encoding('UTF-8') : nil
end
login
end
# @note Some bodies are encoded in ASCII-8BIT, and Nokogiri doesn't support it
# So it's forced to UTF-8 when this encoding is detected
#
# @param [ String ] body
#
# @return [ String ] The display_name
def self.display_name_from_body(body)
if title_tag = body[%r{<title>([^<]+)</title>}i, 1]
title_tag.force_encoding('UTF-8') if title_tag.encoding == Encoding::ASCII_8BIT
title_tag = Nokogiri::HTML::DocumentFragment.parse(title_tag).to_s
# &amp; are not decoded with Nokogiri
title_tag.gsub!('&amp;', '&')
# replace UTF chars like &#187; with dummy character
title_tag.gsub!(/&#(\d+);/, '|')
name = title_tag[%r{([^|«»]+) }, 1]
return name.strip if name
end
end
end
end

View File

@@ -1,38 +1,38 @@
# encoding: UTF-8
require 'wp_version/findable'
require 'wp_version/output'
class WpVersion < WpItem
extend WpVersion::Findable
include WpVersion::Output
# The version number
attr_accessor :number
alias_method :version, :number # Needed to have the right behaviour in Vulnerable#vulnerable_to?
# @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 ]
def ==(other)
number == other.number
end
# @return [ Array<String> ] All the stable versions from version_file
def self.all(versions_file = WP_VERSIONS_FILE)
Nokogiri.XML(File.open(versions_file)).css('version').reduce([]) do |a, node|
a << node.text.to_s
end
end
end
# encoding: UTF-8
require 'wp_version/findable'
require 'wp_version/output'
class WpVersion < WpItem
extend WpVersion::Findable
include WpVersion::Output
# The version number
attr_accessor :number
alias_method :version, :number # Needed to have the right behaviour in Vulnerable#vulnerable_to?
# @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 ]
def ==(other)
number == other.number
end
# @return [ Array<String> ] All the stable versions from version_file
def self.all(versions_file = WP_VERSIONS_FILE)
Nokogiri.XML(File.open(versions_file)).css('version').reduce([]) do |a, node|
a << node.text.to_s
end
end
end

View File

@@ -130,8 +130,6 @@ class WpVersion < WpItem
def find_from_advanced_fingerprinting(target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
xml = xml(versions_xml)
# This wp_item will take care of encoding the path
# and replace variables like $wp-content$ & $wp-plugins$
wp_item = WpItem.new(target_uri,
wp_content_dir: wp_content_dir,
wp_plugins_dir: wp_plugins_dir)

View File

@@ -3,8 +3,8 @@
require 'rubygems'
version = RUBY_VERSION.dup
if Gem::Version.create(version) < Gem::Version.create(1.9)
puts "Ruby >= 1.9 required to run wpscan (You have #{version})"
if Gem::Version.create(version) < Gem::Version.create(2.0)
puts "Ruby >= 2.0.0 required to run wpscan (You have #{version})"
exit(1)
end

View File

@@ -14,7 +14,7 @@ class WpTarget < WebSite
queue_count = 0
backups.each do |file|
file_url = @uri.merge(URI.escape(file)).to_s
file_url = @uri.merge(url_encode(file)).to_s
request = browser.forge_request(file_url)
request.on_complete do |response|

View File

@@ -105,11 +105,6 @@ describe WpItem do
@expected = 'plugins/readme.txt'
end
end
it 'also encodes chars' do
@path = 'some dir with spaces'
@expected = 'some%20dir%20with%20spaces'
end
end
describe '#uri' do

View File

@@ -10,7 +10,7 @@ shared_examples 'WpTarget::WpConfigBackup' do
# set all @config_backup_files to point to a 404
before :each do
config_backup_files.each do |backup_file|
file_url = wp_target.uri.merge(URI.escape(backup_file)).to_s
file_url = wp_target.uri.merge(url_encode(backup_file)).to_s
stub_request(:get, file_url).to_return(status: 404)
end
@@ -24,7 +24,7 @@ shared_examples 'WpTarget::WpConfigBackup' do
expected = []
config_backup_files.sample(1).each do |backup_file|
file_url = wp_target.uri.merge(URI.escape(backup_file)).to_s
file_url = wp_target.uri.merge(url_encode(backup_file)).to_s
expected << file_url
stub_request_to_fixture(url: file_url, fixture: fixtures_dir + '/wp-config.php')
@@ -40,7 +40,7 @@ shared_examples 'WpTarget::WpConfigBackup' do
expected = []
config_backup_files.sample(2).each do |backup_file|
file_url = wp_target.uri.merge(URI.escape(backup_file)).to_s
file_url = wp_target.uri.merge(url_encode(backup_file)).to_s
expected << file_url
stub_request_to_fixture(url: file_url, fixture: fixtures_dir + '/wp-config.php')