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 sudo: false
cache: bundler cache: bundler
rvm: rvm:
- 1.9.2
- 1.9.3
- 2.0.0 - 2.0.0
- 2.1.0 - 2.1.0
- 2.1.1 - 2.1.1
@@ -23,9 +21,6 @@ script: bundle exec rspec
notifications: notifications:
email: email:
- team@wpscan.org - team@wpscan.org
matrix:
allow_failures:
- rvm: 1.9.2
# do not build gh-pages branch # do not build gh-pages branch
branches: branches:
except: except:

View File

@@ -92,7 +92,7 @@ WPScan comes pre-installed on the following Linux distributions:
Prerequisites: 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 - Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
- RubyGems - Recommended: latest - RubyGems - Recommended: latest
- Git - 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 curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm source ~/.rvm/scripts/rvm
echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc
rvm install 2.2.4 rvm install 2.3.0
rvm use 2.2.4 --default rvm use 2.3.0 --default
echo "gem: --no-ri --no-rdoc" > ~/.gemrc echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler gem install bundler
git clone https://github.com/wpscanteam/wpscan.git 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) 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 ruby extconf.rb
make make
make install make install

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,35 +1,5 @@
# encoding: UTF-8 # 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 # This is used in WpItem::Existable
module Typhoeus module Typhoeus
class Response class Response

View File

@@ -1,62 +1,62 @@
# encoding: UTF-8 # encoding: UTF-8
require 'vulnerability/output' require 'vulnerability/output'
require 'vulnerability/urls' require 'vulnerability/urls'
class Vulnerability class Vulnerability
include Vulnerability::Output include Vulnerability::Output
include Vulnerability::Urls include Vulnerability::Urls
attr_accessor :title, :references, :type, :fixed_in attr_accessor :title, :references, :type, :fixed_in
# #
# @param [ String ] title The title of the vulnerability # @param [ String ] title The title of the vulnerability
# @param [ String ] type The type of the vulnerability # @param [ String ] type The type of the vulnerability
# @param [ Hash ] references References # @param [ Hash ] references References
# @param [ String ] fixed_in Vuln fixed in Version X # @param [ String ] fixed_in Vuln fixed in Version X
# #
# @return [ Vulnerability ] # @return [ Vulnerability ]
def initialize(title, type, references = {}, fixed_in = '') def initialize(title, type, references = {}, fixed_in = '')
@title = title @title = title
@type = type @type = type
@references = references @references = references
@fixed_in = fixed_in @fixed_in = fixed_in
end end
# @param [ Vulnerability ] other # @param [ Vulnerability ] other
# #
# @return [ Boolean ] # @return [ Boolean ]
# :nocov: # :nocov:
def ==(other) def ==(other)
title == other.title && title == other.title &&
type == other.type && type == other.type &&
references == other.references && references == other.references &&
fixed_in == other.fixed_in fixed_in == other.fixed_in
end end
# :nocov: # :nocov:
# Create the Vulnerability from the json_item # Create the Vulnerability from the json_item
# #
# @param [ Hash ] json_item # @param [ Hash ] json_item
# #
# @return [ Vulnerability ] # @return [ Vulnerability ]
def self.load_from_json_item(json_item) def self.load_from_json_item(json_item)
references = {} references = {}
references['id'] = [json_item['id']] references['id'] = [json_item['id']]
%w(url cve secunia osvdb metasploit exploitdb).each do |key| %w(url cve secunia osvdb metasploit exploitdb).each do |key|
if json_item['references'][key] if json_item['references'][key]
json_item['references'][key] = [json_item['references'][key]] if json_item['references'][key].class != Array json_item['references'][key] = [json_item['references'][key]] if json_item['references'][key].class != Array
references[key] = json_item['references'][key] references[key] = json_item['references'][key]
end end
end end
new( new(
json_item['title'], json_item['title'],
json_item['type'], json_item['type'],
references, references,
json_item['fixed_in'] json_item['fixed_in']
) )
end end
end end

View File

@@ -1,123 +1,121 @@
# encoding: UTF-8 # encoding: UTF-8
require 'wp_item/findable' require 'wp_item/findable'
require 'wp_item/versionable' require 'wp_item/versionable'
require 'wp_item/vulnerable' require 'wp_item/vulnerable'
require 'wp_item/existable' require 'wp_item/existable'
require 'wp_item/infos' require 'wp_item/infos'
require 'wp_item/output' require 'wp_item/output'
class WpItem class WpItem
extend WpItem::Findable extend WpItem::Findable
include WpItem::Versionable include WpItem::Versionable
include WpItem::Vulnerable include WpItem::Vulnerable
include WpItem::Existable include WpItem::Existable
include WpItem::Infos include WpItem::Infos
include WpItem::Output include WpItem::Output
attr_reader :path attr_reader :path
attr_accessor :name, :wp_content_dir, :wp_plugins_dir attr_accessor :name, :wp_content_dir, :wp_plugins_dir
# @return [ Array ] # @return [ Array ]
# Make it private ? # Make it private ?
def allowed_options def allowed_options
[:name, :wp_content_dir, :wp_plugins_dir, :path, :version, :db_file] [:name, :wp_content_dir, :wp_plugins_dir, :path, :version, :db_file]
end end
# @param [ URI ] target_base_uri # @param [ URI ] target_base_uri
# @param [ Hash ] options See allowed_option # @param [ Hash ] options See allowed_option
# #
# @return [ WpItem ] # @return [ WpItem ]
def initialize(target_base_uri, options = {}) def initialize(target_base_uri, options = {})
options[:wp_content_dir] ||= 'wp-content' options[:wp_content_dir] ||= 'wp-content'
options[:wp_plugins_dir] ||= options[:wp_content_dir] + '/plugins' options[:wp_plugins_dir] ||= options[:wp_content_dir] + '/plugins'
set_options(options) set_options(options)
forge_uri(target_base_uri) forge_uri(target_base_uri)
end end
def identifier def identifier
@identifier ||= name @identifier ||= name
end end
# @return [ Hash ] # @return [ Hash ]
def db_data def db_data
@db_data ||= json(db_file)[identifier] || {} @db_data ||= json(db_file)[identifier] || {}
end end
def latest_version def latest_version
db_data['latest_version'] db_data['latest_version']
end end
def last_updated def last_updated
db_data['last_ipdated'] db_data['last_ipdated']
end end
def popular? def popular?
db_data['popular'] db_data['popular']
end end
# @param [ Hash ] options # @param [ Hash ] options
# #
# @return [ void ] # @return [ void ]
def set_options(options) def set_options(options)
allowed_options.each do |allowed_option| allowed_options.each do |allowed_option|
if options.has_key?(allowed_option) if options.has_key?(allowed_option)
method = :"#{allowed_option}=" method = :"#{allowed_option}="
if self.respond_to?(method) if self.respond_to?(method)
self.send(method, options[allowed_option]) self.send(method, options[allowed_option])
else else
raise "#{self.class} does not respond to #{method}" raise "#{self.class} does not respond to #{method}"
end end
end end
end end
end end
private :set_options private :set_options
# @param [ URI ] target_base_uri # @param [ URI ] target_base_uri
# #
# @return [ void ] # @return [ void ]
def forge_uri(target_base_uri) def forge_uri(target_base_uri)
@uri = target_base_uri @uri = target_base_uri
end end
# @return [ URI ] The uri to the WpItem, with the path if present # @return [ URI ] The uri to the WpItem, with the path if present
def uri def uri
path ? @uri.merge(path) : @uri path ? @uri.merge(path) : @uri
end end
# @return [ String ] The url to the WpItem # @return [ String ] The url to the WpItem
def url; uri.to_s end def url; uri.to_s end
# Sets the path # Sets the path
# #
# Variable, such as $wp-plugins$ and $wp-content$ can be used # Variable, such as $wp-plugins$ and $wp-content$ can be used
# and will be replace by their value # and will be replace by their value
# #
# @param [ String ] path # @param [ String ] path
# #
# @return [ void ] # @return [ void ]
def path=(path) def path=(path)
@path = URI.encode( @path = path.gsub(/\$wp-plugins\$/i, wp_plugins_dir).gsub(/\$wp-content\$/i, wp_content_dir)
path.gsub(/\$wp-plugins\$/i, wp_plugins_dir).gsub(/\$wp-content\$/i, wp_content_dir) end
)
end # @param [ WpItem ] other
def <=>(other)
# @param [ WpItem ] other name <=> other.name
def <=>(other) end
name <=> other.name
end # @param [ WpItem ] other
def ==(other)
# @param [ WpItem ] other name === other.name
def ==(other) end
name === other.name
end # @param [ WpItem ] other
def ===(other)
# @param [ WpItem ] other self == other && version === other.version
def ===(other) end
self == other && version === other.version
end end
end

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,64 +1,64 @@
# encoding: UTF-8 # encoding: UTF-8
class WpTheme < WpItem class WpTheme < WpItem
module Findable module Findable
# Find the main theme of the blog # Find the main theme of the blog
# #
# @param [ URI ] target_uri # @param [ URI ] target_uri
# #
# @return [ WpTheme ] # @return [ WpTheme ]
def find(target_uri) def find(target_uri)
methods.grep(/^find_from_/).each do |method| methods.grep(/^find_from_/).each do |method|
if wp_theme = self.send(method, target_uri) if wp_theme = self.send(method, target_uri)
wp_theme.found_from = method wp_theme.found_from = method
return wp_theme return wp_theme
end end
end end
nil nil
end end
protected protected
# Discover the wordpress theme by parsing the css link rel # Discover the wordpress theme by parsing the css link rel
# #
# @param [ URI ] target_uri # @param [ URI ] target_uri
# #
# @return [ WpTheme ] # @return [ WpTheme ]
def find_from_css_link(target_uri) def find_from_css_link(target_uri)
response = Browser.get_and_follow_location(target_uri.to_s) response = Browser.get_and_follow_location(target_uri.to_s)
# https + domain is optional because of relative links # https + domain is optional because of relative links
return unless response.body =~ %r{(?:https?://[^"']+/)?([^/\s]+)/themes/([^"'/]+)[^"']*/style.css}i return unless response.body =~ %r{(?:https?://[^"']+/)?([^/\s]+)/themes/([^"'/]+)[^"']*/style.css}i
new( new(
target_uri, target_uri,
name: Regexp.last_match[2], name: Regexp.last_match[2],
referenced_url: Regexp.last_match[0], referenced_url: Regexp.last_match[0],
wp_content_dir: Regexp.last_match[1] wp_content_dir: Regexp.last_match[1]
) )
end end
# @param [ URI ] target_uri # @param [ URI ] target_uri
# #
# @return [ WpTheme ] # @return [ WpTheme ]
def find_from_wooframework(target_uri) def find_from_wooframework(target_uri)
body = Browser.get(target_uri.to_s).body body = Browser.get(target_uri.to_s).body
regexp = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?" />\s+<meta name="generator" content="WooFramework\s?([^"]+)?" />} regexp = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?" />\s+<meta name="generator" content="WooFramework\s?([^"]+)?" />}
if matches = regexp.match(body) if matches = regexp.match(body)
woo_theme_name = matches[1] woo_theme_name = matches[1]
woo_theme_version = matches[2] woo_theme_version = matches[2]
#woo_framework_version = matches[3] # Not used at this time #woo_framework_version = matches[3] # Not used at this time
return new( return new(
target_uri, target_uri,
name: woo_theme_name, name: woo_theme_name,
version: woo_theme_version version: woo_theme_version
) )
end end
end end
end end
end end

View File

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

View File

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

View File

@@ -1,24 +1,24 @@
# encoding: UTF-8 # encoding: UTF-8
class WpTimthumb < WpItem class WpTimthumb < WpItem
module Versionable module Versionable
# Get the version from the body of an invalid request # Get the version from the body of an invalid request
# See https://code.google.com/p/timthumb/source/browse/trunk/timthumb.php#426 # See https://code.google.com/p/timthumb/source/browse/trunk/timthumb.php#426
# #
# @return [ String ] The version # @return [ String ] The version
def version def version
unless @version unless @version
response = Browser.get(url) response = Browser.get(url)
@version = response.body[%r{TimThumb version\s*: ([^<]+)} , 1] @version = response.body[%r{TimThumb version\s*: ([^<]+)} , 1]
end end
@version @version
end end
# @return [ String ] # @return [ String ]
def to_s def to_s
"#{url}#{ ' v' + version if version}" "#{url}#{ ' v' + version if version}"
end end
end end
end end

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ class WpTarget < WebSite
queue_count = 0 queue_count = 0
backups.each do |file| 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 = browser.forge_request(file_url)
request.on_complete do |response| request.on_complete do |response|

View File

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

View File

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