Compare commits

..

54 Commits
2.8 ... 2.9

Author SHA1 Message Date
ethicalhack3r
361c96d746 Version 2.9 release 2015-10-15 13:01:53 +02:00
erwanlr
e7dbf9278d Fixes #873 - mu-plugins detection 2015-10-13 13:17:22 +01:00
erwanlr
6564fddb27 Adds a reminder about updating the terminal-table version 2015-10-13 13:12:12 +01:00
erwanlr
d382874e86 Fixes incorrect detection of the FDP data 2015-10-12 12:57:20 +01:00
erwanlr
91b30bee9f Updates Typhoeus dependency 2015-10-09 19:03:37 +02:00
erwanlr
7804aad776 Removes useless stuff & update the --throttle options text 2015-10-07 22:09:23 +01:00
erwanlr
b7552ac8aa Tried to throttle things 2015-10-07 19:03:52 +01:00
erwanlr
a76c94cccf Let's try Travis container-based infra & caching 2015-09-18 16:13:37 +02:00
Christian Mehlmauer
c0ae5c7cad Merge pull request #864 from wpscanteam/apiv2
new dependency
2015-09-11 21:09:51 +02:00
Christian Mehlmauer
cc55b39b83 new dependency 2015-09-11 15:31:29 +02:00
ethicalhack3r
d8a6884ab6 Only show 'up to date' string when version found 2015-09-09 15:46:44 +02:00
Ryan Dewhurst
5ce3581386 Merge pull request #862 from wpscanteam/apiv2
Apiv2
2015-09-08 21:00:03 +02:00
ethicalhack3r
2208f2a8c0 Implement lesser? method #862 2015-09-08 17:54:32 +02:00
ethicalhack3r
a4a14c7e63 Better version output #862 2015-09-08 17:24:10 +02:00
erwanlr
aa464b476c Fixes a bug where -e vp was displaying non vulnerable plugins - Ref #853 2015-09-06 15:25:29 +01:00
erwanlr
3c92712a6e Uses yajl as JSON parser to reduce memory used 2015-09-06 14:29:41 +01:00
erwanlr
fd0c47f5d7 Adds the latest_version, last_updated and popular? attributes - Ref #853 2015-09-06 14:26:36 +01:00
erwanlr
c03a44d225 Removes useless code 2015-09-06 13:32:13 +01:00
ethicalhack3r
d31d45ba71 Remove unneede newline 2015-09-05 14:10:08 +02:00
ethicalhack3r
db528b27f4 Implement Erwan's feedback #853 2015-09-05 13:49:03 +02:00
ethicalhack3r
e6d29f6f18 New json structure implemented #853 2015-09-03 22:04:44 +02:00
Christian Mehlmauer
e4d6b988ef forgot spec file, #858
Signed-off-by: Christian Mehlmauer <firefart@gmail.com>
2015-08-22 21:52:55 +02:00
Christian Mehlmauer
ec68291bf0 fix #858 2015-08-22 21:50:31 +02:00
ethicalhack3r
3a6a451db1 Update to Ruby 2.2.3 2015-08-21 09:41:06 +02:00
Christian Mehlmauer
7ec095d708 fix duplicate robots.txt entries 2015-08-18 15:55:10 +02:00
ethicalhack3r
57f6206aee Implement Erwan's feedbaxk #853 2015-08-14 21:51:55 +02:00
ethicalhack3r
390f10e83f Remove ArchAssault, 'had to close its doors' 2015-08-14 19:26:52 +02:00
ethicalhack3r
8727935cb2 Fix specs #853 2015-08-14 16:33:57 +02:00
ethicalhack3r
d0e868f556 Enable rspec fail-fast #853 2015-08-14 16:04:26 +02:00
ethicalhack3r
01c357e146 Fix specs #853 2015-08-14 16:03:21 +02:00
ethicalhack3r
a0fed4a9d0 Clean up last commit #853 2015-08-14 00:22:48 +02:00
ethicalhack3r
c4aed0ec89 Initial attempt at implementing apiv2 #853 2015-08-14 00:19:22 +02:00
erwanlr
cc737090a2 Fixes incorrect detection of the username 2015-08-13 10:27:33 +01:00
erwanlr
1652c09e95 Merge pull request #850 from mikicaivosevic/master
Re-factorises a statement
2015-08-12 14:53:43 +01:00
erwanlr
2538b88579 Adds the Accept-Encoding header when updating the DBs - Fixes #852 2015-08-12 14:50:14 +01:00
Mikica Ivosevic
8c2eb63840 update wp_target.rb
Refactor if else statement - wp_content_dir (credits: ethicalhack3r)
2015-07-28 12:41:09 +02:00
erwanlr
36df5ee6e4 Comments debug statement 2015-07-23 14:15:46 +01:00
erwanlr
9720b4edf1 Escapes brackets etc potentially present in Dir.pwd When using Dir.glob - Fixes #840 2015-07-23 14:15:04 +01:00
Christian Mehlmauer
13d35b7607 update email 2015-07-08 14:29:18 +02:00
Christian Mehlmauer
13c2c51cfd update email adress 2015-07-08 13:45:47 +02:00
ethicalhack3r
f43175b0c3 Use older terminal-table gem #841 2015-07-02 10:48:34 +02:00
erwanlr
1508aba8b2 Uses terminal-table 1.5.1 - Fixes #839 2015-06-28 13:54:25 +01:00
erwanlr
5414ab05e5 Restraints terminal-table version - Ref #839 2015-06-27 09:23:26 +01:00
erwanlr
bd5d2db634 Fixes #836 2015-06-26 09:24:17 +01:00
erwanlr
3259dd29d8 Merge pull request #833 from stefancastille/master
Adds a --vhost option (Virtualhost support)
2015-06-26 09:14:39 +01:00
stefancastille
6e56013a95 Update browser.rb 2015-06-25 16:18:04 +02:00
stefancastille
252f762209 Update wp_target.rb 2015-06-25 16:17:03 +02:00
stefancastille
15c0448cf1 Update wpscan_options.rb 2015-06-25 16:13:04 +02:00
erwanlr
4c800bacaa Fixes #835 2015-06-24 11:46:06 +01:00
stefancastille
86a73229c0 Update wp_target.rb 2015-06-17 08:46:14 +02:00
stefancastille
cc41b96e88 Update wpscan_options.rb 2015-06-17 08:44:50 +02:00
stefancastille
e16c5584d1 Update wpscan_options.rb 2015-06-17 08:44:04 +02:00
stefancastille
94bab3f550 Update wpscan_options.rb
Add support for virtual hosts
2015-06-17 08:42:59 +02:00
stefancastille
9d04b23fb2 Update browser.rb
add support for virtual hosts
2015-06-16 17:23:25 +02:00
66 changed files with 1150 additions and 907 deletions

View File

@@ -1 +1 @@
2.2.2
2.2.3

View File

@@ -1,4 +1,6 @@
language: ruby
sudo: false
cache: bundler
rvm:
- 1.9.2
- 1.9.3
@@ -12,12 +14,13 @@ rvm:
- 2.2.0
- 2.2.1
- 2.2.2
- 2.2.3
before_install:
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
script: bundle exec rspec
notifications:
email:
- wpscanteam@gmail.com
- team@wpscan.org
matrix:
allow_failures:
- rvm: 1.9.2

View File

@@ -1,6 +1,43 @@
# Changelog
## Master
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.8...master)
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.9...master)
## Version 2.9
Released: 2015-10-15
New
* GZIP Encoding in updater
* Adds --throttle option to throttle requests
* Uses new API and local database file structure
* Adds last updated and latest version to plugins and themes
Removed
* ArchAssault from README
* APIv1 local databases
General core
* Update to Ruby 2.2.3
* Use yajl-ruby as JSON parser
* New dependancy for Ubuntu 14.04 (libgmp-dev)
* Use Travis container based infra and caching
Fixed issues
* Fix #835 - Readme requests to wp root dir
* Fix #836 - Critical icon output twice when the site is not running WP
* Fix #839 - Terminal-table dependency is broken
* Fix #841 - error: undefined method `cells' for #<Array:0x000000029cc2f8>
* Fix #852 - GZIP Encoding in updater
* Fix #853 - APIv2 integration
* Fix #858 - Detection FP
* Fix #873 - false positive "site has Must Use Plugins"
WPScan Database Statistics:
* Total vulnerable versions: 132
* Total vulnerable plugins: 1170
* Total vulnerable themes: 368
* Total version vulnerabilities: 1476
* Total plugin vulnerabilities: 1913
* Total theme vulnerabilities: 450
## Version 2.8
Released: 2015-06-22
@@ -79,7 +116,7 @@ Fixed issues
* Fix #746 - Add a global counter for all active requests to server.
* Fix #747 - Add 'security-protection' plugin to wp_login_protection module
* Fix #753 - undefined method `round' for "10":String for request or connect timeouts
* Fix #760 - typhoeus issue (infinite loop)
* Fix #760 - typhoeus issue (infinite loop)
WPScan Database Statistics:
* Total vulnerable versions: 89

View File

@@ -1,6 +1,6 @@
**CREDITS**
This file is used to state the individual WPScan Team members (core developers) and give credit to WPScan's other contributors. If you feel your name should be in here email wpscanteam@gmail.com.
This file is used to state the individual WPScan Team members (core developers) and give credit to WPScan's other contributors. If you feel your name should be in here email team@wpscan.org.
*WPScan Team*

10
Gemfile
View File

@@ -1,15 +1,17 @@
source 'https://rubygems.org'
gem 'typhoeus', '~>0.7.0'
gem 'typhoeus', '~>0.8.0'
gem 'nokogiri'
gem 'addressable'
gem 'json'
gem 'terminal-table'
gem 'yajl-ruby' # Better JSON parser regarding memory usage
# TODO: update the below when terminal-table 1.5.3+ is released.
# (and delete the Terminal module in lib/common/hacks.rb)
gem 'terminal-table', '~>1.4.5'
gem 'ruby-progressbar', '>=1.6.0'
group :test do
gem 'webmock', '>=1.17.2'
gem 'simplecov'
gem 'rspec', '>= 3.3.0'
gem 'rspec', '>=3.3.0'
gem 'rspec-its'
end

View File

@@ -27,7 +27,7 @@ Example cases which do not require a commercial license, and thus fall under the
- Using WPScan to test your own systems.
- Any non-commercial use of WPScan.
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - wpscanteam@gmail.com.
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
We may grant commercial licenses at no monetary cost at our own discretion if the commercial usage is deemed by the WPScan Team to significantly benefit WPScan.

View File

@@ -38,7 +38,7 @@ Example cases which do not require a commercial license, and thus fall under the
- Using WPScan to test your own systems.
- Any non-commercial use of WPScan.
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - wpscanteam@gmail.com.
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
We may grant commercial licenses at no monetary cost at our own discretion if the commercial usage is deemed by the WPScan Team to significantly benefit WPScan.
@@ -88,12 +88,11 @@ WPScan comes pre-installed on the following Linux distributions:
- [Kali Linux](http://www.kali.org/)
- [Pentoo](http://www.pentoo.ch/)
- [SamuraiWTF](http://samurai.inguardians.com/)
- [ArchAssault](https://archassault.org/)
- [BlackArch](http://blackarch.org/)
Prerequisites:
- Ruby >= 1.9.2 - Recommended: 2.2.2
- Ruby >= 1.9.2 - Recommended: 2.2.3
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
- RubyGems - Recommended: latest
- Git
@@ -112,7 +111,7 @@ Before Ubuntu 14.04:
From Ubuntu 14.04:
sudo apt-get install libcurl4-openssl-dev libxml2 libxml2-dev libxslt1-dev ruby-dev build-essential
sudo apt-get install libcurl4-openssl-dev libxml2 libxml2-dev libxslt1-dev ruby-dev build-essential libgmp-dev
git clone https://github.com/wpscanteam/wpscan.git
cd wpscan
sudo gem install bundler && bundle install --without test
@@ -156,8 +155,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.2
rvm use 2.2.2 --default
rvm install 2.2.3
rvm use 2.2.3 --default
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler
git clone https://github.com/wpscanteam/wpscan.git

BIN
data.zip

Binary file not shown.

View File

@@ -17,14 +17,15 @@ class Browser
:proxy_auth,
:request_timeout,
:connect_timeout,
:cookie
:cookie,
:throttle
]
@@instance = nil
attr_reader :hydra, :cache_dir
attr_accessor :referer, :cookie
attr_accessor :referer, :cookie, :vhost
# @param [ Hash ] options
#
@@ -70,12 +71,14 @@ class Browser
# sets browser default values
#
def browser_defaults
@max_threads = 20
# 10 minutes, at this time the cache is cleaned before each scan. If this value is set to 0, the cache will be disabled
@cache_ttl = 600
@max_threads = 20
# 10 minutes, at this time the cache is cleaned before each scan.
# If this value is set to 0, the cache will be disabled
@cache_ttl = 600
@request_timeout = 60 # 60s
@connect_timeout = 10 # 10s
@user_agent = "WPScan v#{WPSCAN_VERSION} (http://wpscan.org)"
@user_agent = "WPScan v#{WPSCAN_VERSION} (http://wpscan.org)"
@throttle = 0
end
#
@@ -86,7 +89,6 @@ class Browser
#
# @return [ void ]
def load_config(config_file = nil)
if File.symlink?(config_file)
raise '[ERROR] Config file is a symlink.'
else
@@ -99,7 +101,6 @@ class Browser
self.send(:"#{option_name}=", data[option_name])
end
end
end
# @param [ String ] url
@@ -121,11 +122,8 @@ class Browser
)
if @proxy
params = params.merge(proxy: @proxy)
if @proxy_auth
params = params.merge(proxyauth: @proxy_auth)
end
params.merge!(proxy: @proxy)
params.merge!(proxyauth: @proxy_auth) if @proxy_auth
end
if @basic_auth
@@ -136,15 +134,23 @@ class Browser
)
end
if vhost
params = Browser.append_params_header_field(
params,
'Host',
vhost
)
end
params.merge!(referer: referer)
params.merge!(timeout: @request_timeout) if @request_timeout
params.merge!(connecttimeout: @connect_timeout) if @connect_timeout
# Used to enable the cache system if :cache_ttl > 0
params.merge!(cache_ttl: @cache_ttl) unless params.has_key?(:cache_ttl)
params.merge!(cache_ttl: @cache_ttl) unless params.key?(:cache_ttl)
# Prevent infinite self redirection
params.merge!(maxredirs: 3) unless params.has_key?(:maxredirs)
params.merge!(maxredirs: 3) unless params.key?(:maxredirs)
# Disable SSL-Certificate checks
params.merge!(ssl_verifypeer: false)
@@ -172,5 +178,4 @@ class Browser
end
params
end
end

View File

@@ -4,7 +4,7 @@ class Browser
module Options
attr_accessor :cache_ttl, :request_timeout, :connect_timeout
attr_reader :basic_auth, :proxy, :proxy_auth
attr_reader :basic_auth, :proxy, :proxy_auth, :throttle
attr_writer :user_agent
# Sets the Basic Authentification credentials
@@ -93,6 +93,11 @@ class Browser
@connect_timeout = timeout.to_i
end
# @param [ String, Integer ] throttle
def throttle=(throttle)
@throttle = throttle.to_i.abs / 1000.0
end
protected
def invalid_proxy_auth_format
@@ -110,6 +115,5 @@ class Browser
end
end
end
end
end

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,22 +18,17 @@ 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'
WPSCAN_VERSION = '2.9'
$LOAD_PATH.unshift(LIB_DIR)
$LOAD_PATH.unshift(WPSCAN_LIB_DIR)
@@ -49,14 +44,18 @@ end
require 'environment'
def escape_glob(s)
s.gsub(/[\\\{\}\[\]\*\?]/) { |x| '\\' + x }
end
# TODO : add an exclude pattern ?
def require_files_from_directory(absolute_dir_path, files_pattern = '*.rb')
files = Dir[File.join(absolute_dir_path, files_pattern)]
files = Dir[File.join(escape_glob(absolute_dir_path), files_pattern)]
# Files in the root dir are loaded first, then those in the subdirectories
files.sort_by { |file| [file.count('/'), file] }.each do |f|
f = File.expand_path(f)
#puts "require #{f}" # Used for debug
# puts "require #{f}" # Used for debug
require f
end
end

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
@@ -22,7 +21,8 @@ class DbUpdater
def request_params
{
ssl_verifyhost: 2,
ssl_verifypeer: true
ssl_verifypeer: true,
accept_encoding: 'gzip, deflate'
}
end

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 version}" 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

@@ -39,7 +39,7 @@ class WpUser < WpItem
#
# @return [ String ] The login
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')
end

View File

@@ -1,21 +1,27 @@
# 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
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 ]
@@ -29,5 +35,4 @@ class WpVersion < WpItem
a << node.text.to_s
end
end
end

View File

@@ -1,222 +1,222 @@
# encoding: UTF-8
class WpVersion < WpItem
module Findable
# Find the version of the blog designated from target_uri
#
# @param [ URI ] target_uri
# @param [ String ] wp_content_dir
# @param [ String ] wp_plugins_dir
#
# @return [ WpVersion ]
def find(target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
methods.grep(/^find_from_/).each do |method|
if method === :find_from_advanced_fingerprinting
version = send(method, target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
else
version = send(method, target_uri)
end
if version
return new(target_uri, number: version, found_from: method)
end
end
nil
end
# Used to check if the version is correct: must contain at least one dot.
#
# @return [ String ]
def version_pattern
'([^\r\n"\']+\.[^\r\n"\']+)'
end
protected
# Returns the first match of <pattern> in the body of the url
#
# @param [ URI ] target_uri
# @param [ Regex ] pattern
# @param [ String ] path
#
# @return [ String ]
def scan_url(target_uri, pattern, path = nil)
url = path ? target_uri.merge(path).to_s : target_uri.to_s
response = Browser.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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_meta_generator(target_uri)
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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_rss_generator(target_uri)
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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_rdf_generator(target_uri)
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 Atom source.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_atom_generator(target_uri)
scan_url(
target_uri,
%r{<generator uri="http://wordpress.org/" version="#{version_pattern}">WordPress</generator>}i,
'feed/atom/'
)
end
def find_from_stylesheets_numbers(target_uri)
wp_versions = WpVersion.all
found = {}
pattern = /\bver=([0-9\.]+)/i
Nokogiri::HTML(Browser.get(target_uri.to_s).body).css('link,script').each do |tag|
%w(href src).each do |attribute|
attr_value = tag.attribute(attribute).to_s
next if attr_value.nil? || attr_value.empty?
uri = Addressable::URI.parse(attr_value)
next unless uri.query && uri.query.match(pattern)
version = Regexp.last_match[1].to_s
found[version] ||= 0
found[version] += 1
end
end
found.delete_if { |v, _| !wp_versions.include?(v) }
best_guess = found.sort_by(&:last).last
# best_guess[0]: version number, [1] numbers of occurences
best_guess && best_guess[1] > 1 ? best_guess[0] : nil
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)
#
# @param [ URI ] target_uri
# @param [ String ] wp_content_dir
# @param [ String ] wp_plugins_dir
# @param [ String ] versions_xml The path to the xml containing all versions
#
# @return [ String ] The version number
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)
xml.xpath('//file').each do |node|
wp_item.path = node.attribute('src').text
response = Browser.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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_readme(target_uri)
scan_url(
target_uri,
%r{<br />\sversion #{version_pattern}}i,
'readme.html'
)
end
# Attempts to find the WordPress version from the sitemap.xml file.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_sitemap_generator(target_uri)
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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_links_opml(target_uri)
scan_url(
target_uri,
%r{generator="wordpress/#{version_pattern}"}i,
'wp-links-opml.php'
)
end
end
end
# encoding: UTF-8
class WpVersion < WpItem
module Findable
# Find the version of the blog designated from target_uri
#
# @param [ URI ] target_uri
# @param [ String ] wp_content_dir
# @param [ String ] wp_plugins_dir
#
# @return [ WpVersion ]
def find(target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
methods.grep(/^find_from_/).each do |method|
if method === :find_from_advanced_fingerprinting
version = send(method, target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
else
version = send(method, target_uri)
end
if version
return new(target_uri, number: version, found_from: method)
end
end
nil
end
# Used to check if the version is correct: must contain at least one dot.
#
# @return [ String ]
def version_pattern
'([^\r\n"\',]+\.[^\r\n"\',]+)'
end
protected
# Returns the first match of <pattern> in the body of the url
#
# @param [ URI ] target_uri
# @param [ Regex ] pattern
# @param [ String ] path
#
# @return [ String ]
def scan_url(target_uri, pattern, path = nil)
url = path ? target_uri.merge(path).to_s : target_uri.to_s
response = Browser.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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_meta_generator(target_uri)
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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_rss_generator(target_uri)
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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_rdf_generator(target_uri)
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 Atom source.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_atom_generator(target_uri)
scan_url(
target_uri,
%r{<generator uri="http://wordpress.org/" version="#{version_pattern}">WordPress</generator>}i,
'feed/atom/'
)
end
def find_from_stylesheets_numbers(target_uri)
wp_versions = WpVersion.all
found = {}
pattern = /\bver=([0-9\.]+)/i
Nokogiri::HTML(Browser.get(target_uri.to_s).body).css('link,script').each do |tag|
%w(href src).each do |attribute|
attr_value = tag.attribute(attribute).to_s
next if attr_value.nil? || attr_value.empty?
uri = Addressable::URI.parse(attr_value)
next unless uri.query && uri.query.match(pattern)
version = Regexp.last_match[1].to_s
found[version] ||= 0
found[version] += 1
end
end
found.delete_if { |v, _| !wp_versions.include?(v) }
best_guess = found.sort_by(&:last).last
# best_guess[0]: version number, [1] numbers of occurences
best_guess && best_guess[1] > 1 ? best_guess[0] : nil
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)
#
# @param [ URI ] target_uri
# @param [ String ] wp_content_dir
# @param [ String ] wp_plugins_dir
# @param [ String ] versions_xml The path to the xml containing all versions
#
# @return [ String ] The version number
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)
xml.xpath('//file').each do |node|
wp_item.path = node.attribute('src').text
response = Browser.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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_readme(target_uri)
scan_url(
target_uri,
%r{<br />\sversion #{version_pattern}}i,
'readme.html'
)
end
# Attempts to find the WordPress version from the sitemap.xml file.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_sitemap_generator(target_uri)
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.
#
# @param [ URI ] target_uri
#
# @return [ String ] The version number
def find_from_links_opml(target_uri)
scan_url(
target_uri,
%r{generator="wordpress/#{version_pattern}"}i,
'wp-links-opml.php'
)
end
end
end

View File

@@ -1,25 +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
# @return [ String ]
# def vulns_xpath
# "//wordpress[@version='#{@number}']/vulnerability"
# 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'

View File

@@ -28,6 +28,7 @@ class WebSite
if entries
entries.flatten!
entries.compact.sort!
entries.uniq!
wordpress_path = @uri.path
RobotsTxt.known_dirs.each do |d|
entries.delete(d)

View File

@@ -28,8 +28,13 @@ class WpTarget < WebSite
@wp_content_dir = options[:wp_content_dir]
@wp_plugins_dir = options[:wp_plugins_dir]
@multisite = nil
@vhost = options[:vhost]
Browser.instance.referer = url
if @vhost
Browser.instance.vhost = @vhost
end
end
# check if the target website is
@@ -44,11 +49,7 @@ class WpTarget < WebSite
fail "The target is responding with a 403, this might be due to a WAF or a plugin.\n" \
'You should try to supply a valid user-agent via the --user-agent option or use the --random-agent option' if response.code == 403
if wp_content_dir
dir = wp_content_dir
else
dir = 'wp-content'
end
dir = wp_content_dir ? wp_content_dir : 'wp-content'
if response.body =~ /["'][^"']*\/#{Regexp.escape(dir)}\/[^"']*["']/i
wordpress = true

View File

@@ -40,7 +40,7 @@ class WpTarget < WebSite
# @return [ Array ]
def self.config_backup_files
%w{
wp-config.php~ #wp-config.php# wp-config.php.save .wp-config.php.swp wp-config.php.swp wp-config.php.swo
wp-config.php~ #wp-config.php# wp-config.php.save .wp-config.php.swp wp-config.php.swp wp-config.php.swo
wp-config.php_bak wp-config.bak wp-config.php.bak wp-config.save wp-config.old wp-config.php.old
wp-config.php.orig wp-config.orig wp-config.php.original wp-config.original wp-config.txt
} # thanks to Feross.org for these

View File

@@ -2,24 +2,21 @@
class WpTarget < WebSite
module WpFullPathDisclosure
# Check for Full Path Disclosure (FPD)
#
# @return [ Boolean ]
def has_full_path_disclosure?
response = Browser.get(full_path_disclosure_url)
response.body[%r{Fatal error}i] ? true : false
Browser.get(full_path_disclosure_url).body[%r/Fatal error/i] ? true : false
end
def full_path_disclosure_data
return nil unless has_full_path_disclosure?
Browser.get(full_path_disclosure_url).body[%r{<b>([^<]+\.php)</b>}, 1]
Browser.get(full_path_disclosure_url).body[/Fatal error:.+? in (.+?) on/i, 1]
end
# @return [ String ]
def full_path_disclosure_url
@uri.merge('wp-includes/rss-functions.php').to_s
end
end
end

View File

@@ -2,14 +2,13 @@
class WpTarget < WebSite
module WpMustUsePlugins
# Checks to see if the must use plugin folder exists
#
# @return [ Boolean ]
def has_must_use_plugins?
response = Browser.get(must_use_url)
if response && WpTarget.valid_response_codes.include?(response.code)
if response && [200, 401, 403].include?(response.code)
hash = WebSite.page_hash(response)
return true if hash != error_404_hash && hash != homepage_hash
end
@@ -21,6 +20,5 @@ class WpTarget < WebSite
def must_use_url
@uri.merge("#{wp_content_dir}/mu-plugins/").to_s
end
end
end

View File

@@ -105,6 +105,7 @@ def help
puts '--request-timeout <request-timeout> Request Timeout.'
puts '--connect-timeout <connect-timeout> Connect Timeout.'
puts '--max-threads <max-threads> Maximum Threads.'
puts '--throttle <milliseconds> Milliseconds to wait before doing another web request. If used, the --threads should be set to 1.'
puts '--help | -h This help screen.'
puts '--verbose | -v Verbose output.'
puts '--version Output the current version and exit.'
@@ -118,8 +119,14 @@ down = 0
@total_requests_done = 0
Typhoeus.on_complete do |response|
next if response.cached?
down += 1 if response.code == 0
@total_requests_done += 1
fail 'The target seems to be down' if down >= 30
next unless Browser.instance.throttle > 0
sleep(Browser.instance.throttle)
end

View File

@@ -1,7 +1,6 @@
# encoding: UTF-8
class WpscanOptions
ACCESSOR_OPTIONS = [
:batch,
:enumerate_plugins,
@@ -19,6 +18,7 @@ class WpscanOptions
:proxy_auth,
:threads,
:url,
:vhost,
:wordlist,
:force,
:update,
@@ -42,7 +42,8 @@ class WpscanOptions
:request_timeout,
:connect_timeout,
:max_threads,
:no_banner
:no_banner,
:throttle
]
attr_accessor *ACCESSOR_OPTIONS
@@ -61,6 +62,10 @@ class WpscanOptions
@url = URI.parse(add_http_protocol(url)).to_s
end
def vhost=(vhost)
@vhost = vhost
end
def threads=(threads)
@threads = threads.is_a?(Integer) ? threads : threads.to_i
end
@@ -246,6 +251,7 @@ class WpscanOptions
def self.get_opt_long
GetoptLong.new(
['--url', '-u', GetoptLong::REQUIRED_ARGUMENT],
['--vhost',GetoptLong::OPTIONAL_ARGUMENT],
['--enumerate', '-e', GetoptLong::OPTIONAL_ARGUMENT],
['--username', '-U', GetoptLong::REQUIRED_ARGUMENT],
['--usernames', GetoptLong::REQUIRED_ARGUMENT],
@@ -275,7 +281,8 @@ class WpscanOptions
['--no-color', GetoptLong::NO_ARGUMENT],
['--cookie', GetoptLong::REQUIRED_ARGUMENT],
['--log', GetoptLong::NO_ARGUMENT],
['--no-banner', GetoptLong::NO_ARGUMENT]
['--no-banner', GetoptLong::NO_ARGUMENT],
['--throttle', GetoptLong::REQUIRED_ARGUMENT]
)
end

View File

@@ -11,7 +11,7 @@ describe WpPlugins do
let(:expected) do
{
request_params: { cache_ttl: 0, followlocation: true },
vulns_file: PLUGINS_VULNS_FILE,
vulns_file: PLUGINS_FILE,
targets_items_from_file: [ WpPlugin.new(uri, name: 'plugin1'),
WpPlugin.new(uri, name:'plugin-2'),
WpPlugin.new(uri, name: 'mr-smith')],

View File

@@ -13,7 +13,7 @@ describe WpThemes do
let(:expected) do
{
request_params: { cache_ttl: 0, followlocation: true },
vulns_file: THEMES_VULNS_FILE,
vulns_file: THEMES_FILE,
targets_items_from_file: [ WpTheme.new(uri, name: '3colours'),
WpTheme.new(uri, name:'42k'),
WpTheme.new(uri, name: 'a-ri')],

View File

@@ -11,11 +11,11 @@ describe WpItem do
end
it_behaves_like 'WpItem::Versionable'
it_behaves_like 'WpItem::Vulnerable' do
let(:vulns_file) { MODELS_FIXTURES + '/wp_item/vulnerable/items_vulns.json' }
let(:db_file) { MODELS_FIXTURES + '/wp_item/vulnerable/items_vulns.json' }
let(:identifier) { 'neo' }
let(:expected_refs) { {
'id' => [2993],
'url' => ['Ref 1,Ref 2'],
'url' => ['Ref 1', 'Ref 2'],
'cve' => ['2011-001'],
'secunia' => ['secunia'],
'osvdb' => ['osvdb'],

View File

@@ -5,11 +5,11 @@ require 'spec_helper'
describe WpPlugin do
it_behaves_like 'WpPlugin::Vulnerable'
it_behaves_like 'WpItem::Vulnerable' do
let(:options) { { name: 'white-rabbit' } }
let(:vulns_file) { MODELS_FIXTURES + '/wp_plugin/vulnerable/plugins_vulns.json' }
let(:options) { { name: 'white-rabbit' } }
let(:db_file) { MODELS_FIXTURES + '/wp_plugin/vulnerable/plugins.json' }
let(:expected_refs) { {
'id' => [2993],
'url' => ['Ref 1,Ref 2'],
'url' => ['Ref 1', 'Ref 2'],
'cve' => ['2011-001'],
'secunia' => ['secunia'],
'osvdb' => ['osvdb'],

View File

@@ -7,10 +7,10 @@ describe WpTheme do
it_behaves_like 'WpTheme::Vulnerable'
it_behaves_like 'WpItem::Vulnerable' do
let(:options) { { name: 'the-oracle' } }
let(:vulns_file) { MODELS_FIXTURES + '/wp_theme/vulnerable/themes_vulns.json' }
let(:db_file) { MODELS_FIXTURES + '/wp_theme/vulnerable/themes_vulns.json' }
let(:expected_refs) { {
'id' => [2993],
'url' => ['Ref 1,Ref 2'],
'url' => ['Ref 1', 'Ref 2'],
'cve' => ['2011-001'],
'secunia' => ['secunia'],
'osvdb' => ['osvdb'],

View File

@@ -65,6 +65,11 @@ describe 'WpVersion::Findable' do
@fixture = '/3.5_minified.html'
@expected = '3.5'
end
it 'returns 3.5.1' do
@fixture = '/3.5.1_mobile.html'
@expected = '3.5.1'
end
end
end

View File

@@ -4,20 +4,6 @@ require 'spec_helper'
describe WpVersion do
it_behaves_like 'WpVersion::Vulnerable'
it_behaves_like 'WpItem::Vulnerable' do
let(:options) { { number: '3.2' } }
let(:vulns_file) { MODELS_FIXTURES + '/wp_version/vulnerable/versions_vulns.json' }
let(:expected_refs) { {
'id' => [2993],
'url' => ['Ref 1,Ref 2'],
'cve' => ['2011-001'],
'secunia' => ['secunia'],
'osvdb' => ['osvdb'],
'metasploit' => ['exploit/ex1'],
'exploitdb' => ['exploitdb']
} }
let(:expected_vulns) { Vulnerabilities.new << Vulnerability.new('Here I Am', 'SQLI', expected_refs) }
end
subject(:wp_version) { WpVersion.new(uri, options) }
let(:uri) { URI.parse('http://example.com/') }

View File

@@ -121,4 +121,122 @@ describe 'VersionCompare' do
end
end
describe '::lesser?' do
context 'version checked is newer' do
after { expect(VersionCompare::lesser?(@version1, @version2)).to be_truthy }
it 'returns true' do
@version1 = '1.0'
@version2 = '2.0'
end
it 'returns true' do
@version1 = '1.0'
@version2 = '1.1'
end
it 'returns true' do
@version1 = '1.0a'
@version2 = '1.0b'
end
it 'returns true' do
@version1 = '1.0'
@version2 = '5000000'
end
it 'returns true' do
@version1 = '0'
@version2 = '1'
end
it 'returns true' do
@version1 = '0.4.2b'
@version2 = '2.3.3'
end
it 'returns true' do
@version1 = '.47'
@version2 = '.50.3'
end
end
context 'version checked is older' do
after { expect(VersionCompare::lesser?(@version1, @version2)).to be_falsey }
it 'returns false' do
@version1 = '1'
@version2 = '0'
end
it 'returns false' do
@version1 = '1.0'
@version2 = '0.5'
end
it 'returns false' do
@version1 = '500000'
@version2 = '1'
end
it 'returns false' do
@version1 = '1.6.3.7.3.4'
@version2 = '1.2.4.567.679.8.e'
end
it 'returns false' do
@version1 = '.47'
@version2 = '.46.3'
end
end
context 'version checked is the same' do
after { expect(VersionCompare::lesser?(@version1, @version2)).to be_falsey }
it 'returns true' do
@version1 = '1'
@version2 = '1'
end
it 'returns true' do
@version1 = 'a'
@version2 = 'a'
end
end
context 'version number causes Gem::Version new Exception' do
after { expect(VersionCompare::lesser?(@version1, @version2)).to be_falsey }
it 'returns false' do
@version1 = 'a'
@version2 = 'b'
end
end
context 'one version number is not set' do
after { expect(VersionCompare::lesser?(@version1, @version2)).to be_falsey }
it 'returns false (version2 nil)' do
@version1 = '1'
@version2 = nil
end
it 'returns false (version1 nil)' do
@version1 = nil
@version2 = '1'
end
it 'returns false (version2 empty)' do
@version1 = '1'
@version2 = ''
end
it 'returns false (version1 empty)' do
@version1 = ''
@version2 = '1'
end
end
end
end

View File

@@ -1,58 +1,64 @@
[
{
"mr-smith":{
"vulnerabilities":[
{
"id":2989,
"title":"Administrator-exploitable blind SQLi in WordPress 1.0 - 3.8.1",
"references":"https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/,http://www.example.com",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:43:41.000Z"
{
"mr-smith": {
"vulnerabilities":[
{
"id":2989,
"title":"Administrator-exploitable blind SQLi in WordPress 1.0 - 3.8.1",
"references": {
"url": "https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/,http://www.example.com"
},
{
"id":2990,
"title":"Potential Authentication Cookie Forgery",
"references":"https://labs.mwrinfosecurity.com/blog/2014/04/11/wordpress-auth-cookie-forgery/,https://github.com/WordPress/WordPress/commit/78a915e0e5927cf413aa6c2cef2fca3dc587f8be",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:43:41.000Z"
},
{
"id":2990,
"title":"Potential Authentication Cookie Forgery",
"references": {
"url": "https://labs.mwrinfosecurity.com/blog/2014/04/11/wordpress-auth-cookie-forgery/,https://github.com/WordPress/WordPress/commit/78a915e0e5927cf413aa6c2cef2fca3dc587f8be",
"osvdb":"105620",
"cve":"2014-0166",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
"cve":"2014-0166"
},
{
"id":2991,
"title":"Privilege escalation: contributors publishing posts",
"references":"https://github.com/wpscanteam/wpscan/wiki/CVE-2014-0165",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
},
{
"id":2991,
"title":"Privilege escalation: contributors publishing posts",
"references": {
"url": "https://github.com/wpscanteam/wpscan/wiki/CVE-2014-0165",
"osvdb":"105630",
"cve":"2014-0165",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
"cve":"2014-0165"
},
{
"id":2992,
"title":"Plupload Unspecified XSS",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
},
{
"id":2992,
"title":"Plupload Unspecified XSS",
"references": {
"osvdb":"105622",
"secunia":"57769",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
}
]
}
"secunia":"57769"
},
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
}
]
},
{
"neo":{
"vulnerabilities":[
{
"id":2993,
"title":"wp-admin/options-writing.php Cleartext Admin Credentials Disclosure",
"references":"http://seclists.org/fulldisclosure/2013/Dec/135",
"osvdb":"101101",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
"neo": {
"vulnerabilities":[
{
"id":2993,
"title":"wp-admin/options-writing.php Cleartext Admin Credentials Disclosure",
"references": {
"url": "http://seclists.org/fulldisclosure/2013/Dec/135",
"osvdb":"101101"
},
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
]
}

View File

@@ -1,58 +1,64 @@
[
{
"mr-smith":{
"vulnerabilities":[
{
"id":2989,
"title":"Administrator-exploitable blind SQLi in WordPress 1.0 - 3.8.1",
"references":"https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/,http://www.example.com",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:43:41.000Z"
{
"mr-smith": {
"vulnerabilities":[
{
"id":2989,
"title":"Administrator-exploitable blind SQLi in WordPress 1.0 - 3.8.1",
"references": {
"url": "https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/,http://www.example.com"
},
{
"id":2990,
"title":"Potential Authentication Cookie Forgery",
"references":"https://labs.mwrinfosecurity.com/blog/2014/04/11/wordpress-auth-cookie-forgery/,https://github.com/WordPress/WordPress/commit/78a915e0e5927cf413aa6c2cef2fca3dc587f8be",
"osvdb":"105620",
"cve":"2014-0166",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:43:41.000Z"
},
{
"id":2990,
"title":"Potential Authentication Cookie Forgery",
"references": {
"url": "https://labs.mwrinfosecurity.com/blog/2014/04/11/wordpress-auth-cookie-forgery/,https://github.com/WordPress/WordPress/commit/78a915e0e5927cf413aa6c2cef2fca3dc587f8be"
},
{
"id":2991,
"title":"Privilege escalation: contributors publishing posts",
"references":"https://github.com/wpscanteam/wpscan/wiki/CVE-2014-0165",
"osvdb":"105620",
"cve":"2014-0166",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
},
{
"id":2991,
"title":"Privilege escalation: contributors publishing posts",
"references": {
"url": "https://github.com/wpscanteam/wpscan/wiki/CVE-2014-0165",
"osvdb":"105630",
"cve":"2014-0165",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
"cve":"2014-0165"
},
{
"id":2992,
"title":"Plupload Unspecified XSS",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
},
{
"id":2992,
"title":"Plupload Unspecified XSS",
"references": {
"osvdb":"105622",
"secunia":"57769",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
}
]
}
"secunia":"57769"
},
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
}
]
},
{
"neo":{
"vulnerabilities":[
{
"id":2993,
"title":"wp-admin/options-writing.php Cleartext Admin Credentials Disclosure",
"references":"http://seclists.org/fulldisclosure/2013/Dec/135",
"osvdb":"101101",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
"neo": {
"vulnerabilities":[
{
"id":2993,
"title":"wp-admin/options-writing.php Cleartext Admin Credentials Disclosure",
"references": {
"url": "http://seclists.org/fulldisclosure/2013/Dec/135",
"osvdb":"101101"
},
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
]
}

View File

@@ -1,58 +1,65 @@
[
{
"shopperpress":{
"vulnerabilities":[
{
"id":2989,
"title":"Administrator-exploitable blind SQLi in WordPress 1.0 - 3.8.1",
"references":"https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/,http://www.example.com",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:43:41.000Z"
{
"shopperpress": {
"vulnerabilities":[
{
"id":2989,
"title":"Administrator-exploitable blind SQLi in WordPress 1.0 - 3.8.1",
"references": {
"url": "https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/,http://www.example.com"
},
{
"id":2990,
"title":"Potential Authentication Cookie Forgery",
"references":"https://labs.mwrinfosecurity.com/blog/2014/04/11/wordpress-auth-cookie-forgery/,https://github.com/WordPress/WordPress/commit/78a915e0e5927cf413aa6c2cef2fca3dc587f8be",
"osvdb":"105620",
"cve":"2014-0166",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:43:41.000Z"
},
{
"id":2990,
"title":"Potential Authentication Cookie Forgery",
"references": {
"url": "https://labs.mwrinfosecurity.com/blog/2014/04/11/wordpress-auth-cookie-forgery/,https://github.com/WordPress/WordPress/commit/78a915e0e5927cf413aa6c2cef2fca3dc587f8be",
"osvdb":"105620",
"cve":"2014-0166"
},
{
"id":2991,
"title":"Privilege escalation: contributors publishing posts",
"references":"https://github.com/wpscanteam/wpscan/wiki/CVE-2014-0165",
"osvdb":"105630",
"cve":"2014-0165",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
},
{
"id":2991,
"title":"Privilege escalation: contributors publishing posts",
"references": {
"url": "https://github.com/wpscanteam/wpscan/wiki/CVE-2014-0165",
"osvdb":"105630",
"cve":"2014-0165"
},
{
"id":2992,
"title":"Plupload Unspecified XSS",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
},
{
"id":2992,
"title":"Plupload Unspecified XSS",
"references": {
"osvdb":"105622",
"secunia":"57769",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
}
]
}
"secunia":"57769"
},
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z",
"fixed_in":"3.8.2"
}
]
},
{
"webfolio":{
"vulnerabilities":[
{
"id":2993,
"title":"wp-admin/options-writing.php Cleartext Admin Credentials Disclosure",
"references":"http://seclists.org/fulldisclosure/2013/Dec/135",
"osvdb":"101101",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
"webfolio": {
"vulnerabilities":[
{
"id":2993,
"title":"wp-admin/options-writing.php Cleartext Admin Credentials Disclosure",
"references": {
"url": "http://seclists.org/fulldisclosure/2013/Dec/135",
"osvdb":"101101"
},
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
]
}

View File

@@ -1,12 +1,14 @@
{
"id": "3911",
"title": "Vuln Title",
"url": "Ref 1,Ref 2",
"secunia": "secunia",
"osvdb": "osvdb",
"cve": "2011-001",
"metasploit": "exploit/ex1",
"exploitdb": "exploitdb",
"references":{
"url": "Ref 1,Ref 2",
"secunia": "secunia",
"osvdb": "osvdb",
"cve": "2011-001",
"metasploit": "exploit/ex1",
"exploitdb": "exploitdb"
},
"created_at": "2014-07-28T12:10:45.000Z",
"updated_at": "2014-07-28T12:10:45.000Z",
"type": "CSRF",

View File

@@ -1,35 +1,35 @@
[
{
"not-this-one":{
"vulnerabilities":[
{
"id":2989,
"title":"Administrator-exploitable blind SQLi in WordPress 1.0 - 3.8.1",
"url":"https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/,http://www.example.com",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:43:41.000Z"
}
]
}
{
"not-this-one": {
"vulnerabilities":[
{
"id":2989,
"title":"Administrator-exploitable blind SQLi in WordPress 1.0 - 3.8.1",
"references": {
"url": ["https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/" ,"http://www.example.com"]
},
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:43:41.000Z"
}
]
},
{
"neo":{
"vulnerabilities":[
{
"id":2993,
"title":"I'm the one",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
"neo": {
"vulnerabilities":[
{
"id":2993,
"title":"I'm the one",
"references": {
"url": ["Ref 1", "Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
]
}

View File

@@ -0,0 +1,58 @@
{
"mr-smith": {
"vulnerabilities":[
{
"id":2993,
"title":"I should not appear in the results",
"references": {
"url": ["Ref 1","Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
},
{
"id":2989,
"title":"Neither do I",
"references": {
"url": ["Ref 1" ,"Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
},
"white-rabbit": {
"vulnerabilities": [
{
"id":2993,
"title":"Follow me!",
"references": {
"url": ["Ref 1", "Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
"type":"REDIRECT",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
}

View File

@@ -1,56 +0,0 @@
[
{
"mr-smith":{
"vulnerabilities":[
{
"id":2989,
"title":"I should not appear in the results",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
},
{
"id":2989,
"title":"Neither do I",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
},
{
"white-rabbit":{
"vulnerabilities":[
{
"id":2993,
"title":"Follow me!",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"type":"REDIRECT",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
}
]

View File

@@ -1,56 +1,59 @@
[
{
"mr-smith":{
"vulnerabilities":[
{
"id":2989,
"title":"I should not appear in the results",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
{
"mr-smith": {
"vulnerabilities":[
{
"id":2989,
"title":"I should not appear in the results",
"references": {
"url": ["Ref 1", "Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
{
"id":2989,
"title":"Neither do I",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
},
{
"id":2989,
"title":"Neither do I",
"references": {
"url": ["Ref 1", "Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
},
{
"the-oracle":{
},
"the-oracle": {
"vulnerabilities":[
{
"id":2993,
"title":"I see you",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"references": {
"url": ["Ref 1", "Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
"type":"FPD",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
}
]
}

View File

@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en-US" prefix="og: http://ogp.me/ns#">
<head>
<meta charset="UTF-8" />
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="http://example.com/wp-content/themes/scrollider child/style.css" media="screen" />
<link rel="stylesheet" type="text/css" media="print"
href="http://example.com/wp-content/themes/scrollider child/print.css" />
<link rel="pingback" href="http://example.com/xmlrpc.php" />
<!-- This site is optimized with the Yoast WordPress SEO plugin v1.6.1 - https://yoast.com/wordpress/plugins/seo/ -->
<link rel="canonical" href="http://example.com" />
<link rel="next" href="http://example.com/page/2/" />
<meta property="og:locale" content="en_US" />
<meta property="og:type" content="website" />
<meta property="og:title" content="Test" />
<meta property="og:url" content="http://example.com" />
<meta property="og:site_name" content="Test" />
<script type="application/ld+json">{ "@context": "http://schema.org", "@type": "WebSite", "url": "http://example.com/", "potentialAction": { "@type": "SearchAction", "target": "http://example.com/?s={search_term}", "query-input": "required name=search_term" } }</script>
<!-- / Yoast WordPress SEO plugin. -->
<link rel="alternate" type="application/rss+xml" title="Test" href="http://www.example.com/feed" />
<link rel="alternate" type="application/rss+xml" title="Test" href="http://example.com/comments/feed/" />
<link rel='stylesheet' id='colorbox-theme3-css' href='http://example.com/wp-content/plugins/ewsel-lightbox-for-galleries/colorbox/theme3/colorbox.css?ver=1.3.14' type='text/css' media='screen' />
<link rel='stylesheet' id='woo-layout-css' href='http://example.com/wp-content/themes/scrollider/css/layout.css?ver=3.5.1' type='text/css' media='all' />
<link rel='stylesheet' id='woocommerce-css' href='http://example.com/wp-content/themes/scrollider/css/woocommerce.css?ver=3.5.1' type='text/css' media='all' />
<link rel='stylesheet' id='contact-form-7-css' href='http://example.com/wp-content/plugins/contact-form-7/includes/css/styles.css?ver=3.4.2' type='text/css' media='all' />
<link rel='stylesheet' id='videogallery_css-css' href='http://example.com/wp-content/plugins/contus-video-gallery/css/style.min.css?ver=3.5.1' type='text/css' media='all' />
<link rel='stylesheet' id='SFSImainCss-css' href='http://example.com/wp-content/plugins/ultimate-social-media-icons/css/sfsi-style.css?ver=3.5.1' type='text/css' media='all' />
<link rel='stylesheet' id='SFSIJqueryCSS-css' href='http://example.com/wp-content/plugins/ultimate-social-media-icons/css/jquery-ui-1.10.4/jquery-ui-min.css?ver=3.5.1' type='text/css' media='all' />
<link rel='stylesheet' id='prettyPhoto-css' href='http://example.com/wp-content/themes/scrollider/includes/css/prettyPhoto.css?ver=3.5.1' type='text/css' media='all' />
<script type='text/javascript' src='http://example.com/wp-includes/js/jquery/jquery.js?ver=1.8.3'></script>
<script type='text/javascript' src='http://example.com/wp-content/plugins/ewsel-lightbox-for-galleries/colorbox/jquery.colorbox-min.js?ver=1.3.14'></script>
<script type='text/javascript' src='http://example.com/wp-content/plugins/contus-video-gallery/js/script.min.js?ver=3.5.1'></script>
<script type='text/javascript'>
/* <![CDATA[ */
var ajax_object = {"ajax_url":"http:\/\/example.com\/wp-admin\/admin-ajax.php"};
/* ]]> */
</script>
<script type='text/javascript' src='http://example.com/wp-content/plugins/ultimate-social-media-icons/js/custom.js?ver=3.5.1'></script>
<script type='text/javascript' src='http://example.com/wp-content/themes/scrollider/includes/js/third-party.js?ver=3.5.1'></script>
<script type='text/javascript' src='http://example.com/wp-content/themes/scrollider/includes/js/general.js?ver=3.5.1'></script>
<script type='text/javascript'>
/* <![CDATA[ */
var woo_masonry_data = {"numberOfColumns":"3"};
/* ]]> */
</script>
<script type='text/javascript' src='http://example.com/wp-content/themes/scrollider/includes/js/jquery.masonry.min.js?ver=3.5.1'></script>
<script type='text/javascript' src='http://example.com/wp-content/themes/scrollider/includes/js/jquery.flexslider-min.js?ver=3.5.1'></script>
<script type='text/javascript'>
/* <![CDATA[ */
var woo_localized_data = {"animation":"fade","controlsContainer":".controls-container","smoothHeight":"true","useCSS":"true","directionNav":"true","controlNav":"true","manualControls":".manual ol li","slideshow":"true","pauseOnHover":"false","slideshowSpeed":"3000","animationDuration":"0","touch":"false"};
/* ]]> */
</script>
<script type='text/javascript' src='http://example.com/wp-content/themes/scrollider/includes/js/featured-slider.js?ver=1.2.5'></script>
<script type='text/javascript' src='http://example.com/wp-content/plugins/vslider/js/vslider.js?ver=3.5.1'></script>
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://example.com/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://example.com/wp-includes/wlwmanifest.xml" />
<meta name="generator" content="WordPress 3.5.1, fitted with the WordPress Mobile Pack 1.2.5" />
<meta name="viewport" content="width=device-width, initial-scale=1"><meta property="og:image" content="http://example.com/wp-content/uploads/2015/07/test.jpg"><meta property="og:image:type" content=""><meta property="og:image:width" content="1000"><meta property="og:image:height" content="752">
<!-- Theme version -->
<meta name="generator" content="Scrollider Child 1.0.0" />
<meta name="generator" content="Scrollider 1.2.13" />
<meta name="generator" content="WooFramework 5.5.5" />

View File

@@ -1,42 +1,42 @@
[
{
"3.5":{
"vulnerabilities":[
{
"id":2989,
"title":"I should not appear in the results",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
},
{
"3.2":{
"vulnerabilities":[
{
"id":2993,
"title":"Here I Am",
"url":"Ref 1,Ref 2",
"osvdb":"osvdb",
"cve":"2011-001",
"secunia":"secunia",
"metasploit":"exploit/ex1",
"exploitdb":"exploitdb",
"type":"SQLI",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
{
"3.5": {
"vulnerabilities":[
{
"id":2989,
"title":"I should not appear in the results",
"references": {
"url": ["Ref 1", "Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
"type":"XSS",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
},
"3.2": {
"vulnerabilities":[
{
"id":2993,
"title":"Here I Am",
"references": {
"url": ["Ref 1", "Ref 2"],
"osvdb": ["osvdb"],
"cve": ["2011-001"],
"secunia": ["secunia"],
"metasploit": ["exploit/ex1"],
"exploitdb": ["exploitdb"]
},
"type":"SQLI",
"fixed_in":"",
"created_at":"2014-07-28T12:10:07.000Z",
"updated_at":"2014-07-28T12:10:07.000Z"
}
]
}
]
}

View File

@@ -0,0 +1,17 @@
User-agent: *
Disallow: /wp-admin/
Disallow: /wp-admin/
Disallow: /wp-admin/
Disallow: /wp-admin/
Disallow: /wp-includes/
Disallow: /wordpress/admin/
Disallow: /wordpress/wp-admin/
Disallow: /wordpress/secret/
Disallow: /wordpress/secret/
Disallow: /wordpress/
Disallow: /wordpress/secret/
Disallow: /Wordpress/wp-admin/
Disallow: /wp-admin/tralling-space/
Allow: /asdf/
Sitemap: http://10.0.0.0/sitemap.xml.gz

View File

@@ -0,0 +1,9 @@
User-agent: *
Disallow: /wp-admin/
Disallow: /wp-admin/
Disallow: /wp-admin/
Disallow: /wp-admin/
Disallow: /wp-admin/
Disallow: /wp-admin/
Sitemap: http://10.0.0.0/sitemap.xml.gz

View File

@@ -61,6 +61,24 @@ shared_examples 'WebSite::RobotsTxt' do
http://example.localhost/asdf/
)
end
it 'removes duplicate entries from robots.txt test 1' do
@fixture = fixtures_dir + '/robots_txt/robots_duplicate_1.txt'
@expected = %w(
http://example.localhost/wordpress/
http://example.localhost/wordpress/admin/
http://example.localhost/wordpress/wp-admin/
http://example.localhost/wordpress/secret/
http://example.localhost/Wordpress/wp-admin/
http://example.localhost/wp-admin/tralling-space/
http://example.localhost/asdf/
)
end
it 'removes duplicate entries from robots.txt test 2' do
@fixture = fixtures_dir + '/robots_txt/robots_duplicate_2.txt'
@expected = nil
end
end
context 'installed in sub directory' do

View File

@@ -3,8 +3,8 @@
shared_examples 'WpItem::Vulnerable' do
# 2 variables have to be set in the described class or subject:
# let(:vulns_file) { }
# let(:expected_vulns) { } The expected Vulnerabilities when using vulns_file and vulns_xpath
# let(:db_file) { }
# let(:expected_vulns) { } The expected Vulnerabilities when using db_file and vulns_xpath
#
# 1 variable is optional, used if supplied, otherwise subject.vulns_xpath is used
# let(:vulns_xpath) { }
@@ -18,7 +18,7 @@ shared_examples 'WpItem::Vulnerable' do
end
after do
subject.vulns_file = @vulns_file
subject.db_file = @db_file
subject.identifier = identifier if defined?(identifier)
result = subject.vulnerabilities
@@ -26,16 +26,16 @@ shared_examples 'WpItem::Vulnerable' do
expect(result).to eq @expected
end
context 'when the vulns_file is empty' do
context 'when the db_file is empty' do
it 'returns an empty Vulnerabilities' do
@vulns_file = empty_file
@expected = Vulnerabilities.new
@db_file = empty_file
@expected = Vulnerabilities.new
end
end
it 'returns the expected vulnerabilities' do
@vulns_file = vulns_file
@expected = expected_vulns
@db_file = db_file
@expected = expected_vulns
end
end

View File

@@ -39,68 +39,8 @@ shared_examples 'WpItems::Detectable' do
end
end
describe '::targets_items_from_file' do
after do
results = subject.send(:targets_items_from_file, file, wp_target, item_class, vulns_file)
expect(results.map { |i| i.name }).to eq @expected.map { |i| i.name }
unless results.empty?
results.each do |item|
expect(item).to be_a item_class
end
end
end
# should raise error.
# context 'when an empty file' do
# let(:file) { empty_file }
# it 'returns an empty Array' do
# @expected = []
# end
# end
context 'when a file' do
let(:file) { targets_items_file }
it 'returns the expected Array of WpItem' do
@expected = expected[:targets_items_from_file]
end
end
end
describe '::vulnerable_targets_items' do
after do
results = subject.send(:vulnerable_targets_items, wp_target, item_class, vulns_file)
expect(results.map { |i| i.name }).to eq @expected.map { |i| i.name }
unless results.empty?
results.each do |item|
expect(item).to be_a item_class
end
end
end
# should raise error.
# context 'when an empty file' do
# let(:file) { empty_file }
# it 'returns an empty Array' do
# @expected = []
# end
# end
context 'when a file' do
it 'returns the expected Array of WpItem' do
@expected = expected[:vulnerable_targets_items]
end
end
end
describe '::targets_items' do
let(:options) { {} }
let(:options) { { type: :all } }
after do
if @expected
@@ -110,29 +50,13 @@ shared_examples 'WpItems::Detectable' do
end
end
context 'when :only_vulnerable' do
let(:options) { { only_vulnerable: true } }
context 'when :type = :vulnerable' do
let(:options) { { type: :vulnerable } }
it 'returns the expected Array of WpItem' do
@expected = expected[:vulnerable_targets_items]
end
end
context 'when not :only_vulnerable' do
context 'when no :file' do
it 'raises an error' do
expect { subject.send(:targets_items, wp_target, options) }.to raise_error('A file must be supplied')
end
end
context 'when :file' do
let(:options) { { file: targets_items_file } }
it 'returns the expected Array of WpItem' do
@expected = (expected[:targets_items_from_file] + expected[:vulnerable_targets_items]).uniq {|t| t.name }
end
end
end
end
describe '::passive_detection' do
@@ -176,8 +100,8 @@ shared_examples 'WpItems::Detectable' do
expect(result.sort.map { |i| i.name }).to eq @expected.sort.map { |i| i.name }
end
context 'when :only_vulnerable' do
let(:options) { { only_vulnerable: true } }
context 'when :type = :vulnerable' do
let(:options) { { type: :vulnerable } }
let(:targets) { expected[:vulnerable_targets_items] }
it 'only checks and return vulnerable targets' do
@@ -207,7 +131,7 @@ shared_examples 'WpItems::Detectable' do
end
end
context 'when no :only_vulnerable' do
context 'when no :type = :vulnerable' do
let(:targets) { (expected[:vulnerable_targets_items] + expected[:targets_items_from_file]).uniq { |t| t.name } }
it 'checks all targets, and merge the results with passive_detection' do

View File

@@ -2,25 +2,25 @@
shared_examples 'WpPlugin::Vulnerable' do
describe '#vulns_file' do
after { expect(subject.vulns_file).to eq @expected }
describe '#db_file' do
after { expect(subject.db_file).to eq @expected }
context 'when :vulns_file is no set' do
context 'when :db_file is no set' do
it 'returns the default one' do
@expected = PLUGINS_VULNS_FILE
@expected = PLUGINS_FILE
end
end
context 'when the :vulns_file is already set' do
context 'when the :db_file is already set' do
it 'returns it' do
@expected = 'test.json'
subject.vulns_file = @expected
@expected = 'test.json'
subject.db_file = @expected
end
end
end
describe '#identifier' do
its(:identifier) { is_expected.to eq 'plugin-name' }
its(:identifier) { should eq 'plugin-name' }
end
end

View File

@@ -2,25 +2,25 @@
shared_examples 'WpTheme::Vulnerable' do
describe '#vulns_file' do
after { expect(subject.vulns_file).to eq @expected }
describe '#db_file' do
after { expect(subject.db_file).to eq @expected }
context 'when :vulns_file is not set' do
context 'when :db_file is not set' do
it 'returns the default one' do
@expected = THEMES_VULNS_FILE
@expected = THEMES_FILE
end
end
context 'when the :vulns_file is already set' do
context 'when the :db_file is already set' do
it 'returns it' do
@expected = 'test.json'
subject.vulns_file = @expected
@expected = 'test.json'
subject.db_file = @expected
end
end
end
describe '#identifier' do
its(:identifier) { is_expected.to eq 'theme-name' }
its(:identifier) { should eq 'theme-name' }
end
end

View File

@@ -29,6 +29,13 @@ shared_examples 'WpUser::Existable' do
@expected = nil
end
end
context 'when no author given' do
it 'returns nil' do
@text = '<a href="http://wp.lab/author/" class="btn btn-default">See Posts</a>'
@expected = nil
end
end
end
describe '::login_from_body' do

View File

@@ -2,25 +2,25 @@
shared_examples 'WpVersion::Vulnerable' do
describe '#vulns_file' do
after { expect(subject.vulns_file).to eq @expected }
describe '#db_file' do
after { expect(subject.db_file).to eq @expected }
context 'when :vulns_file is no set' do
context 'when :db_file is no set' do
it 'returns the default one' do
@expected = WP_VULNS_FILE
@expected = WORDPRESSES_FILE
end
end
context 'when the :vulns_file is already set' do
context 'when the :db_file is already set' do
it 'returns it' do
@expected = 'test.json'
subject.vulns_file = @expected
@expected = 'test.json'
subject.db_file = @expected
end
end
end
describe '#identifier' do
its(:identifier) { is_expected.to eq '1.2' }
its(:identifier) { should eq '1.2' }
end
end

View File

@@ -62,7 +62,7 @@ def main
exit(1)
else
if missing_db_file?
puts critical('You can not run a scan without any databases.')
puts critical('You can not run a scan without any databases.')
exit(1)
end
end
@@ -133,7 +133,7 @@ def main
# Remote website is wordpress?
unless wpscan_options.force
unless wp_target.wordpress?
raise critical('The remote website is up, but does not seem to be running WordPress.')
raise 'The remote website is up, but does not seem to be running WordPress.'
end
end
@@ -273,15 +273,29 @@ def main
# Enumerate the installed plugins
if wpscan_options.enumerate_plugins or wpscan_options.enumerate_only_vulnerable_plugins or wpscan_options.enumerate_all_plugins
puts
puts info("Enumerating installed plugins #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_plugins} ...")
if wpscan_options.enumerate_only_vulnerable_plugins
puts info('Enumerating installed plugins (only ones with known vulnerabilities) ...')
plugin_enumeration_type = :vulnerable
end
if wpscan_options.enumerate_plugins
puts info('Enumerating installed plugins (only ones marked as popular) ...')
plugin_enumeration_type = :popular
end
if wpscan_options.enumerate_all_plugins
puts info('Enumerating all plugins (may take a while and use a lot of system resources) ...')
plugin_enumeration_type = :all
end
puts
wp_plugins = WpPlugins.aggressive_detection(wp_target,
enum_options.merge(
file: wpscan_options.enumerate_all_plugins ? PLUGINS_FULL_FILE : PLUGINS_FILE,
only_vulnerable: wpscan_options.enumerate_only_vulnerable_plugins || false
file: PLUGINS_FILE,
type: plugin_enumeration_type
)
)
puts
if !wp_plugins.empty?
puts info("We found #{wp_plugins.size} plugins:")
@@ -295,13 +309,26 @@ def main
# Enumerate installed themes
if wpscan_options.enumerate_themes or wpscan_options.enumerate_only_vulnerable_themes or wpscan_options.enumerate_all_themes
puts
puts info("Enumerating installed themes #{'(only vulnerable ones)' if wpscan_options.enumerate_only_vulnerable_themes} ...")
if wpscan_options.enumerate_only_vulnerable_themes
puts info('Enumerating installed themes (only ones with known vulnerabilities) ...')
theme_enumeration_type = :vulnerable
end
if wpscan_options.enumerate_themes
puts info('Enumerating installed themes (only ones marked as popular) ...')
theme_enumeration_type = :popular
end
if wpscan_options.enumerate_all_themes
puts info('Enumerating all themes (may take a while and use a lot of system resources) ...')
theme_enumeration_type = :all
end
puts
wp_themes = WpThemes.aggressive_detection(wp_target,
enum_options.merge(
file: wpscan_options.enumerate_all_themes ? THEMES_FULL_FILE : THEMES_FILE,
only_vulnerable: wpscan_options.enumerate_only_vulnerable_themes || false
file: THEMES_FILE,
type: theme_enumeration_type
)
)
puts