Compare commits

...

114 Commits
2.7 ... 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
ethicalhack3r
5902a483b4 Ready for release version 2.8 #834 2015-06-22 18:56:37 +02:00
Christian Mehlmauer
ca73e4b93e fix some code styling issues 2015-06-21 11:05:25 +02:00
Christian Mehlmauer
ace64d88ce Merge branch 'master' of github.com:wpscanteam/wpscan 2015-06-21 11:03:55 +02:00
Christian Mehlmauer
4cc9f7c8b5 merge 2015-06-21 11:03:51 +02:00
Christian Mehlmauer
f4f1390b67 fix some code styling issues 2015-06-21 10:59:57 +02:00
erwanlr
14115761f9 Uses the URI.join to determine the redirection URL - Fix #829 2015-06-18 20:48:43 +01:00
Peter
ac3409e376 Update CHANGELOG 2015-06-18 21:07:12 +02: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
Ryan Dewhurst
2657e5050f Merge pull request #830 from mrnfrancesco/fix-issue-815
Fix issue 815
2015-06-04 09:46:26 +02:00
ethicalhack3r
3d6e5b2b9e Continue if user chooses not to update + db exists 2015-06-03 16:42:23 +02:00
ethicalhack3r
bdd6b9727d Dont update if user chooses default + no DBs exist 2015-06-03 16:40:04 +02:00
Francesco Marano
6c8172c7cf Removed Time.parse('2000-01-01') expedient 2015-06-03 16:03:01 +02:00
Francesco Marano
ae5bae9899 Capitalised 'Last db update' in 'Last DB update' 2015-06-03 15:52:33 +02:00
Francesco Marano
b6bf306042 Removed unnecessary 'return' and '()' 2015-06-03 15:43:58 +02:00
Francesco Marano
9c5196dfec Added last db update to --version option (see #815) 2015-06-03 15:33:14 +02:00
Francesco Marano
3d7b8592ea Defined function to get last db update and removed redundant code 2015-06-03 15:32:34 +02:00
Christian Mehlmauer
e03f7691f2 switch to mitre 2015-05-24 09:02:26 +02:00
Christian Mehlmauer
7a54ac62d6 output path 2015-05-21 23:16:33 +02:00
Christian Mehlmauer
8db06d37d2 check if method exist 2015-05-16 08:21:32 +02:00
Christian Mehlmauer
5ee5e76544 new link types 2015-05-15 22:34:24 +02:00
Christian Mehlmauer
090cd999cb fix rspec 2015-05-12 22:36:07 +02:00
Christian Mehlmauer
50b75354e0 #796, do not swallow exit code 2015-05-12 21:51:15 +02:00
Christian Mehlmauer
c7b6b25851 removed debug output 2015-05-12 21:29:21 +02:00
Christian Mehlmauer
b931df654d fix #796 2015-05-12 21:28:12 +02:00
erwanlr
b5d5c4177d Removes potential spaces in robots.txt entries - Ref #819 2015-05-08 09:50:51 +01:00
Christian Mehlmauer
b22550ea55 fix #814 2015-05-01 22:15:58 +02:00
Christian Mehlmauer
04d50ebea5 more logic 2015-05-01 13:14:23 +02:00
Christian Mehlmauer
202180909c warn the user to update his DB files 2015-05-01 11:29:03 +02:00
erwanlr
0d806e6d74 Ignores potential non version chars in theme version detection - Fixes #816 2015-05-01 09:56:18 +01:00
erwanlr
54f31ebe7f Merge branch 'master' of github.com:wpscanteam/wpscan 2015-05-01 09:50:45 +01:00
erwanlr
227a39d2fa Updates the theme detection pattern - Ref #816 2015-05-01 09:50:20 +01:00
Christian Mehlmauer
99d8faa38b switch from gnutls to openssl 2015-04-30 23:45:10 +02:00
Christian Mehlmauer
9a7afe1549 option to hide banner 2015-04-30 21:39:03 +02:00
erwanlr
e6751e0d89 Remove potential new line at the end of .sha512 files during the update 2015-04-25 15:27:13 +01:00
ethicalhack3r
371f1df830 Remove www subdomain from wpvulndb.com link 2015-04-24 10:12:15 +02:00
Peter
8e1ba352ee Singular and plural sentences 2015-04-21 20:33:32 +02:00
ethicalhack3r
7ebfe42eb2 Install bundler gem README 2015-04-17 16:25:17 +02:00
ethicalhack3r
df514d3b9f Update to Ruby 2.2.2 2015-04-16 18:52:25 +02:00
erwanlr
acae16e7ee Adds the missing spec file - Ref #804 2015-04-15 18:38:57 +01:00
erwanlr
deb8508ea5 Updates the Theme detection pattern - Fixes #804 2015-04-15 18:37:23 +01:00
erwanlr
a4bbf41086 Forces UTF-8 encoding when enumerating usernames - Fixes #801 2015-04-11 12:26:15 +01:00
erwanlr
4fbc535b0c Increases default connect-timeout to 10s - Fixes #803 2015-04-10 16:58:21 +01:00
Ryan Dewhurst
36f6f98ce7 Merge pull request #802 from wpscanteam/remove_wpstoools
Remove wpstools #793
2015-04-10 14:29:57 +02:00
ethicalhack3r
21cc7d604c Remove wpstools #793 2015-04-10 13:43:11 +02:00
erwanlr
44207161e6 Also check for potential timed out requests when updating - Ref #797 2015-04-03 17:48:59 +01:00
erwanlr
dc20ef0754 Increases the timeout values - Ref #797 2015-04-03 17:10:07 +01:00
erwanlr
413ee7a6d3 Adds the HttpError exception - Fixes #792 2015-04-03 16:22:28 +01:00
Christian Mehlmauer
5b94714ca7 remove GHOST warning, fixes #795 2015-04-03 17:00:17 +02:00
Christian Mehlmauer
3675fe1ed7 whitespace 2015-04-03 16:45:41 +02:00
erwanlr
e074a03c40 Fixes Indentation 2015-04-03 12:29:27 +01:00
erwanlr
a7860f72a2 Merge pull request #798 from surfer190/master
Add db checksum to verbose logging during update
2015-04-03 12:25:16 +01:00
surfer190
4b587593ee Add db checksum to verbose logging during update 2015-04-03 10:27:26 +02:00
Christian Mehlmauer
0aa8a97070 additional output 2015-04-02 07:17:58 +02:00
Christian Mehlmauer
3c16f84853 even more output 2015-04-02 00:34:44 +02:00
Christian Mehlmauer
346898e549 more output 2015-04-02 00:21:53 +02:00
erwanlr
bcef4b2de7 Fixes #791 - Rogue character causing the scan of non-wordpress site to crash 2015-04-01 13:09:10 +01:00
erwanlr
e42bf7fd7c Consider the target down after 30 requests timed out requests instead of 10 - Fixes 790 2015-04-01 09:25:17 +01:00
Christian Mehlmauer
48cd0602d8 do not build gh-pages branch 2015-03-30 22:00:39 +02:00
Christian Mehlmauer
814e837ae5 No rdoc and no ri for gems 2015-03-30 21:58:28 +02:00
erwanlr
a58b34eba8 Updates request timeout values to realistic ones (and in seconds) 2015-03-30 16:08:49 +01:00
ethicalhack3r
7d790f8f79 Add blackarch to readme. Fix #789 2015-03-30 16:44:27 +02:00
106 changed files with 1916 additions and 1929 deletions

View File

@@ -1 +1 @@
2.2.1
2.2.3

View File

@@ -1,4 +1,6 @@
language: ruby
sudo: false
cache: bundler
rvm:
- 1.9.2
- 1.9.3
@@ -11,10 +13,18 @@ rvm:
- 2.1.5
- 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
# do not build gh-pages branch
branches:
except:
- gh-pages

View File

@@ -1,6 +1,89 @@
# Changelog
## Master
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.7...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
New
* Warn the user to update his DB files
* Added last db update to --version option (see #815)
* Add db checksum to verbose logging during update
* Option to hide banner
* Continue if user chooses not to update + db exists
* Don't update if user chooses default + no DBs exist
* Updates request timeout values to realistic ones (and in seconds)
Removed
* Removed `Time.parse('2000-01-01')` expedient
* Removed unnecessary 'return' and '()'
* Removed debug output
* Removed wpstools
General core
* Update to Ruby 2.2.2
* Switch to mitre
* Install bundler gem README
* Switch from gnutls to openssl
Fixed issues
* Fix #789 - Add blackarch to readme
* Fix #790 - Consider the target down after 30 requests timed out requests instead of 10
* Fix #791 - Rogue character causing the scan of non-wordpress site to crash
* Fix #792 - Adds the HttpError exception
* Fix #795 - Remove GHOST warning
* Fix #796 - Do not swallow exit code
* Fix #797 - Increases the timeout values
* Fix #801 - Forces UTF-8 encoding when enumerating usernames
* Fix #803 - Increases default connect-timeout to 10s
* Fix #804 - Updates the Theme detection pattern
* Fix #816 - Ignores potential non version chars in theme version detection
* Fix #819 - Removes potential spaces in robots.txt entries
WPScan Database Statistics:
* Total vulnerable versions: 98
* Total vulnerable plugins: 1076
* Total vulnerable themes: 361
* Total version vulnerabilities: 1104
* Total plugin vulnerabilities: 1763
* Total theme vulnerabilities: 443
## Version 2.7
Released: 2015-03-16
@@ -33,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.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,11 +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.1
- Ruby >= 1.9.2 - Recommended: 2.2.3
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
- RubyGems - Recommended: latest
- Git
@@ -104,21 +104,21 @@ If installed from Github update the code base with ```git pull```. The databases
Before Ubuntu 14.04:
sudo apt-get install libcurl4-gnutls-dev libopenssl-ruby libxml2 libxml2-dev libxslt1-dev ruby-dev
sudo apt-get install libcurl4-openssl-dev libopenssl-ruby libxml2 libxml2-dev libxslt1-dev ruby-dev
git clone https://github.com/wpscanteam/wpscan.git
cd wpscan
sudo gem install bundler && bundle install --without test
From Ubuntu 14.04:
sudo apt-get install libcurl4-gnutls-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
####Installing on Debian:
sudo apt-get install git ruby ruby-dev libcurl4-gnutls-dev make
sudo apt-get install git ruby ruby-dev libcurl4-openssl-dev make
git clone https://github.com/wpscanteam/wpscan.git
cd wpscan
sudo gem install bundler
@@ -155,12 +155,13 @@ 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.1
rvm use 2.2.1 --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
cd wpscan
gem install bundler
bundle install --without test
#### KNOWN ISSUES
@@ -310,28 +311,13 @@ Debug output...
```ruby wpscan.rb --url www.example.com --debug-output 2>debug.log```
#### WPSTOOLS ARGUMENTS
-v, --verbose Verbose output
--check-vuln-ref-urls, --cvru Check all the vulnerabilities reference urls for 404
--check-local-vulnerable-files, --clvf LOCAL_DIRECTORY Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells
-s, --stats Show WpScan Database statistics.
--spellcheck, --sc Check all files for common spelling mistakes.
#### WPSTOOLS EXAMPLES
Locally scan a wordpress installation for vulnerable files or shells:
```ruby wpstools.rb --check-local-vulnerable-files /var/www/wordpress/```
#### PROJECT HOME
[http://www.wpscan.org](http://www.wpscan.org)
#### VULNERABILITY DATABASE
[https://www.wpvulndb.com](https://www.wpvulndb.com)
[https://wpvulndb.com](https://wpvulndb.com)
#### GIT REPOSITORY

BIN
data.zip

Binary file not shown.

View File

@@ -23,7 +23,7 @@ end
html = open(html_path).read
examples = html.match(/(\d+) examples/)[0].to_i rescue 0
errors = html.match(/(\d+) errors/)[0].to_i rescue 0
if errors == 0 then
if errors == 0
errors = html.match(/(\d+) failure/)[0].to_i rescue 0
end
pending = html.match(/(\d+) pending/)[0].to_i rescue 0

View File

@@ -10,9 +10,9 @@
"cache_ttl": 600, // 10 minutes, at this time the cache is cleaned before each scan. If this value is set to 0, the cache will be disabled
"request_timeout": 2000, // 2s
"request_timeout": 60, // 1min
"connect_timeout": 1000, // 1s
"connect_timeout": 10, // 10s
"max_threads": 20
}

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,14 +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
# 2s
@request_timeout = 2000
# 1s
@connect_timeout = 1000
@user_agent = "WPScan v#{WPSCAN_VERSION} (http://wpscan.org)"
@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)"
@throttle = 0
end
#
@@ -88,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
@@ -101,7 +101,6 @@ class Browser
self.send(:"#{option_name}=", data[option_name])
end
end
end
# @param [ String ] url
@@ -123,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
@@ -138,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)
@@ -174,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

@@ -1,74 +1,75 @@
# encoding: UTF-8
require 'common/collections/wp_items/detectable'
require 'common/collections/wp_items/output'
class WpItems < Array
extend WpItems::Detectable
include WpItems::Output
attr_accessor :wp_target
# @param [ WpTarget ] wp_target
def initialize(wp_target = nil)
self.wp_target = wp_target
end
# @param [String] argv
#
# @return [ void ]
def add(*args)
index = 0
until args[index].nil?
arg = args[index]
if arg.is_a?(String)
if (next_arg = args[index + 1]).is_a?(Hash)
item = create_item(arg, next_arg)
index += 1
else
item = create_item(arg)
end
elsif arg.is_a?(Item)
item = arg
else
raise 'Invalid arguments'
end
self << item
index += 1
end
end
# @param [ String ] name
# @param [ Hash ] attrs
#
# @return [ WpItem ]
def create_item(name, attrs = {})
raise 'wp_target must be set' unless wp_target
item_class.new(
wp_target.uri,
attrs.merge(
name: name,
wp_content_dir: wp_target.wp_content_dir,
wp_plugins_dir: wp_target.wp_plugins_dir
) { |key, oldval, newval| oldval }
)
end
# @param [ WpItems ] other
#
# @return [ self ]
def +(other)
other.each { |item| self << item }
self
end
protected
# @return [ Class ]
def item_class
Object.const_get(self.class.to_s.gsub(/.$/, ''))
end
end
# encoding: UTF-8
require 'common/collections/wp_items/detectable'
require 'common/collections/wp_items/output'
class WpItems < Array
extend WpItems::Detectable
include WpItems::Output
attr_accessor :wp_target
# @param [ WpTarget ] wp_target
def initialize(wp_target = nil)
self.wp_target = wp_target
end
# @param [String] args
#
# @return [ void ]
def add(*args)
index = 0
until args[index].nil?
arg = args[index]
if arg.is_a?(String)
if (next_arg = args[index + 1]).is_a?(Hash)
item = create_item(arg, next_arg)
index += 1
else
item = create_item(arg)
end
elsif arg.is_a?(Item)
item = arg
else
raise 'Invalid arguments'
end
self << item
index += 1
end
end
# @param [ String ] name
# @param [ Hash ] attrs
#
# @return [ WpItem ]
def create_item(name, attrs = {})
raise 'wp_target must be set' unless wp_target
item_class.new(
wp_target.uri,
attrs.merge(
name: name,
wp_content_dir: wp_target.wp_content_dir,
wp_plugins_dir: wp_target.wp_plugins_dir
) { |key, oldval, newval| oldval }
)
end
# @param [ WpItems ] other
#
# @return [ self ]
def +(other)
other.each { |item| self << item }
self
end
protected
# @return [ Class ]
def item_class
Object.const_get(self.class.to_s.gsub(/.$/, ''))
end
end

View File

@@ -1,238 +1,236 @@
# encoding: UTF-8
class WpItems < Array
module Detectable
attr_reader :vulns_file, :item_xpath
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
# @option options [ Boolean ] :show_progression Whether or not output the progress bar
# @option options [ Boolean ] :only_vulnerable Only check for vulnerable items
# @option options [ String ] :exclude_content
#
# @return [ WpItems ]
def aggressive_detection(wp_target, options = {})
browser = Browser.instance
hydra = browser.hydra
targets = targets_items(wp_target, options)
progress_bar = progress_bar(targets.size, options)
queue_count = 0
exist_options = {
error_404_hash: wp_target.error_404_hash,
homepage_hash: wp_target.homepage_hash,
exclude_content: options[:exclude_content] ? %r{#{options[:exclude_content]}} : nil
}
results = passive_detection(wp_target, options)
targets.each do |target_item|
request = browser.forge_request(target_item.url, request_params)
request.on_complete do |response|
progress_bar.progress += 1 if options[:show_progression]
if target_item.exists?(exist_options, response)
if !results.include?(target_item)
if !options[:only_vulnerable] || options[:only_vulnerable] && target_item.vulnerable?
results << target_item
end
end
end
end
hydra.queue(request)
queue_count += 1
if queue_count >= browser.max_threads
hydra.run
queue_count = 0
puts "Sent #{browser.max_threads} requests ..." if options[:verbose]
end
end
# run the remaining requests
hydra.run
results.select!(&:vulnerable?) if options[:only_vulnerable]
results.sort!
results # can't just return results.sort as it would return an array, and we want a WpItems
end
# @param [ Integer ] targets_size
# @param [ Hash ] options
#
# @return [ ProgressBar ]
# :nocov:
def progress_bar(targets_size, options)
if options[:show_progression]
ProgressBar.create(
format: '%t %a <%B> (%c / %C) %P%% %e',
title: ' ', # Used to craete a left margin
total: targets_size
)
end
end
# :nocov:
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
#
# @return [ WpItems ]
def passive_detection(wp_target, options = {})
results = new(wp_target)
# improves speed
body = remove_base64_images_from_html(Browser.get(wp_target.url).body)
page = Nokogiri::HTML(body)
names = []
page.css('link,script,style').each do |tag|
%w(href src).each do |attribute|
attr_value = tag.attribute(attribute).to_s
next unless attr_value
names << Regexp.last_match[1] if attr_value.match(attribute_pattern(wp_target))
end
next unless tag.name == 'script' || tag.name == 'style'
code = tag.text.to_s
next if code.empty?
code.scan(code_pattern(wp_target)).flatten.uniq.each do |item_name|
names << item_name
end
end
names.uniq.each { |name| results.add(name) }
results.sort!
results
end
protected
# @param [ WpTarget ] wp_target
#
# @return [ Regex ]
def item_pattern(wp_target)
type = to_s.gsub(/Wp/, '').downcase
wp_content_dir = wp_target.wp_content_dir
wp_content_url = wp_target.uri.merge(wp_content_dir).to_s
url = /#{wp_content_url.gsub(%r{\A(?:http|https)}, 'https?').gsub('/', '\\\\\?\/')}/i
content_dir = %r{(?:#{url}|\\?\/\\?\/?#{wp_content_dir})}i
%r{#{content_dir}\\?/#{type}\\?/}
end
# @param [ WpTarget ] wp_target
#
# @return [ Regex ]
def attribute_pattern(wp_target)
/\A#{item_pattern(wp_target)}([^\/]+)/i
end
# @param [ WpTarget ] wp_target
#
# @return [ Regex ]
def code_pattern(wp_target)
/["'\(]#{item_pattern(wp_target)}([^\\\/\)"']+)/i
end
# The default request parameters
#
# @return [ Hash ]
def request_params; { cache_ttl: 0, followlocation: true } end
# @param [ WpTarget ] wp_target
# @param [ options ] options
# @option options [ Boolean ] :only_vulnerable
# @option options [ String ] :file The path to the file containing the targets
#
# @return [ Array<WpItem> ]
def targets_items(wp_target, options = {})
item_class = self.item_class
vulns_file = self.vulns_file
targets = vulnerable_targets_items(wp_target, item_class, vulns_file)
unless options[:only_vulnerable]
unless options[:file]
raise 'A file must be supplied'
end
targets += targets_items_from_file(options[:file], wp_target, item_class, vulns_file)
end
targets.uniq! { |t| t.name }
targets.sort_by { rand }
end
# @param [ WpTarget ] wp_target
# @param [ Class ] item_class
# @param [ String ] vulns_file
#
# @return [ Array<WpItem> ]
def vulnerable_targets_items(wp_target, item_class, vulns_file)
targets = []
json = json(vulns_file)
[*json].each do |item|
targets << create_item(
item_class,
item.keys.inject,
wp_target,
vulns_file
)
end
targets
end
# @param [ Class ] klass
# @param [ String ] name
# @param [ WpTarget ] wp_target
# @option [ String ] vulns_file
#
# @return [ WpItem ]
def create_item(klass, name, wp_target, vulns_file = nil)
klass.new(
wp_target.uri,
name: name,
vulns_file: vulns_file,
wp_content_dir: wp_target.wp_content_dir,
wp_plugins_dir: wp_target.wp_plugins_dir
)
end
# @param [ String ] file
# @param [ WpTarget ] wp_target
# @param [ Class ] item_class
# @param [ String ] vulns_file
#
# @return [ Array<WpItem> ]
def targets_items_from_file(file, wp_target, item_class, vulns_file)
targets = []
File.open(file, 'r') do |f|
f.readlines.collect do |item_name|
targets << create_item(
item_class,
item_name.strip,
wp_target,
vulns_file
)
end
end
targets
end
# @return [ Class ]
def item_class
Object.const_get(self.to_s.gsub(/.$/, ''))
end
end
end
# encoding: UTF-8
class WpItems < Array
module Detectable
attr_reader :vulns_file, :item_xpath
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
# @option options [ Boolean ] :show_progression Whether or not output the progress bar
# @option options [ Boolean ] :only_vulnerable Only check for vulnerable items
# @option options [ String ] :exclude_content
#
# @return [ WpItems ]
def aggressive_detection(wp_target, options = {})
browser = Browser.instance
hydra = browser.hydra
targets = targets_items(wp_target, options)
progress_bar = progress_bar(targets.size, options)
queue_count = 0
exist_options = {
error_404_hash: wp_target.error_404_hash,
homepage_hash: wp_target.homepage_hash,
exclude_content: options[:exclude_content] ? %r{#{options[:exclude_content]}} : nil
}
results = passive_detection(wp_target, options)
targets.each do |target_item|
request = browser.forge_request(target_item.url, request_params)
request.on_complete do |response|
progress_bar.progress += 1 if options[:show_progression]
if target_item.exists?(exist_options, response)
results << target_item unless results.include?(target_item)
end
end
hydra.queue(request)
queue_count += 1
if queue_count >= browser.max_threads
hydra.run
queue_count = 0
puts "Sent #{browser.max_threads} requests ..." if options[:verbose]
end
end
# run the remaining requests
hydra.run
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
end
# @param [ Integer ] targets_size
# @param [ Hash ] options
#
# @return [ ProgressBar ]
# :nocov:
def progress_bar(targets_size, options)
if options[:show_progression]
ProgressBar.create(
format: '%t %a <%B> (%c / %C) %P%% %e',
title: ' ', # Used to craete a left margin
total: targets_size
)
end
end
# :nocov:
# @param [ WpTarget ] wp_target
# @param [ Hash ] options
#
# @return [ WpItems ]
def passive_detection(wp_target, options = {})
results = new(wp_target)
# improves speed
body = remove_base64_images_from_html(Browser.get(wp_target.url).body)
page = Nokogiri::HTML(body)
names = []
page.css('link,script,style').each do |tag|
%w(href src).each do |attribute|
attr_value = tag.attribute(attribute).to_s
next unless attr_value
names << Regexp.last_match[1] if attr_value.match(attribute_pattern(wp_target))
end
next unless tag.name == 'script' || tag.name == 'style'
code = tag.text.to_s
next if code.empty?
code.scan(code_pattern(wp_target)).flatten.uniq.each do |item_name|
names << item_name
end
end
names.uniq.each { |name| results.add(name) }
results.sort!
results
end
protected
# @param [ WpTarget ] wp_target
#
# @return [ Regex ]
def item_pattern(wp_target)
type = to_s.gsub(/Wp/, '').downcase
wp_content_dir = wp_target.wp_content_dir
wp_content_url = wp_target.uri.merge(wp_content_dir).to_s
url = /#{wp_content_url.gsub(%r{\A(?:http|https)}, 'https?').gsub('/', '\\\\\?\/')}/i
content_dir = %r{(?:#{url}|\\?\/\\?\/?#{wp_content_dir})}i
%r{#{content_dir}\\?/#{type}\\?/}
end
# @param [ WpTarget ] wp_target
#
# @return [ Regex ]
def attribute_pattern(wp_target)
/\A#{item_pattern(wp_target)}([^\/]+)/i
end
# @param [ WpTarget ] wp_target
#
# @return [ Regex ]
def code_pattern(wp_target)
/["'\(]#{item_pattern(wp_target)}([^\\\/\)"']+)/i
end
# The default request parameters
#
# @return [ Hash ]
def request_params; { cache_ttl: 0, followlocation: true } end
# @param [ WpTarget ] wp_target
# @param [ options ] options
# @option options [ Boolean ] :only_vulnerable
# @option options [ String ] :file The path to the file containing the targets
#
# @return [ Array<WpItem> ]
def targets_items(wp_target, options = {})
item_class = self.item_class
vulns_file = self.vulns_file
targets = target_items_from_type(wp_target, item_class, vulns_file, options[:type])
targets.uniq! { |t| t.name }
targets.sort_by { rand }
end
# @param [ WpTarget ] wp_target
# @param [ Class ] item_class
# @param [ String ] vulns_file
#
# @return [ Array<WpItem> ]
def target_items_from_type(wp_target, item_class, vulns_file, type)
targets = []
json = json(vulns_file)
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,
wp_target,
vulns_file
)
end
targets
end
# @param [ Class ] klass
# @param [ String ] name
# @param [ WpTarget ] wp_target
# @option [ String ] vulns_file
#
# @return [ WpItem ]
def create_item(klass, name, wp_target, vulns_file = nil)
klass.new(
wp_target.uri,
name: name,
vulns_file: vulns_file,
wp_content_dir: wp_target.wp_content_dir,
wp_plugins_dir: wp_target.wp_plugins_dir
)
end
# @param [ String ] file
# @param [ WpTarget ] wp_target
# @param [ Class ] item_class
# @param [ String ] vulns_file
#
# @return [ Array<WpItem> ]
def targets_items_from_file(file, wp_target, item_class, vulns_file)
targets = []
File.open(file, 'r') do |f|
f.readlines.collect do |item_name|
targets << create_item(
item_class,
item_name.strip,
wp_target,
vulns_file
)
end
end
targets
end
# @return [ Class ]
def item_class
Object.const_get(self.to_s.gsub(/.$/, ''))
end
end
end

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

@@ -6,7 +6,6 @@ DATA_DIR = File.join(ROOT_DIR, 'data')
CONF_DIR = File.join(ROOT_DIR, 'conf')
CACHE_DIR = File.join(ROOT_DIR, 'cache')
WPSCAN_LIB_DIR = File.join(LIB_DIR, 'wpscan')
WPSTOOLS_LIB_DIR = File.join(LIB_DIR, 'wpstools')
UPDATER_LIB_DIR = File.join(LIB_DIR, 'updater')
COMMON_LIB_DIR = File.join(LIB_DIR, 'common')
MODELS_LIB_DIR = File.join(COMMON_LIB_DIR, 'models')
@@ -17,24 +16,19 @@ LOG_FILE = File.join(ROOT_DIR, 'log.txt')
# Plugins directories
COMMON_PLUGINS_DIR = File.join(COMMON_LIB_DIR, 'plugins')
WPSCAN_PLUGINS_DIR = File.join(WPSCAN_LIB_DIR, 'plugins') # Not used ATM
WPSTOOLS_PLUGINS_DIR = File.join(WPSTOOLS_LIB_DIR, 'plugins')
# 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')
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.7'
WPSCAN_VERSION = '2.9'
$LOAD_PATH.unshift(LIB_DIR)
$LOAD_PATH.unshift(WPSCAN_LIB_DIR)
@@ -42,7 +36,7 @@ $LOAD_PATH.unshift(MODELS_LIB_DIR)
def kali_linux?
begin
File.readlines("/etc/debian_version").grep(/^kali/i).any?
File.readlines('/etc/debian_version').grep(/^kali/i).any?
rescue
false
end
@@ -50,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|
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
@@ -80,6 +78,20 @@ def missing_db_file?
false
end
def last_update
date = nil
if File.exists?(LAST_UPDATE_FILE)
content = File.read(LAST_UPDATE_FILE)
date = Time.parse(content) rescue nil
end
date
end
def update_required?
date = last_update
(true if date.nil?) or (date < 5.days.ago)
end
# Define colors
def colorize(text, color_code)
if $COLORSWITCH
@@ -110,19 +122,21 @@ def blue(text)
end
def critical(text)
red(text)
$exit_code += 1 if defined?($exit_code) # hack for undefined var via rspec
"#{red('[!]')} #{text}"
end
def warning(text)
amber(text)
$exit_code += 1 if defined?($exit_code) # hack for undefined var via rspec
"#{amber('[!]')} #{text}"
end
def info(text)
green(text)
"#{green('[+]')} #{text}"
end
def notice(text)
blue(text)
"#{blue('[i]')} #{text}"
end
# our 1337 banner

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
@@ -36,8 +36,8 @@ class DbUpdater
url = "#{remote_file_url(filename)}.sha512"
res = Browser.get(url, request_params)
fail "Unable to get #{url}" unless res.code == 200
res.body
fail DownloadError, res if res.timed_out? || res.code != 200
res.body.chomp
end
def local_file_path(filename)
@@ -72,7 +72,7 @@ class DbUpdater
file_url = remote_file_url(filename)
res = Browser.get(file_url, request_params)
fail "Error while downloading #{file_url}" unless res.code == 200
fail DownloadError, res if res.timed_out? || res.code != 200
File.open(file_path, 'wb') { |f| f.write(res.body) }
local_file_checksum(filename)
@@ -96,6 +96,7 @@ class DbUpdater
puts ' [i] Downloading new file' if verbose
dl_checksum = download(filename)
puts " [i] Downloaded File Checksum: #{dl_checksum}" if verbose
puts " [i] Database File Checksum : #{db_checksum}" if verbose
unless dl_checksum == db_checksum
fail "#{filename}: checksums do not match"
@@ -111,5 +112,8 @@ class DbUpdater
end
end
end
# write last_update date to file
File.write(LAST_UPDATE_FILE, Time.now)
end
end

33
lib/common/errors.rb Normal file
View File

@@ -0,0 +1,33 @@
# HTTP Error
class HttpError < StandardError
attr_reader :response
# @param [ Typhoeus::Response ] response
def initialize(response)
@response = response
end
def failure_details
msg = response.effective_url
if response.code == 0 || response.timed_out?
msg += " (#{response.return_message})"
else
msg += " (status: #{response.code})"
end
msg
end
def message
"HTTP Error: #{failure_details}"
end
end
# Used in the Updater
class DownloadError < HttpError
def message
"Unable to get #{failure_details}"
end
end

View File

@@ -53,7 +53,7 @@ def puts(o = '')
temp = o.gsub(/\e\[\d+m/, '') # remove color for logging
File.open(LOG_FILE, 'a+') { |f| f.puts(temp) }
end
super(o)
end
@@ -78,7 +78,7 @@ module Terminal
class Style
@@defaults = {
:border_x => "-", :border_y => "|", :border_i => "+",
:border_x => '-', :border_y => '|', :border_i => '+',
:padding_left => 1, :padding_right => 1,
:margin_left => '',
:width => nil, :alignment => nil
@@ -102,7 +102,20 @@ class Numeric
def bytes_to_human
units = %w{B KB MB GB TB}
e = (Math.log(self)/Math.log(1024)).floor
s = "%.3f" % (to_f / 1024**e)
s = '%.3f' % (to_f / 1024**e)
s.sub(/\.?0*$/, ' ' + units[e])
end
end
# time calculations
class Fixnum
SECONDS_IN_DAY = 24 * 60 * 60
def days
self * SECONDS_IN_DAY
end
def ago
Time.now - self
end
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,21 +2,22 @@
class Vulnerability
module Output
# output the vulnerability
def output(verbose = false)
puts
puts "#{critical('[!]')} Title: #{title}"
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
if !fixed_in.nil?
puts "#{notice('[i]')} Fixed in: #{fixed_in}"
end
puts notice("Fixed in: #{fixed_in}") if fixed_in
end
end
end

View File

@@ -6,31 +6,39 @@ class Vulnerability
def url_metasploit(module_path)
# remove leading slash
module_path = module_path.sub(/^\//, '')
"http://www.rapid7.com/db/modules/#{module_path}"
"https://www.rapid7.com/db/modules/#{module_path}"
end
def url_url(url)
url
end
def url_cve(cve)
"http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-#{cve}"
def url_cve(id)
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-#{id}"
end
def url_osvdb(id)
"http://osvdb.org/#{id}"
"http://osvdb.org/show/osvdb/#{id}"
end
def url_secunia(id)
"https://secunia.com/advisories/#{id}"
"https://secunia.com/advisories/#{id}/"
end
def url_exploitdb(id)
"http://www.exploit-db.com/exploits/#{id}/"
"https://www.exploit-db.com/exploits/#{id}/"
end
def url_id(id)
"https://wpvulndb.com/vulnerabilities/#{id}"
end
def url_packetstorm(id)
"http://packetstormsecurity.com/files/#{id}/"
end
def url_securityfocus(id)
"http://www.securityfocus.com/bid/#{id}/"
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,20 +5,25 @@ 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 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('[!]')} 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?
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?
additional_output(verbose) if respond_to?(:additional_output)
if version.nil? && vulnerabilities.length > 0
puts
puts "#{warning('[+]')} We could not determine a version so all vulnerabilities are printed out"
puts warning('We could not determine a version so all vulnerabilities are printed out')
end
vulnerabilities.output

View File

@@ -22,7 +22,7 @@ class WpItem
# @return [ String ]
def to_s
item_version = self.version
"#@name#{' - v' + item_version.strip if item_version}"
"#{@name}#{' - v' + item_version.strip if item_version}"
end
# Extracts the version number from a given string/body

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

@@ -14,7 +14,7 @@ class WpTheme < WpItem
def get_parent_theme_style_url
if is_child_theme?
return style_url.sub("/#{name}/style.css", "/#@theme_template/style.css")
return style_url.sub("/#{name}/style.css", "/#{@theme_template}/style.css")
end
nil
end

View File

@@ -30,17 +30,14 @@ class WpTheme < WpItem
response = Browser.get_and_follow_location(target_uri.to_s)
# https + domain is optional because of relative links
matches = /(?:https?:\/\/[^"']+)?\/([^\/]+)\/themes\/([^"'\/]+)[^"']*\/style.css/i.match(response.body)
if matches
return new(
target_uri,
{
name: matches[2],
referenced_url: matches[0],
wp_content_dir: matches[1]
}
)
end
return unless response.body =~ %r{(?:https?://[^"']+/)?([^/\s]+)/themes/([^"'/]+)[^"']*/style.css}i
new(
target_uri,
name: Regexp.last_match[2],
referenced_url: Regexp.last_match[0],
wp_content_dir: Regexp.last_match[1]
)
end
# @param [ URI ] target_uri
@@ -50,7 +47,6 @@ class WpTheme < WpItem
body = Browser.get(target_uri.to_s).body
regexp = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?" />\s+<meta name="generator" content="WooFramework\s?([^"]+)?" />}
if matches = regexp.match(body)
woo_theme_name = matches[1]
woo_theme_version = matches[2]
@@ -58,10 +54,8 @@ class WpTheme < WpItem
return new(
target_uri,
{
name: woo_theme_name,
version: woo_theme_version
}
name: woo_theme_name,
version: woo_theme_version
)
end
end

View File

@@ -10,16 +10,16 @@ class WpTheme
theme_desc = verbose ? @theme_description : truncate(@theme_description, 100)
puts " | Style URL: #{style_url}"
puts " | Referenced style.css: #{referenced_url}" if referenced_url && referenced_url != style_url
puts " | Theme Name: #@theme_name" if @theme_name
puts " | Theme URI: #@theme_uri" if @theme_uri
puts " | Theme Name: #{@theme_name}" if @theme_name
puts " | Theme URI: #{@theme_uri}" if @theme_uri
puts " | Description: #{theme_desc}"
puts " | Author: #@theme_author" if @theme_author
puts " | Author URI: #@theme_author_uri" if @theme_author_uri
puts " | Template: #@theme_template" if @theme_template and verbose
puts " | License: #@theme_license" if @theme_license and verbose
puts " | License URI: #@theme_license_uri" if @theme_license_uri and verbose
puts " | Tags: #@theme_tags" if @theme_tags and verbose
puts " | Text Domain: #@theme_text_domain" if @theme_text_domain and verbose
puts " | Author: #{@theme_author}" if @theme_author
puts " | Author URI: #{@theme_author_uri}" if @theme_author_uri
puts " | Template: #{@theme_template}" if @theme_template and verbose
puts " | License: #{@theme_license}" if @theme_license and verbose
puts " | License URI: #{@theme_license_uri}" if @theme_license_uri and verbose
puts " | Tags: #{@theme_tags}" if @theme_tags and verbose
puts " | Text Domain: #{@theme_text_domain}" if @theme_text_domain and verbose
end
end

View File

@@ -3,7 +3,7 @@
class WpTheme < WpItem
module Versionable
def version
@version ||= Browser.get(style_url).body[%r{Version:\s*([^\s]+)}i, 1]
@version ||= Browser.get(style_url).body[%r{Version:\s*(?!trunk)([0-9a-z\.-]+)}i, 1]
end
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

@@ -5,7 +5,7 @@ class WpTimthumb < WpItem
def output(verbose = false)
puts
puts "#{info('[+]')} #{self}" #this will also output the version number if detected
puts info("#{self}") #this will also output the version number if detected
vulnerabilities.output
end

View File

@@ -15,7 +15,7 @@ class WpTimthumb < WpItem
end
def check_rce_132
return rce_132_vuln unless VersionCompare.lesser_or_equal?('1.33', version)
rce_132_vuln unless VersionCompare.lesser_or_equal?('1.33', version)
end
# Vulnerable versions : > 1.35 (or >= 2.0) and < 2.8.14
@@ -24,7 +24,7 @@ class WpTimthumb < WpItem
response = Browser.get(uri.merge('?webshot=1&src=http://' + default_allowed_domains.sample))
return rce_webshot_vuln unless response.body =~ /WEBSHOT_ENABLED == true/
rce_webshot_vuln unless response.body =~ /WEBSHOT_ENABLED == true/
end
# @return [ Array<String> ] The default allowed domains (between the 2.0 and 2.8.13)

View File

@@ -1,81 +1,81 @@
# encoding: UTF-8
require 'wp_user/existable'
require 'wp_user/brute_forcable'
class WpUser < WpItem
include WpUser::Existable
include WpUser::BruteForcable
attr_accessor :id, :login, :display_name, :password
# @return [ Array<Symbol> ]
def allowed_options; [:id, :login, :display_name, :password] end
# @return [ URI ] The uri to the author page
def uri
if id
return @uri.merge("?author=#{id}")
else
raise 'The id is nil'
end
end
# @return [ String ]
def login_url
unless @login_url
@login_url = @uri.merge('wp-login.php').to_s
# Let's check if the login url is redirected (to https url for example)
if redirection = redirection(@login_url)
@login_url = redirection
end
end
@login_url
end
def redirection(url)
redirection = nil
response = Browser.get(url)
if response.code == 301 || response.code == 302
redirection = response.headers_hash['location']
# Let's check if there is a redirection in the redirection
if other_redirection = redirection(redirection)
redirection = other_redirection
end
end
redirection
end
# @return [ String ]
def to_s
s = "#{id}"
s << " | #{login}" if login
s << " | #{display_name}" if display_name
s
end
# @param [ WpUser ] other
def <=>(other)
id <=> other.id
end
# @param [ WpUser ] other
#
# @return [ Boolean ]
def ==(other)
self === other
end
# @param [ WpUser ] other
#
# @return [ Boolean ]
def ===(other)
id === other.id && login === other.login
end
end
# encoding: UTF-8
require 'wp_user/existable'
require 'wp_user/brute_forcable'
class WpUser < WpItem
include WpUser::Existable
include WpUser::BruteForcable
attr_accessor :id, :login, :display_name, :password
# @return [ Array<Symbol> ]
def allowed_options; [:id, :login, :display_name, :password] end
# @return [ URI ] The uri to the author page
def uri
if id
@uri.merge("?author=#{id}")
else
raise 'The id is nil'
end
end
# @return [ String ]
def login_url
unless @login_url
@login_url = @uri.merge('wp-login.php').to_s
# Let's check if the login url is redirected (to https url for example)
if redirection = redirection(@login_url)
@login_url = redirection
end
end
@login_url
end
def redirection(url)
redirection = nil
response = Browser.get(url)
if response.code == 301 || response.code == 302
redirection = response.headers_hash['location']
# Let's check if there is a redirection in the redirection
if other_redirection = redirection(redirection)
redirection = other_redirection
end
end
redirection
end
# @return [ String ]
def to_s
s = "#{id}"
s << " | #{login}" if login
s << " | #{display_name}" if display_name
s
end
# @param [ WpUser ] other
def <=>(other)
id <=> other.id
end
# @param [ WpUser ] other
#
# @return [ Boolean ]
def ==(other)
self === other
end
# @param [ WpUser ] other
#
# @return [ Boolean ]
def ===(other)
id === other.id && login === other.login
end
end

View File

@@ -34,7 +34,7 @@ class WpUser < WpItem
# Generate a random one on each request
unless redirect_url
random = (0...8).map { 65.+(rand(26)).chr }.join
redirect_url = "#@uri#{random}/"
redirect_url = "#{@uri}#{random}/"
end
request = login_request(password, redirect_url)
@@ -66,7 +66,7 @@ class WpUser < WpItem
puts if options[:show_progression] # mandatory to avoid the output of the progressbar to be overriden
end
# @param [ Integer ] targets_size
# @param [ Integer ] passwords_size
# @param [ Hash ] options
#
# @return [ ProgressBar ]
@@ -109,13 +109,13 @@ class WpUser < WpItem
elsif response.body =~ /login_error/i
verbose = "\n Incorrect login and/or password."
elsif response.timed_out?
progression = "#{critical('ERROR:')} Request timed out."
progression = critical('ERROR: Request timed out.')
elsif response.code == 0
progression = "#{critical('ERROR:')} No response from remote server. WAF/IPS?"
progression = critical("ERROR: No response from remote server. WAF/IPS? (#{response.return_message})")
elsif response.code.to_s =~ /^50/
progression = "#{critical('ERROR:')} Server error, try reducing the number of threads."
progression = critical('ERROR: Server error, try reducing the number of threads.')
else
progression = "#{critical('ERROR:')} We received an unknown response for #{password}..."
progression = critical("ERROR: We received an unknown response for #{password}...")
verbose = critical(" Code: #{response.code}\n Body: #{response.body}\n")
end

View File

@@ -39,7 +39,9 @@ class WpUser < WpItem
#
# @return [ String ] The login
def self.login_from_author_pattern(text)
text[%r{/author/([^/\b]+)/?}i, 1]
return unless text =~ %r{/author/([^/\b"']+)/?}i
Regexp.last_match[1].force_encoding('UTF-8')
end
# @param [ String ] body
@@ -52,6 +54,7 @@ class WpUser < WpItem
unless login
# No Permalinks
login = body[%r{<body class="archive author author-([^\s]+)[ "]}i, 1]
login ? login.force_encoding('UTF-8') : nil
end
login

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

@@ -5,13 +5,16 @@ class WpVersion < WpItem
def output(verbose = false)
puts
puts "#{info('[+]')} WordPress version #{self.number} identified from #{self.found_from}"
puts info("WordPress version #{self.number} identified from #{self.found_from}")
vulnerabilities = self.vulnerabilities
unless vulnerabilities.empty?
puts "#{critical('[!]')} #{vulnerabilities.size} vulnerabilities identified from the version number"
if vulnerabilities.size == 1
puts critical("#{vulnerabilities.size} vulnerability identified from the version number")
else
puts critical("#{vulnerabilities.size} vulnerabilities identified from the version number")
end
vulnerabilities.output
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

@@ -54,10 +54,7 @@ class WebSite
redirected_uri = URI.parse(add_trailing_slash(add_http_protocol(url)))
if response.code == 301 || response.code == 302
redirection = response.headers_hash['location']
if redirection[0] == '/'
redirection = "#{redirected_uri.scheme}://#{redirected_uri.host}#{redirection}"
end
redirection = redirected_uri.merge(response.headers_hash['location']).to_s
return redirection if url == redirection # prevents infinite loop

View File

@@ -15,7 +15,6 @@ class WebSite
@uri.clone.merge('robots.txt').to_s
end
# Parse robots.txt
# @return [ Array ] URLs generated from robots.txt
def parse_robots_txt
@@ -29,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)
@@ -40,9 +40,9 @@ class WebSite
entries.each do |d|
begin
temp = @uri.clone
temp.path = d
temp.path = d.strip
rescue URI::Error
temp = d
temp = d.strip
end
return_object << temp.to_s
end

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,19 +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[/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

@@ -8,7 +8,7 @@ class WpTarget < WebSite
@login_protection_plugin = nil
def has_login_protection?
!login_protection_plugin().nil?
!login_protection_plugin.nil?
end
# Checks if a login protection plugin is enabled
@@ -74,7 +74,7 @@ class WpTarget < WebSite
# http://wordpress.org/extend/plugins/login-security-solution/
def has_login_security_solution_protection?
Browser.get(login_security_solution_url()).code != 404
Browser.get(login_security_solution_url).code != 404
end
def login_security_solution_url

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

@@ -10,7 +10,7 @@ class WpTarget < WebSite
#
# @return [ Boolean ]
def has_readme?
response = Browser.get(readme_url())
response = Browser.get(readme_url)
unless response.code == 404
return response.body =~ %r{wordpress}i ? true : false

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.'
@@ -113,13 +114,19 @@ end
# Hook to check if the target if down during the scan
# And have the number of requests performed to display at the end of the scan
# The target is considered down after 10 requests with status = 0
# The target is considered down after 30 requests with status = 0
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 >= 10
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,
@@ -41,7 +41,9 @@ class WpscanOptions
:cache_ttl,
:request_timeout,
:connect_timeout,
:max_threads
:max_threads,
:no_banner,
:throttle
]
attr_accessor *ACCESSOR_OPTIONS
@@ -60,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
@@ -245,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],
@@ -273,7 +280,9 @@ class WpscanOptions
['--batch', GetoptLong::NO_ARGUMENT],
['--no-color', GetoptLong::NO_ARGUMENT],
['--cookie', GetoptLong::REQUIRED_ARGUMENT],
['--log', GetoptLong::NO_ARGUMENT]
['--log', GetoptLong::NO_ARGUMENT],
['--no-banner', GetoptLong::NO_ARGUMENT],
['--throttle', GetoptLong::REQUIRED_ARGUMENT]
)
end

View File

@@ -1,138 +0,0 @@
# encoding: UTF-8
class CheckerPlugin < Plugin
def initialize
super(author: 'WPScanTeam - @erwanlr')
register_options(
['--check-vuln-ref-urls', '--cvru', 'Check all the vulnerabilities reference urls for 404'],
['--check-local-vulnerable-files LOCAL_DIRECTORY', '--clvf', 'Perform a recursive scan in the LOCAL_DIRECTORY to find vulnerable files or shells']
)
end
def run(options = {})
if options[:check_vuln_ref_urls]
check_vuln_ref_urls
end
if options[:check_local_vulnerable_files]
check_local_vulnerable_files(options[:check_local_vulnerable_files])
end
end
def check_vuln_ref_urls
vuln_ref_files = [PLUGINS_VULNS_FILE, THEMES_VULNS_FILE, WP_VULNS_FILE]
error_codes = [404, 500, 403]
not_found_regexp = %r{No Results Found|error 404|ID Invalid or Not Found}i
puts '[+] Checking vulnerabilities reference urls'
vuln_ref_files.each do |vuln_ref_file|
json = json(vuln_ref_file)
urls = []
json.each do |asset|
asset[asset.keys.inject]['vulnerabilities'].each do |url|
unless url['url'].nil?
url['url'].each do |url|
urls << url
end
end
end
end
urls.uniq!
puts "[!] No URLs found in #{vuln_ref_file}!" if urls.empty?
dead_urls = []
queue_count = 0
request_count = 0
browser = Browser.instance
hydra = browser.hydra
number_of_urls = urls.size
urls.each do |url|
request = browser.forge_request(url, { cache_ttl: 0, followlocation: true })
request_count += 1
request.on_complete do |response|
print "\r [+] Checking #{vuln_ref_file} #{number_of_urls} total ... #{(request_count * 100) / number_of_urls}% complete."
if error_codes.include?(response.code) or not_found_regexp.match(response.body)
dead_urls << url
end
end
hydra.queue(request)
queue_count += 1
if queue_count == browser.max_threads
hydra.run
queue_count = 0
end
end
hydra.run
puts
unless dead_urls.empty?
dead_urls.each { |url| puts " Not Found #{url}" }
end
end
end
def check_local_vulnerable_files(dir_to_scan)
if Dir.exist?(dir_to_scan)
xml_file = LOCAL_FILES_FILE
local_hashes = {}
file_extension_to_scan = '*.{js,php,swf,html,htm}'
print '[+] Generating local hashes ... '
Dir[File.join(dir_to_scan, '**', file_extension_to_scan)]
.select { |f| File.file?(f) }
.each do |filename|
sha1sum = Digest::SHA1.file(filename).hexdigest
if local_hashes.key?(sha1sum)
local_hashes[sha1sum] << filename
else
local_hashes[sha1sum] = [filename]
end
end
puts 'done.'
puts '[+] Checking for vulnerable files ...'
xml = xml(xml_file)
xml.xpath('//hash').each do |node|
sha1sum = node.attribute('sha1').text
if local_hashes.has_key?(sha1sum)
local_filenames = local_hashes[sha1sum]
vuln_title = node.search('title').text
vuln_filename = node.search('file').text
vuln_refrence = node.search('reference').text
puts " #{vuln_filename} found :"
puts ' | Location(s):'
local_filenames.each do |file|
puts " | - #{file}"
end
puts ' |'
puts " | Title: #{vuln_title}"
puts " | Refrence: #{vuln_refrence}" if !vuln_refrence.empty?
puts
end
end
puts 'done.'
else
puts "The supplied directory '#{dir_to_scan}' does not exist"
end
end
end

View File

@@ -1,91 +0,0 @@
# encoding: UTF-8
class CheckerSpelling < Plugin
def initialize
super(author: 'WPScanTeam - @ethicalhack3r')
register_options(['--spellcheck', '--sc', 'Check all files for common spelling mistakes.'])
end
def run(options = {})
spellcheck if options[:spellcheck]
end
def spellcheck
mistakes = 0
puts '[+] Checking for spelling mistakes'
puts
files.each do |file_name|
if File.exists?(file_name)
file = File.open(file_name, 'r')
misspellings.each_key do |misspelling|
begin
file.read.scan(/#{misspelling}/).each do |match|
mistakes += 1
puts "[MISSPELLING] File: #{file_name} Bad: #{match} Good: #{misspellings[misspelling]}"
end
rescue => e
puts "Error in #{file_name} #{e}"
next
end
end
file.close
end
end
puts
puts "[+] Found #{mistakes} spelling mistakes"
mistakes
end
def misspellings
{
/databse/i => 'database',
/whith/i => 'with',
/wich/i => 'which',
/verions/i => 'versions',
/vulnerabilitiy/i => 'vulnerability',
/unkown/i => 'unknown',
/recieved/i => 'received',
/acheive/i => 'achieve',
/wierd/i => 'weird',
/untill/i => 'until',
/alot/i => 'a lot',
/randomstorm/ => 'RandomStorm',
/wpscan/ => 'WPScan',
/Wordpress/ => 'WordPress'
}
end
def files
files = Dir['**/*'].reject {|fn| File.directory?(fn) }
ignore.each do |ignore|
files.delete_if { |data| data.match(ignore) }
end
files
end
def ignore
ignore = []
ignore << File.basename(__FILE__)
ignore << 'spec/cache/'
ignore << 'spec/spec_session/'
ignore << 'cache/'
ignore << 'coverage/'
ignore << 'wordlist-iso-8859-1'
ignore << 'log.txt'
ignore << 'debug.log'
ignore << 'wordlist.txt'
ignore
end
end

View File

@@ -1,106 +0,0 @@
# encoding: UTF-8
class StatsPlugin < Plugin
def initialize
super(author: 'WPScanTeam - Christian Mehlmauer')
register_options(
['--stats', '-s', 'Show WpScan Database statistics.']
)
end
def run(options = {})
if options[:stats]
date_wp = File.mtime(WP_VULNS_FILE)
date_plugins = File.mtime(PLUGINS_VULNS_FILE)
date_themes = File.mtime(THEMES_VULNS_FILE)
date_plugins_full = File.mtime(PLUGINS_FULL_FILE)
date_themes_full = File.mtime(THEMES_FULL_FILE)
puts "WPScan Database Statistics:"
puts "---------------------------"
puts
puts "[#] Total vulnerable versions: #{vuln_core_count}"
puts "[#] Total vulnerable plugins: #{vuln_plugin_count}"
puts "[#] Total vulnerable themes: #{vuln_theme_count}"
puts
puts "[#] Total version vulnerabilities: #{version_vulns_count}"
puts "[#] Total fixed vulnerabilities: #{fix_version_count}"
puts
puts "[#] Total plugin vulnerabilities: #{plugin_vulns_count}"
puts "[#] Total fixed vulnerabilities: #{fix_plugin_count}"
puts
puts "[#] Total theme vulnerabilities: #{theme_vulns_count}"
puts "[#] Total fixed vulnerabilities: #{fix_theme_count}"
puts
puts "[#] Total plugins to enumerate: #{total_plugins}"
puts "[#] Total themes to enumerate: #{total_themes}"
puts
puts "[+] WordPress DB modified: #{date_wp.strftime('%Y-%m-%d %H:%M:%S')}"
puts "[+] Plugins DB modified: #{date_plugins.strftime('%Y-%m-%d %H:%M:%S')}"
puts "[+] Themes DB modified: #{date_themes.strftime('%Y-%m-%d %H:%M:%S')}"
puts "[+] Enumeration plugins: #{date_plugins_full.strftime('%Y-%m-%d %H:%M:%S')}"
puts "[+] Enumeration themes: #{date_themes_full.strftime('%Y-%m-%d %H:%M:%S')}"
puts
puts "[+] Report generated: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
end
end
def vuln_core_count(file=WP_VULNS_FILE)
json(file).size
end
def vuln_plugin_count(file=PLUGINS_VULNS_FILE)
json(file).size
end
def vuln_theme_count(file=THEMES_VULNS_FILE)
json(file).size
end
def version_vulns_count(file=WP_VULNS_FILE)
asset_vulns_count(json(file))
end
def fix_version_count(file=WP_VULNS_FILE)
asset_fixed_in_count(json(file))
end
def plugin_vulns_count(file=PLUGINS_VULNS_FILE)
asset_vulns_count(json(file))
end
def fix_plugin_count(file=PLUGINS_VULNS_FILE)
asset_fixed_in_count(json(file))
end
def theme_vulns_count(file=THEMES_VULNS_FILE)
asset_vulns_count(json(file))
end
def fix_theme_count(file=THEMES_VULNS_FILE)
asset_fixed_in_count(json(file))
end
def total_plugins(file=PLUGINS_FULL_FILE)
lines_in_file(file)
end
def total_themes(file=THEMES_FULL_FILE)
lines_in_file(file)
end
def lines_in_file(file)
IO.readlines(file).size
end
def asset_vulns_count(json)
json.map { |asset| asset[asset.keys.inject]['vulnerabilities'].size }.inject(:+)
end
def asset_fixed_in_count(json)
json.map { |asset| asset[asset.keys.inject]['vulnerabilities'].map {|a| a['fixed_in'].nil? ? 0 : 1 }.inject(:+) }.inject(:+)
end
end

View File

@@ -1,20 +0,0 @@
# encoding: UTF-8
require File.expand_path(File.dirname(__FILE__) + '/../common/common_helper')
require_files_from_directory(WPSTOOLS_LIB_DIR)
require_files_from_directory(WPSTOOLS_PLUGINS_DIR, '**/*.rb')
def usage
script_name = $0
puts
puts '-h for further help.'
puts
puts 'Examples:'
puts
puts 'Locally scan a wordpress installation for vulnerable files or shells'
puts "ruby #{script_name} --check-local-vulnerable-files /var/www/wordpress/"
puts
puts 'See README for further information.'
puts
end

View File

@@ -64,7 +64,7 @@ describe Browser do
it 'raises an error' do
File.symlink('./testfile', config_file)
expect { browser.load_config(config_file) }.to raise_error("[ERROR] Config file is a symlink.")
expect { browser.load_config(config_file) }.to raise_error('[ERROR] Config file is a symlink.')
File.unlink(config_file)
end
end
@@ -130,7 +130,7 @@ describe Browser do
headers: { 'User-Agent' => 'SomeUA' },
ssl_verifypeer: false, ssl_verifyhost: 0,
cookiejar: cookie_jar, cookiefile: cookie_jar,
timeout: 2000, connecttimeout: 1000,
timeout: 60, connecttimeout: 10,
maxredirs: 3,
referer: nil
}

View File

@@ -92,7 +92,7 @@ describe CacheFileStore do
it 'should create a unique storage dir' do
storage_dirs = []
(1..5).each do |i|
(1..5).each do |_|
storage_dirs << CacheFileStore.new(cache_dir).storage_path
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

@@ -17,10 +17,9 @@ describe 'WpTheme::Findable' do
wp_theme = WpTheme.send(:find_from_css_link, uri)
if @expected
expect(wp_theme).to be_a WpTheme
end
expect(wp_theme).to be_a WpTheme if @expected
expect(wp_theme).to eq @expected
expect(wp_theme.wp_content_dir).to eql 'wp-content' if @expected
end
context 'when theme is not present' do
@@ -59,6 +58,13 @@ describe 'WpTheme::Findable' do
end
end
# This one might introduce FP btw
context 'when leaked from comments' do
it 'returns the WpTheme' do
@file = 'comments.html'
@expected = WpTheme.new(uri, name: 'debug')
end
end
end
describe '::find_from_wooframework' do
@@ -96,7 +102,6 @@ describe 'WpTheme::Findable' do
@expected = WpTheme.new(uri, name: 'Editorial', version: '1.3.5')
end
end
end
describe '::find' do
@@ -109,7 +114,6 @@ describe 'WpTheme::Findable' do
context 'when a method is named s_find_from_s' do
it 'does not call it' do
class WpTheme
module Findable
extend self
@@ -117,7 +121,7 @@ describe 'WpTheme::Findable' do
end
end
stub_all_to_nil()
stub_all_to_nil
expect { WpTheme.find(uri) }.to_not raise_error
end
@@ -125,7 +129,7 @@ describe 'WpTheme::Findable' do
context 'when the theme is not found' do
it 'returns nil' do
stub_all_to_nil()
stub_all_to_nil
expect(WpTheme.find(uri)).to be_nil
end
@@ -133,7 +137,7 @@ describe 'WpTheme::Findable' do
context 'when the theme is found' do
it 'returns it, with the :found_from set' do
stub_all_to_nil()
stub_all_to_nil
stub_request(:get, /.+\/the-oracle\/style.css$/).to_return(status: 200)
expected = WpTheme.new(uri, name: 'the-oracle')

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
@@ -178,7 +183,7 @@ describe 'WpVersion::Findable' do
context 'when no version found' do
it 'returns nil' do
stub_all_to_nil()
stub_all_to_nil
@expected = nil
end
end
@@ -188,8 +193,8 @@ describe 'WpVersion::Findable' do
found_from = method[/^find_from_(.*)/, 1].sub('_', ' ')
context "when found from #{found_from}" do
it "returns the correct WpVersion" do
stub_all_to_nil()
it 'returns the correct WpVersion' do
stub_all_to_nil
allow(WpVersion).to receive(method).and_return(number)

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

@@ -17,7 +17,7 @@ describe 'WebSite' do
)
end
describe "#new" do
describe '#new' do
its(:url) { is_expected.to be === 'http://example.localhost/' }
end
@@ -68,14 +68,14 @@ describe 'WebSite' do
describe '#xml_rpc_url' do
it 'returns the xmlrpc url' do
expect(web_site.xml_rpc_url).to be === "http://example.localhost/xmlrpc.php"
expect(web_site.xml_rpc_url).to be === 'http://example.localhost/xmlrpc.php'
end
end
describe '#has_xml_rpc?' do
it 'returns true' do
stub_request(:get, web_site.xml_rpc_url).
to_return(status: 200, body: "XML-RPC server accepts POST requests only")
to_return(status: 200, body: 'XML-RPC server accepts POST requests only')
expect(web_site).to have_xml_rpc
end
@@ -116,12 +116,24 @@ describe 'WebSite' do
expect(web_site.redirection).to eql absolute_location
end
context 'when starts with a ?' do
it 'returns the absolute URI' do
relative_location = '?p=blog'
absolute_location = web_site.uri.merge(relative_location).to_s
stub_request(:get, web_site.url).to_return(status: 301, headers: { location: relative_location })
stub_request(:get, absolute_location)
expect(web_site.redirection).to eql absolute_location
end
end
end
context 'when multiple redirections' do
it 'returns the last redirection' do
first_redirection = 'www.redirection.com'
last_redirection = 'redirection.com'
first_redirection = 'http://www.redirection.com'
last_redirection = 'http://redirection.com'
stub_request(:get, web_site.url).to_return(status: 301, headers: { location: first_redirection })
stub_request(:get, first_redirection).to_return(status: 302, headers: { location: last_redirection })

View File

@@ -149,7 +149,7 @@ describe WpTarget do
after :each do
allow(wp_target).to receive_messages(wp_content_dir: 'wp-content')
stub_request_to_fixture(url: wp_target.debug_log_url(), fixture: @fixture)
stub_request_to_fixture(url: wp_target.debug_log_url, fixture: @fixture)
expect(wp_target.has_debug_log?).to be === @expected
end

View File

@@ -1,47 +0,0 @@
# encoding: UTF-8
require File.expand_path(File.dirname(__FILE__) + '/../../wpstools_helper')
describe 'StatsPlugin' do
subject(:stats) { StatsPlugin.new }
let(:plugins_vulns) { MODELS_FIXTURES + '/wp_plugin/vulnerable/plugins_vulns.json' }
let(:themes_vulns) { MODELS_FIXTURES + '/wp_theme/vulnerable/themes_vulns.json' }
let(:plugins_file) { COLLECTIONS_FIXTURES + '/wp_plugins/detectable/targets.txt' }
let(:themes_file) { COLLECTIONS_FIXTURES + '/wp_themes/detectable/targets.txt'}
describe '#vuln_plugin_count' do
it 'returns the correct number' do
expect(stats.vuln_plugin_count(plugins_vulns)).to eq 2
end
end
describe '#vuln_theme_count' do
it 'returns the correct number' do
expect(stats.vuln_theme_count(themes_vulns)).to eq 2
end
end
describe '#plugin_vulns_count' do
it 'returns the correct number' do
expect(stats.plugin_vulns_count(plugins_vulns)).to eq 3
end
end
describe '#theme_vulns_count' do
it 'returns the correct number' do
expect(stats.theme_vulns_count(themes_vulns)).to eq 3
end
end
describe '#total_plugins' do
it 'returns the correct numer' do
expect(stats.total_plugins(plugins_file)).to eq 3
end
end
describe '#total_themes' do
it 'returns the correct numer' do
expect(stats.total_themes(themes_file)).to eq 3
end
end
end

View File

@@ -1,4 +0,0 @@
# encoding: UTF-8
require 'spec_helper'
require WPSTOOLS_LIB_DIR + '/wpstools_helper'

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

@@ -0,0 +1,11 @@
<script type='text/javascript' src="http://wp.lab/wp-content/themes/debug/scripts/debug.js"></script>
<!-- W3 Total Cache: Minify debug info:
Engine: apc
Theme: 88e17
Template: page-home
Replaced CSS files:
1. wp-content/themes/debug/style.css
2. wp-content/themes/debug/css/responsive.css
-->

View File

@@ -4,7 +4,7 @@ Theme URI: http://wordpress.org/extend/themes/twentyeleven
Author: the WordPress team
Author URI: http://wordpress.org/
Description: The 2011 theme for WordPress is sophisticated, lightweight, and adaptable. Make it yours with a custom menu, header image, and background -- then go further with available theme options for light or dark color scheme, custom link colors, and three layout choices. Twenty Eleven comes equipped with a Showcase page template that transforms your front page into a showcase to show off your best content, widget support galore (sidebar, three footer areas, and a Showcase page widget area), and a custom "Ephemera" widget to display your Aside, Link, Quote, or Status posts. Included are styles for print and for the admin editor, support for featured images (as custom header images on posts and pages and as large images on featured "sticky" posts), and special styles for six different post formats.
Version: 1.3
Version: 1.3"
License: GNU General Public License
License URI: license.txt
Tags: dark, light, white, black, gray, one-column, two-columns, left-sidebar, right-sidebar, fixed-width, flexible-width, custom-background, custom-colors, custom-header, custom-menu, editor-style, featured-image-header, featured-images, full-width-template, microformats, post-formats, rtl-language-support, sticky-post, theme-options, translation-ready

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,10 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="renderer" content="webkit">
<title>一路疯下去</title>
<link rel="profile" href="http://gmpg.org/xfn/11" />
<link rel="pingback" href="http://wp.lab/xmlrpc.php" />
<link rel="canonical" href="http://wp.lab/author/一路疯下去/">
<body class="archive author author-78">

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

@@ -1,7 +1,7 @@
{
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0",
"cache_ttl": 600,
"request_timeout": 2000,
"connect_timeout": 1000,
"request_timeout": 60,
"connect_timeout": 10,
"max_threads": 20
}

View File

@@ -2,6 +2,6 @@
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:10.0) Gecko/20100101 Firefox/11.0",
"proxy": "127.0.0.1:3038",
"cache_ttl": 300,
"request_timeout": 2000,
"connect_timeout": 1000
"request_timeout": 60,
"connect_timeout": 10
}

View File

@@ -3,6 +3,6 @@
"proxy": "127.0.0.1:3038",
"proxy_auth": "user:pass",
"cache_ttl": 300,
"request_timeout": 2000,
"connect_timeout": 1000
"request_timeout": 60,
"connect_timeout": 10
}

View File

@@ -5,6 +5,7 @@ Disallow: /wordpress/admin/
Disallow: /wordpress/wp-admin/
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,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

@@ -5,7 +5,7 @@ shared_examples 'WebSite::RobotsTxt' do
describe '#robots_url' do
it 'returns the correct url' do
expect(web_site.robots_url).to be === 'http://example.localhost/robots.txt'
expect(web_site.robots_url).to eql 'http://example.localhost/robots.txt'
end
end
@@ -57,9 +57,28 @@ shared_examples 'WebSite::RobotsTxt' do
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 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
@@ -70,6 +89,7 @@ shared_examples 'WebSite::RobotsTxt' do
http://example.localhost/wordpress/admin/
http://example.localhost/wordpress/secret/
http://example.localhost/Wordpress/wp-admin/
http://example.localhost/wp-admin/tralling-space/
http://example.localhost/asdf/
)
stub_request_to_fixture(url: web_site_sub.robots_url, fixture: fixture)

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

@@ -40,7 +40,7 @@ shared_examples 'WpTarget::WpRegistrable' do
end
it 'returns true' do
@stub = { status: 200, body: %{<form id="setupform" method="post" action="wp-signup.php">} }
@stub = { status: 200, body: '<form id="setupform" method="post" action="wp-signup.php">'}
@expected = true
end
end
@@ -54,7 +54,7 @@ shared_examples 'WpTarget::WpRegistrable' do
end
it 'returns true' do
@stub = { status: 200, body: %{<form name="registerform" id="registerform" action="wp-login.php"} }
@stub = { status: 200, body: '<form name="registerform" id="registerform" action="wp-login.php"'}
@expected = true
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

Some files were not shown because too many files have changed in this diff Show More