Compare commits

..

122 Commits
2.9 ... 2.9.2

Author SHA1 Message Date
ethicalhack3r
2bff063805 More changelog info 2016-11-15 20:51:38 +01:00
ethicalhack3r
53d9956829 Update data.zip 2016-11-15 20:37:54 +01:00
ethicalhack3r
6e98678c3c Bump wpscan version 2016-11-15 20:37:07 +01:00
ethicalhack3r
f0f21f5ac2 Add stats to changelog 2016-11-15 20:35:48 +01:00
ethicalhack3r
aa233b1c4d Add total vuln stats 2016-11-15 20:34:55 +01:00
ethicalhack3r
93f9123f45 Document missing options 2016-11-15 20:17:09 +01:00
ethicalhack3r
5c710d88e4 Update changelog 2016-11-15 20:00:54 +01:00
ethicalhack3r
ded70ff743 add R symbol 2016-11-08 14:03:33 +01:00
Christian Mehlmauer
9df7443aa4 color 2016-11-02 22:23:00 +01:00
Christian Mehlmauer
8362975691 apt tweak 2016-11-02 21:52:14 +01:00
Christian Mehlmauer
49771419ae Merge branch 'master' of github.com:wpscanteam/wpscan 2016-11-01 19:39:24 +01:00
Christian Mehlmauer
d344f84824 remove cloudflare error handling 2016-11-01 19:38:47 +01:00
Christian Mehlmauer
89c0b8d4d0 Merge pull request #1019 from wpscanteam/hash
remove scripts before calculating hashes
2016-10-26 11:48:13 +02:00
Christian Mehlmauer
3c74ee8d97 remove scripts before calculating hashes 2016-10-25 20:44:00 +02:00
ethicalhack3r
785c6efa5b Fix typo 2016-10-14 14:52:54 +02:00
ethicalhack3r
4e2bf5322e Markdown formating 2016-10-14 14:51:40 +02:00
ethicalhack3r
54ed148c87 Add passive detection of google-universal-analytics 2016-10-14 14:48:48 +02:00
Christian Mehlmauer
b08e298eba Merge branch 'master' of github.com:wpscanteam/wpscan 2016-10-06 20:35:44 +02:00
Christian Mehlmauer
89e2088357 fix #1008 2016-10-06 20:35:29 +02:00
ethicalhack3r
f3cc35bd74 trademark update 2016-09-08 09:39:52 +02:00
Christian Mehlmauer
a007d283e5 rspecs 2016-09-05 23:25:33 +02:00
Christian Mehlmauer
70902aa013 Merge branch 'master' of github.com:wpscanteam/wpscan 2016-09-05 22:59:14 +02:00
Christian Mehlmauer
91151fc53b check for ssl related errors. Fix #993 2016-09-05 22:58:56 +02:00
Christian Mehlmauer
d4ee82dac5 Update README.md 2016-08-17 18:31:35 +02:00
Christian Mehlmauer
88d3c26113 moar rspecs 2016-08-16 21:40:19 +02:00
Christian Mehlmauer
054a4ee6aa fix #984 2016-08-16 21:20:29 +02:00
ethicalhack3r
c291022753 Improve yoast seo pasive detection regex #984 2016-08-16 17:20:52 +02:00
Christian Mehlmauer
2fc488b602 rework readme 2016-08-15 00:25:46 +02:00
Christian Mehlmauer
009ddd690e verbose update 2016-08-13 12:52:33 +02:00
Christian Mehlmauer
88b5cd8751 readme 2016-08-13 10:30:06 +02:00
Christian Mehlmauer
cfd19d02b1 readme 2016-08-13 10:29:28 +02:00
Christian Mehlmauer
19ce30d862 trigger docker build 2016-08-13 10:27:52 +02:00
Christian Mehlmauer
c6df6e0e89 move docker stuff 2016-08-13 10:24:02 +02:00
Christian Mehlmauer
e942a5bcf6 Exit on exceptions 2016-08-12 23:56:36 +02:00
Christian Mehlmauer
c0f5163d07 handle null 2016-08-12 21:50:59 +02:00
Christian Mehlmauer
f5aa9f117f fix #968 2016-08-12 21:29:05 +02:00
Christian Mehlmauer
498d93377d rvm install instructions 2016-08-12 21:25:45 +02:00
Christian Mehlmauer
52242e706b Merge branch 'master' of github.com:wpscanteam/wpscan 2016-08-12 20:55:20 +02:00
Christian Mehlmauer
22d69a1bf9 more detailed update exception 2016-08-12 20:54:24 +02:00
Ryan Dewhurst
0b1fa13696 Merge pull request #973 from pierre-dargham/feature_option_cache
Enable --cache-dir option in command line parameters, which solves write permission issues when wpscan is installed in system or root-owned directories
2016-08-12 12:16:11 +02:00
Christian Mehlmauer
19b15b5327 travis 2016-08-08 22:35:20 +02:00
Christian Mehlmauer
e63e96f5ed travis 2016-08-08 22:04:42 +02:00
Christian Mehlmauer
e8ac8f26a7 travis 2016-08-08 22:00:52 +02:00
Christian Mehlmauer
13e4327de4 travis 2016-08-08 21:57:38 +02:00
Christian Mehlmauer
c22a1ed12a travis 2016-08-08 21:55:40 +02:00
Christian Mehlmauer
be5662b5f1 travis 2016-08-08 21:52:30 +02:00
Christian Mehlmauer
6e840ca920 fix #974 2016-08-08 21:40:36 +02:00
Pierre Dargham
8492190f4c Allow --cache-dir option in command line parameters 2016-08-05 10:56:40 +02:00
Christian Mehlmauer
93ab6ee2a0 fucking specs 2016-08-01 22:13:38 +02:00
Christian Mehlmauer
7075e01886 Merge branch 'master' of github.com:wpscanteam/wpscan 2016-08-01 22:07:47 +02:00
Christian Mehlmauer
436a83434c fix #972 2016-08-01 22:04:13 +02:00
pvdl
d270391b56 Fix for missing 'zlib.h' in Nokogiri 2016-07-26 19:43:45 +02:00
Christian Mehlmauer
7f2762eb6f new options 2016-07-21 21:27:21 +02:00
Christian Mehlmauer
2cc5bb0311 fix rspecs 2016-07-21 13:57:18 +02:00
Christian Mehlmauer
d697127261 set user agent globally 2016-07-21 13:21:07 +02:00
Christian Mehlmauer
825523a851 changelog 2016-06-27 16:07:40 +02:00
Christian Mehlmauer
0f3f9cac33 more info 2016-06-24 21:17:43 +02:00
ethicalhack3r
f9b545b100 Clearer instructions 2016-06-23 13:40:15 +02:00
Christian Mehlmauer
943bfc39b3 fix for #957 2016-06-14 03:30:17 +02:00
Ryan Dewhurst
b1a8f445c6 Merge pull request #950 from anthraxx/master
bump terminal-table to 1.6.0 and drop workaround
2016-06-07 09:54:42 +02:00
anthraxx
5435df4345 bump terminal-table to 1.6.0 and drop workaround 2016-06-06 19:28:40 +02:00
ethicalhack3r
8e9d29e94f Update dependencies #939 2016-06-02 11:21:07 +02:00
ethicalhack3r
1afa761f09 RandomStorm is no more 2016-06-02 11:09:10 +02:00
Ryan Dewhurst
d626913ce9 Merge pull request #949 from wpscanteam/finders
more advanced version detection
2016-06-02 11:04:38 +02:00
ethicalhack3r
9c52e4a5ee Update dependencies #939 2016-06-02 11:03:07 +02:00
Christian Mehlmauer
72c2c1992b rspec fixed 2016-05-31 15:23:34 +02:00
Christian Mehlmauer
e1b4b5e8e5 typo 2016-05-31 14:53:50 +02:00
Christian Mehlmauer
0243522854 more advanced version detection 2016-05-31 14:51:09 +02:00
Christian Mehlmauer
5118c68f45 fix #943 2016-05-13 21:23:22 +02:00
Christian Mehlmauer
442884b5c5 remove executable flags 2016-05-09 16:19:11 +02:00
Christian Mehlmauer
f832e27b49 correct stats an correct data files 2016-05-06 11:52:05 +02:00
ethicalhack3r
6ce29f73c5 Update with correct stat #935 2016-05-06 11:35:57 +02:00
ethicalhack3r
920338fb62 Prepare 2.9.1 release #935 2016-05-06 00:15:53 +02:00
Christian Mehlmauer
49d0a9e6d9 check directory listing in wp-includes 2016-05-05 00:01:52 +02:00
Christian Mehlmauer
fe401e622b add stats 2016-05-04 23:09:00 +02:00
Christian Mehlmauer
6e32cb0db2 changelog 2016-05-04 22:46:02 +02:00
Ryan Dewhurst
73171eb39d Merge pull request #929 from wpscanteam/wp_metadata
WP Metadata Integration
2016-04-28 14:35:43 +02:00
ethicalhack3r
2e05f4171e Update to Ruby 2.3.1 2016-04-28 14:04:54 +02:00
Christian Mehlmauer
75b8c303e2 more verbose error 2016-04-27 15:19:07 +02:00
Christian Mehlmauer
bd7a493f1c travis errors 2016-04-20 20:49:17 +02:00
Christian Mehlmauer
9dada7c8f4 travis errors 2016-04-20 20:41:46 +02:00
ethicalhack3r
fe7aede458 Better output 2016-04-20 13:39:05 +02:00
ethicalhack3r
cdf2b38780 Only show changelog if verbose 2016-04-20 13:09:02 +02:00
ethicalhack3r
a09dbab6a8 Use db_file 2016-04-20 12:43:56 +02:00
ethicalhack3r
49a6d275d2 Update comment 2016-04-20 12:37:46 +02:00
ethicalhack3r
8192a4a215 Fix typo 2016-04-20 12:27:09 +02:00
ethicalhack3r
1d6593fd4d Add WP metadata #704 2016-04-20 12:02:15 +02:00
Christian Mehlmauer
bf99e31e70 higher update timeout 2016-04-20 09:33:56 +02:00
Christian Mehlmauer
5386496bdc move wordpress check to the top 2016-04-06 14:13:56 +02:00
Christian Mehlmauer
6451510449 new ruby version with security bugfixes released 2016-04-03 00:34:52 +02:00
Christian Mehlmauer
cd68aa719c possible fix for timeouts 2016-04-01 11:52:13 +02:00
Christian Mehlmauer
b328dc4ff9 possible fix for #912 2016-03-11 09:28:42 +01:00
Christian Mehlmauer
1e1c79aa56 Merge pull request #909 from wpscanteam/ruby_version
drop ruby 1.9 and 2.0 support, whitespaces
2016-02-26 14:08:38 +01:00
Christian Mehlmauer
08650ce156 fix travis 2016-02-25 06:39:47 +01:00
Christian Mehlmauer
a1929719f3 version 2.1.8 minimum requirement 2016-02-24 23:48:50 +01:00
Christian Mehlmauer
d34da72cd3 ruby 2.0.0 is EOL 2016-02-24 23:41:32 +01:00
Christian Mehlmauer
816b18b604 drop ruby 1.9 support, whitespaces 2016-02-23 18:07:20 +01:00
Christian Mehlmauer
a78a13bf3f revert change 2016-02-18 00:02:55 +01:00
Christian Mehlmauer
33f8aaf1dc Merge branch 'master' of github.com:wpscanteam/wpscan 2016-02-17 23:30:45 +01:00
Christian Mehlmauer
26ab95d822 more actual gems 2016-02-17 23:30:28 +01:00
erwanlr
cea01d8aa0 Improves brute forcer output to avoid confustions 2016-02-13 16:44:29 +00:00
Ryan Dewhurst
0e61f1e284 Merge pull request #901 from wpscanteam/new_urls
add new urls
2016-02-06 22:26:25 +01:00
Christian Mehlmauer
ddef061b90 add new urls 2016-02-05 22:25:18 +01:00
erwanlr
addeab8947 Fixes #900 2016-02-04 20:37:13 +01:00
erwanlr
55dc665404 Better specs 2016-01-11 16:33:29 +00:00
erwanlr
8f8538e9e9 Changes the order of the WP version from stylesheets check - Fixes #865 2016-01-11 16:27:22 +00:00
Christian Mehlmauer
348ca55bee copyright 2016-01-08 23:54:04 +01:00
Christian Mehlmauer
1bb5bc7f33 fix rspec 2016-01-03 21:28:02 +01:00
ethicalhack3r
3be5e1fcf5 Add Windows OS detection 2016-01-03 20:15:11 +01:00
Christian Mehlmauer
9df8cc9243 Update README.md 2016-01-02 10:57:55 +01:00
Christian Mehlmauer
e28c84aa34 Update fedore install instructions
See #886
2016-01-02 10:52:23 +01:00
Christian Mehlmauer
7db6b54761 Merge pull request #894 from nonmadden/update-ruby
Update to Ruby 2.3.0
2015-12-31 10:22:47 +01:00
nonmadden
e3a06f5694 Update to Ruby 2.3.0 2015-12-31 10:41:04 +07:00
erwanlr
7c5d15e098 Updates Nokogiri dep 2015-12-18 18:59:32 +01:00
ethicalhack3r
d683c0f151 Update to Ruby 2.2.4 2015-12-18 11:13:41 +01:00
erwanlr
1e67fa26ff Fixes #890 2015-11-26 14:12:04 +00:00
erwanlr
0ae6ef59ec Fixes an issue with --cache-ttl being a Strig instead of an integer 2015-11-26 13:52:12 +00:00
erwanlr
e27ef40e0f Updates Nokogiri dep version 2015-11-26 11:53:13 +00:00
ethicalhack3r
380760d028 Onlt shoe theme description when there is one 2015-10-26 16:06:13 +01:00
ethicalhack3r
18cfdafc19 Fix typo in options 2015-10-15 16:28:42 +02:00
ethicalhack3r
0934a2e329 Recommend RVM in readme 2015-10-15 15:51:38 +02:00
ethicalhack3r
d1a320324e Update reame CLI options 2015-10-15 15:49:18 +02:00
102 changed files with 1243 additions and 1012 deletions

18
.dockerignore Normal file
View File

@@ -0,0 +1,18 @@
git/
bundle/
.idea/
.yardoc/
cache/
coverage/
spec/
dev/
.*
**/*.md
*.md
Dockerfile
**/*.orig
*.orig
CREDITS
data.zip
DISCLAIMER.txt
example.conf.json

View File

@@ -1 +1 @@
2.2.3 2.3.1

View File

@@ -2,28 +2,22 @@ language: ruby
sudo: false sudo: false
cache: bundler cache: bundler
rvm: rvm:
- 1.9.2 - 2.1.9
- 1.9.3
- 2.0.0
- 2.1.0
- 2.1.1
- 2.1.2
- 2.1.3
- 2.1.4
- 2.1.5
- 2.2.0 - 2.2.0
- 2.2.1 - 2.2.1
- 2.2.2 - 2.2.2
- 2.2.3 - 2.2.3
- 2.2.4
- 2.3.0
- 2.3.1
before_install: before_install:
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc" - "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
before_script:
- "unzip -o $TRAVIS_BUILD_DIR/data.zip -d $TRAVIS_BUILD_DIR"
script: bundle exec rspec script: bundle exec rspec
notifications: notifications:
email: email:
- team@wpscan.org - team@wpscan.org
matrix:
allow_failures:
- rvm: 1.9.2
# do not build gh-pages branch # do not build gh-pages branch
branches: branches:
except: except:

View File

@@ -1,6 +1,48 @@
# Changelog # Changelog
## Master ## Master
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.9...master) [Work in progress](https://github.com/wpscanteam/wpscan/compare/2.9.2...master)
## Version 2.9.2
Released: 2016-11-15
* Fixed error when detecting plugins with UTF-8 characters
* Use all possible finders to verify a detected version
* Fix error when detecting a WordPress version not in our database
* Added some additional clarification on error messages
* Upgrade terminal-table gem
* Add --cache-dir option
* Add --disable-tls-checks options
* Improve/add additional plugin passive detections
* Remove scripts when calculating page hashes
* Many other small bug fixes.
WPScan Database Statistics:
* Total tracked wordpresses: 194
* Total tracked plugins: 63703
* Total tracked themes: 13835
* Total vulnerable wordpresses: 177
* Total vulnerable plugins: 1382
* Total vulnerable themes: 379
* Total wordpress vulnerabilities: 2617
* Total plugin vulnerabilities: 2190
* Total theme vulnerabilities: 452
## Version 2.9.1
Released: 2016-05-06
* Update to Ruby 2.3.1, drop older ruby support
* New data file location
* Added experimental Windows support
* Display WordPress metadata on the detected version
* Several small fixes
WPScan Database Statistics:
* Total vulnerable versions: 156
* Total vulnerable plugins: 1324
* Total vulnerable themes: 376
* Total version vulnerabilities: 1998
* Total plugin vulnerabilities: 2057
* Total theme vulnerabilities: 449
## Version 2.9 ## Version 2.9
Released: 2015-10-15 Released: 2015-10-15
@@ -137,7 +179,7 @@ New
* Add Sucuri sponsor to banner * Add Sucuri sponsor to banner
* Add protocol to sucuri url in banner * Add protocol to sucuri url in banner
* Add response code to proxy error output * Add response code to proxy error output
* Add a statement about mendatory newlines at the end of list * Add a statement about mandatory newlines at the end of list
* Give warning if default username 'admin' is still used * Give warning if default username 'admin' is still used
* License amendment to make it more clear about value added usage * License amendment to make it more clear about value added usage
@@ -493,4 +535,3 @@ Fixed issues
## Version 2.1 ## Version 2.1
Released 2013-3-4 Released 2013-3-4

24
Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
FROM ruby:2.3-slim
MAINTAINER WPScan Team <team@wpscan.org>
RUN DEBIAN_FRONTEND=noninteractive && \
rm -rf /var/lib/apt/lists/* && \
apt-get update && \
apt-get --no-install-recommends -qq -y install curl git ca-certificates openssl libcurl4-openssl-dev libxml2 libxml2-dev libxslt1-dev build-essential procps
RUN useradd -d /wpscan wpscan
RUN echo "gem: --no-ri --no-rdoc" > /etc/gemrc
RUN mkdir /wpscan
COPY . /wpscan
WORKDIR /wpscan
RUN bundle install --without test
RUN chown -R wpscan:wpscan /wpscan
USER wpscan
RUN /wpscan/wpscan.rb --update --verbose --no-color
ENTRYPOINT ["/wpscan/wpscan.rb"]
CMD ["--help"]

View File

@@ -1,12 +1,10 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem 'typhoeus', '~>0.8.0' gem 'typhoeus', '>=1.0.0'
gem 'nokogiri' gem 'nokogiri', '>=1.6.7.2'
gem 'addressable' gem 'addressable'
gem 'yajl-ruby' # Better JSON parser regarding memory usage gem 'yajl-ruby' # Better JSON parser regarding memory usage
# TODO: update the below when terminal-table 1.5.3+ is released. gem 'terminal-table', '>=1.6.0'
# (and delete the Terminal module in lib/common/hacks.rb)
gem 'terminal-table', '~>1.4.5'
gem 'ruby-progressbar', '>=1.6.0' gem 'ruby-progressbar', '>=1.6.0'
group :test do group :test do

View File

@@ -1,6 +1,6 @@
WPScan Public Source License WPScan Public Source License
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2015 WPScan Team. The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2016 WPScan Team.
Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, WPScan can be used without charge under the terms set out below. Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, WPScan can be used without charge under the terms set out below.
@@ -68,3 +68,7 @@ To the extent permitted under Law, WPScan is provided under an AS-IS basis. The
10. Disclaimer 10. Disclaimer
Running WPScan against websites without prior mutual consent may be illegal in your country. The WPScan Team accept no liability and are not responsible for any misuse or damage caused by WPScan. Running WPScan against websites without prior mutual consent may be illegal in your country. The WPScan Team accept no liability and are not responsible for any misuse or damage caused by WPScan.
11. Trademark
The "wpscan" term is a registered trademark. This License does not grant the use of the "wpscan" trademark or the use of the WPScan logo.

230
README.md
View File

@@ -4,16 +4,17 @@
[![Build Status](https://travis-ci.org/wpscanteam/wpscan.svg?branch=master)](https://travis-ci.org/wpscanteam/wpscan) [![Build Status](https://travis-ci.org/wpscanteam/wpscan.svg?branch=master)](https://travis-ci.org/wpscanteam/wpscan)
[![Code Climate](https://img.shields.io/codeclimate/github/wpscanteam/wpscan.svg)](https://codeclimate.com/github/wpscanteam/wpscan) [![Code Climate](https://img.shields.io/codeclimate/github/wpscanteam/wpscan.svg)](https://codeclimate.com/github/wpscanteam/wpscan)
[![Dependency Status](https://img.shields.io/gemnasium/wpscanteam/wpscan.svg)](https://gemnasium.com/wpscanteam/wpscan) [![Dependency Status](https://img.shields.io/gemnasium/wpscanteam/wpscan.svg)](https://gemnasium.com/wpscanteam/wpscan)
[![Docker Pulls](https://img.shields.io/docker/pulls/wpscanteam/wpscan.svg)](https://hub.docker.com/r/wpscanteam/wpscan/)
#### LICENSE # LICENSE
#### WPScan Public Source License ## WPScan Public Source License
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2015 WPScan Team. The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2016 WPScan Team.
Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, WPScan can be used without charge under the terms set out below. Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, WPScan can be used without charge under the terms set out below.
##### 1. Definitions ### 1. Definitions
1.1 "License" means this document. 1.1 "License" means this document.
@@ -21,7 +22,7 @@ Cases that include commercialization of WPScan require a commercial, non-free li
1.3 "WPScan Team" means WPScans core developers, an updated list of whom can be found within the CREDITS file. 1.3 "WPScan Team" means WPScans core developers, an updated list of whom can be found within the CREDITS file.
##### 2. Commercialization ### 2. Commercialization
A commercial use is one intended for commercial advantage or monetary compensation. A commercial use is one intended for commercial advantage or monetary compensation.
@@ -44,7 +45,7 @@ We may grant commercial licenses at no monetary cost at our own discretion if th
Free-use Terms and Conditions; Free-use Terms and Conditions;
##### 3. Redistribution ### 3. Redistribution
Redistribution is permitted under the following conditions: Redistribution is permitted under the following conditions:
@@ -52,35 +53,39 @@ Redistribution is permitted under the following conditions:
- Unmodified Copyright notices are provided with WPScan. - Unmodified Copyright notices are provided with WPScan.
- Does not conflict with the commercialization clause. - Does not conflict with the commercialization clause.
##### 4. Copying ### 4. Copying
Copying is permitted so long as it does not conflict with the Redistribution clause. Copying is permitted so long as it does not conflict with the Redistribution clause.
##### 5. Modification ### 5. Modification
Modification is permitted so long as it does not conflict with the Redistribution clause. Modification is permitted so long as it does not conflict with the Redistribution clause.
##### 6. Contributions ### 6. Contributions
Any Contributions assume the Contributor grants the WPScan Team the unlimited, non-exclusive right to reuse, modify and relicense the Contributor's content. Any Contributions assume the Contributor grants the WPScan Team the unlimited, non-exclusive right to reuse, modify and relicense the Contributor's content.
##### 7. Support ### 7. Support
WPScan is provided under an AS-IS basis and without any support, updates or maintenance. Support, updates and maintenance may be given according to the sole discretion of the WPScan Team. WPScan is provided under an AS-IS basis and without any support, updates or maintenance. Support, updates and maintenance may be given according to the sole discretion of the WPScan Team.
##### 8. Disclaimer of Warranty ### 8. Disclaimer of Warranty
WPScan is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the WPScan is free of defects, merchantable, fit for a particular purpose or non-infringing. WPScan is provided under this License on an “as is” basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the WPScan is free of defects, merchantable, fit for a particular purpose or non-infringing.
##### 9. Limitation of Liability ### 9. Limitation of Liability
To the extent permitted under Law, WPScan is provided under an AS-IS basis. The WPScan Team shall never, and without any limit, be liable for any damage, cost, expense or any other payment incurred as a result of WPScan's actions, failure, bugs and/or any other interaction between WPScan and end-equipment, computers, other software or any 3rd party, end-equipment, computer or services. To the extent permitted under Law, WPScan is provided under an AS-IS basis. The WPScan Team shall never, and without any limit, be liable for any damage, cost, expense or any other payment incurred as a result of WPScan's actions, failure, bugs and/or any other interaction between WPScan and end-equipment, computers, other software or any 3rd party, end-equipment, computer or services.
##### 10. Disclaimer ### 10. Disclaimer
Running WPScan against websites without prior mutual consent may be illegal in your country. The WPScan Team accept no liability and are not responsible for any misuse or damage caused by WPScan. Running WPScan against websites without prior mutual consent may be illegal in your country. The WPScan Team accept no liability and are not responsible for any misuse or damage caused by WPScan.
#### INSTALL ### 11. Trademark
The "wpscan" term is a registered trademark. This License does not grant the use of the "wpscan" trademark or the use of the WPScan logo.
# INSTALL
WPScan comes pre-installed on the following Linux distributions: WPScan comes pre-installed on the following Linux distributions:
@@ -90,73 +95,49 @@ WPScan comes pre-installed on the following Linux distributions:
- [SamuraiWTF](http://samurai.inguardians.com/) - [SamuraiWTF](http://samurai.inguardians.com/)
- [BlackArch](http://blackarch.org/) - [BlackArch](http://blackarch.org/)
Prerequisites: Windows is not supported
- Ruby >= 1.9.2 - Recommended: 2.2.3 ## Prerequisites
- Ruby >= 2.1.9 - Recommended: 2.3.1
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault - Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
- RubyGems - Recommended: latest - RubyGems - Recommended: latest
- Git - Git
Windows is not supported. ### Installing dependencies on Ubuntu
If installed from Github update the code base with ```git pull```. The databases are updated with ```wpscan.rb --update```.
####Installing on Ubuntu: sudo apt-get install libcurl4-openssl-dev libxml2 libxml2-dev libxslt1-dev ruby-dev build-essential libgmp-dev zlib1g-dev
Before Ubuntu 14.04: ### Installing dependencies on Debian
sudo apt-get install libcurl4-openssl-dev libopenssl-ruby libxml2 libxml2-dev libxslt1-dev ruby-dev sudo apt-get install git ruby ruby-dev libcurl4-openssl-dev make zlib1g-dev
git clone https://github.com/wpscanteam/wpscan.git
cd wpscan
sudo gem install bundler && bundle install --without test
From Ubuntu 14.04: ### Installing dependencies on Fedora
sudo apt-get install libcurl4-openssl-dev libxml2 libxml2-dev libxslt1-dev ruby-dev build-essential libgmp-dev sudo dnf install gcc ruby-devel libxml2 libxml2-devel libxslt libxslt-devel libcurl-devel patch rpm-build
git clone https://github.com/wpscanteam/wpscan.git
cd wpscan
sudo gem install bundler && bundle install --without test
####Installing on Debian: ### Installing dependencies on Arch Linux
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
bundle install --without test --path vendor/bundle
####Installing on Fedora:
sudo yum install gcc ruby-devel libxml2 libxml2-devel libxslt libxslt-devel libcurl-devel patch
git clone https://github.com/wpscanteam/wpscan.git
cd wpscan
sudo gem install bundler && bundle install --without test
####Installing on Archlinux:
pacman -Syu ruby pacman -Syu ruby
pacman -Syu libyaml pacman -Syu libyaml
git clone https://github.com/wpscanteam/wpscan.git
cd wpscan
sudo gem install bundler && bundle install --without test
gem install typhoeus
gem install nokogiri
####Installing on Mac OSX: ### Installing dependencies on Mac OSX
Apple Xcode, Command Line Tools and the libffi are needed (to be able to install the FFI gem), See [http://stackoverflow.com/questions/17775115/cant-setup-ruby-environment-installing-fii-gem-error](http://stackoverflow.com/questions/17775115/cant-setup-ruby-environment-installing-fii-gem-error) Apple Xcode, Command Line Tools and the libffi are needed (to be able to install the FFI gem), See [http://stackoverflow.com/questions/17775115/cant-setup-ruby-environment-installing-fii-gem-error](http://stackoverflow.com/questions/17775115/cant-setup-ruby-environment-installing-fii-gem-error)
git clone https://github.com/wpscanteam/wpscan.git ## Installing with RVM (recommended)
cd wpscan
sudo gem install bundler && sudo bundle install --without test
####Installing with RVM: If you are using GNOME Terminal, there are some steps required before executing the commands. See here for more information:
https://rvm.io/integration/gnome-terminal#integrating-rvm-with-gnome-terminal
# Install all prerequisites for your OS (look above)
cd ~ cd ~
curl -sSL https://rvm.io/mpapis.asc | gpg --import -
curl -sSL https://get.rvm.io | bash -s stable curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm source ~/.rvm/scripts/rvm
echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc echo "source ~/.rvm/scripts/rvm" >> ~/.bashrc
rvm install 2.2.3 rvm install 2.3.1
rvm use 2.2.3 --default rvm use 2.3.1 --default
echo "gem: --no-ri --no-rdoc" > ~/.gemrc echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler gem install bundler
git clone https://github.com/wpscanteam/wpscan.git git clone https://github.com/wpscanteam/wpscan.git
@@ -164,7 +145,26 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
gem install bundler gem install bundler
bundle install --without test bundle install --without test
#### KNOWN ISSUES ## Installing manually (not recommended)
git clone https://github.com/wpscanteam/wpscan.git
cd wpscan
sudo gem install bundler && bundle install --without test
# DOCKER
Pull the repo with `docker pull wpscanteam/wpscan`
## Start WPScan
```
docker run --rm wpscanteam/wpscan -u http://yourblog.com [options]
```
For the available Options, please see https://github.com/wpscanteam/wpscan#wpscan-arguments
Published on https://hub.docker.com/r/wpscanteam/wpscan/
# KNOWN ISSUES
- Typhoeus segmentation fault - Typhoeus segmentation fault
@@ -191,7 +191,7 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
Then, open the directory of the readline gem (you have to locate it) Then, open the directory of the readline gem (you have to locate it)
cd ~/.rvm/src/ruby-1.9.2-p180/ext/readline cd ~/.rvm/src/ruby-XXXX/ext/readline
ruby extconf.rb ruby extconf.rb
make make
make install make install
@@ -207,15 +207,12 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
See [https://github.com/wpscanteam/wpscan/issues/148](https://github.com/wpscanteam/wpscan/issues/148) See [https://github.com/wpscanteam/wpscan/issues/148](https://github.com/wpscanteam/wpscan/issues/148)
#### WPSCAN ARGUMENTS # WPSCAN ARGUMENTS
--update Update the databases. --update Update the database to the latest version.
--url | -u <target url> The WordPress URL/domain to scan.
--url | -u <target url> The WordPress URL/domain to scan. --force | -f Forces WPScan to not check if the remote site is running WordPress.
--enumerate | -e [option(s)] Enumeration.
--force | -f Forces WPScan to not check if the remote site is running WordPress.
--enumerate | -e [option(s)] Enumeration.
option : option :
u usernames from id 1 to 10 u usernames from id 1 to 10
u[10-20] usernames from id 10 to 20 (you must write [] chars) u[10-20] usernames from id 10 to 20 (you must write [] chars)
@@ -229,55 +226,44 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins
If no option is supplied, the default is "vt,tt,u,vp" If no option is supplied, the default is "vt,tt,u,vp"
--exclude-content-based "<regexp or string>" Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied --exclude-content-based "<regexp or string>"
You do not need to provide the regexp delimiters, but you must write the quotes (simple or double) Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied.
You do not need to provide the regexp delimiters, but you must write the quotes (simple or double).
--config-file | -c <config file> Use the specified config file, see the example.conf.json.
--user-agent | -a <User-Agent> Use the specified User-Agent.
--cookie <string> String to read cookies from.
--random-agent | -r Use a random User-Agent.
--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not
--batch Never ask for user input, use the default behaviour.
--no-color Do not use colors in the output.
--log Creates a log.txt file with WPScan's output.
--no-banner Prevents the WPScan banner from being displayed.
--disable-accept-header Prevents WPScan sending the Accept HTTP header.
--disable-referer Prevents setting the Referer header.
--disable-tls-checks Disables SSL/TLS certificate verification.
--wp-content-dir <wp content dir> WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specify it.
Subdirectories are allowed.
--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory.
If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed
--proxy <[protocol://]host:port> Supply a proxy. HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported.
If no protocol is given (format host:port), HTTP will be used.
--proxy-auth <username:password> Supply the proxy login credentials.
--basic-auth <username:password> Set the HTTP Basic authentication.
--wordlist | -w <wordlist> Supply a wordlist for the password brute forcer.
--username | -U <username> Only brute force the supplied username.
--usernames <path-to-file> Only brute force the usernames from the file.
--cache-dir <cache-directory> Set the cache directory.
--cache-ttl <cache-ttl> Typhoeus cache TTL.
--request-timeout <request-timeout> Request Timeout.
--connect-timeout <connect-timeout> Connect Timeout.
--threads | -t <number of threads> The number of threads to use when multi-threading requests.
--max-threads <max-threads> Maximum Threads.
--throttle <milliseconds> Milliseconds to wait before doing another web request. If used, the --threads should be set to 1.
--help | -h This help screen.
--verbose | -v Verbose output.
--version Output the current version and exit.
--config-file | -c <config file> Use the specified config file, see the example.conf.json # WPSCAN EXAMPLES
--user-agent | -a <User-Agent> Use the specified User-Agent
--random-agent | -r Use a random User-Agent
--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not
--wp-content-dir <wp content dir> WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specified it. Subdirectories are allowed
--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory. If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed
--proxy <[protocol://]host:port> Supply a proxy (will override the one from conf/browser.conf.json).
HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported. If no protocol is given (format host:port), HTTP will be used
--proxy-auth <username:password> Supply the proxy login credentials.
--basic-auth <username:password> Set the HTTP Basic authentication.
--wordlist | -w <wordlist> Supply a wordlist for the password brute forcer.
--threads | -t <number of threads> The number of threads to use when multi-threading requests.
--username | -U <username> Only brute force the supplied username.
--usernames <path-to-file> Only brute force the usernames from the file.
--cache-ttl <cache-ttl> Typhoeus cache TTL.
--request-timeout <request-timeout> Request Timeout.
--connect-timeout <connect-timeout> Connect Timeout.
--max-threads <max-threads> Maximum Threads.
--help | -h This help screen.
--verbose | -v Verbose output.
--batch Never ask for user input, use the default behavior.
--no-color Do not use colors in the output.
--log Save STDOUT to log.txt
#### WPSCAN EXAMPLES
Do 'non-intrusive' checks... Do 'non-intrusive' checks...
@@ -311,26 +297,22 @@ Debug output...
```ruby wpscan.rb --url www.example.com --debug-output 2>debug.log``` ```ruby wpscan.rb --url www.example.com --debug-output 2>debug.log```
#### PROJECT HOME # PROJECT HOME
[http://www.wpscan.org](http://www.wpscan.org) [http://www.wpscan.org](http://www.wpscan.org)
#### VULNERABILITY DATABASE # VULNERABILITY DATABASE
[https://wpvulndb.com](https://wpvulndb.com) [https://wpvulndb.com](https://wpvulndb.com)
#### GIT REPOSITORY # GIT REPOSITORY
[https://github.com/wpscanteam/wpscan](https://github.com/wpscanteam/wpscan) [https://github.com/wpscanteam/wpscan](https://github.com/wpscanteam/wpscan)
#### ISSUES # ISSUES
[https://github.com/wpscanteam/wpscan/issues](https://github.com/wpscanteam/wpscan/issues) [https://github.com/wpscanteam/wpscan/issues](https://github.com/wpscanteam/wpscan/issues)
#### DEVELOPER DOCUMENTATION # DEVELOPER DOCUMENTATION
[http://rdoc.info/github/wpscanteam/wpscan/frames](http://rdoc.info/github/wpscanteam/wpscan/frames) [http://rdoc.info/github/wpscanteam/wpscan/frames](http://rdoc.info/github/wpscanteam/wpscan/frames)
#### SPECIAL THANKS
[RandomStorm](https://www.randomstorm.com)

BIN
data.zip

Binary file not shown.

19
dev/stats.rb Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env ruby
# encoding: UTF-8
require File.expand_path(File.join(__dir__, '..', 'lib', 'wpscan', 'wpscan_helper'))
wordpress_json = json(WORDPRESSES_FILE)
plugins_json = json(PLUGINS_FILE)
themes_json = json(THEMES_FILE)
puts 'WPScan Database Statistics:'
puts "* Total tracked wordpresses: #{wordpress_json.count}"
puts "* Total tracked plugins: #{plugins_json.count}"
puts "* Total tracked themes: #{themes_json.count}"
puts "* Total vulnerable wordpresses: #{wordpress_json.select { |item| !wordpress_json[item]['vulnerabilities'].empty? }.count}"
puts "* Total vulnerable plugins: #{plugins_json.select { |item| !plugins_json[item]['vulnerabilities'].empty? }.count}"
puts "* Total vulnerable themes: #{themes_json.select { |item| !themes_json[item]['vulnerabilities'].empty? }.count}"
puts "* Total wordpress vulnerabilities: #{wordpress_json.map {|k,v| v['vulnerabilities'].count}.inject(:+)}"
puts "* Total plugin vulnerabilities: #{plugins_json.map {|k,v| v['vulnerabilities'].count}.inject(:+)}"
puts "* Total theme vulnerabilities: #{themes_json.map {|k,v| v['vulnerabilities'].count}.inject(:+)}"

View File

@@ -2,8 +2,8 @@
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0", "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:9.0) Gecko/20100101 Firefox/9.0",
/* Uncomment the "proxy" line to use the proxy /* Uncomment the "proxy" line to use the proxy
SOCKS proxies (4, 4A, 5) are supported, ie : "proxy": "socks5://127.0.0.1:9000" SOCKS proxies (4, 4A, 5) are supported, ie : "proxy": "socks5://127.0.0.1:9000"
If you do not specify the protocol, http will be used If you do not specify the protocol, http will be used
*/ */
//"proxy": "127.0.0.1:3128", //"proxy": "127.0.0.1:3128",
//"proxy_auth": "username:password", //"proxy_auth": "username:password",

View File

@@ -18,7 +18,10 @@ class Browser
:request_timeout, :request_timeout,
:connect_timeout, :connect_timeout,
:cookie, :cookie,
:throttle :throttle,
:disable_accept_header,
:disable_referer,
:disable_tls_checks
] ]
@@instance = nil @@instance = nil
@@ -67,17 +70,23 @@ class Browser
@@instance = nil @@instance = nil
end end
# Override for setting the User-Agent
# @param [ String ] user_agent
def user_agent=(user_agent)
Typhoeus::Config.user_agent = user_agent
end
# #
# sets browser default values # sets browser default values
# #
def browser_defaults def browser_defaults
Typhoeus::Config.user_agent = "WPScan v#{WPSCAN_VERSION} (http://wpscan.org)"
@max_threads = 20 @max_threads = 20
# 10 minutes, at this time the cache is cleaned before each scan. # 10 minutes, at this time the cache is cleaned before each scan.
# If this value is set to 0, the cache will be disabled # If this value is set to 0, the cache will be disabled
@cache_ttl = 600 @cache_ttl = 600
@request_timeout = 60 # 60s @request_timeout = 60 # 60s
@connect_timeout = 10 # 10s @connect_timeout = 10 # 10s
@user_agent = "WPScan v#{WPSCAN_VERSION} (http://wpscan.org)"
@throttle = 0 @throttle = 0
end end
@@ -115,12 +124,6 @@ class Browser
# #
# @return [ Hash ] # @return [ Hash ]
def merge_request_params(params = {}) def merge_request_params(params = {})
params = Browser.append_params_header_field(
params,
'User-Agent',
@user_agent
)
if @proxy if @proxy
params.merge!(proxy: @proxy) params.merge!(proxy: @proxy)
params.merge!(proxyauth: @proxy_auth) if @proxy_auth params.merge!(proxyauth: @proxy_auth) if @proxy_auth
@@ -143,8 +146,8 @@ class Browser
end end
params.merge!(referer: referer) params.merge!(referer: referer)
params.merge!(timeout: @request_timeout) if @request_timeout params.merge!(timeout: @request_timeout) if @request_timeout && !params.key?(:timeout)
params.merge!(connecttimeout: @connect_timeout) if @connect_timeout params.merge!(connecttimeout: @connect_timeout) if @connect_timeout && !params.key?(:connecttimeout)
# Used to enable the cache system if :cache_ttl > 0 # Used to enable the cache system if :cache_ttl > 0
params.merge!(cache_ttl: @cache_ttl) unless params.key?(:cache_ttl) params.merge!(cache_ttl: @cache_ttl) unless params.key?(:cache_ttl)
@@ -153,12 +156,18 @@ class Browser
params.merge!(maxredirs: 3) unless params.key?(:maxredirs) params.merge!(maxredirs: 3) unless params.key?(:maxredirs)
# Disable SSL-Certificate checks # Disable SSL-Certificate checks
params.merge!(ssl_verifypeer: false) if @disable_tls_checks
params.merge!(ssl_verifyhost: 0) # Cert validity check
params.merge!(ssl_verifypeer: 0) unless params.key?(:ssl_verifypeer)
# Cert hostname check
params.merge!(ssl_verifyhost: 0) unless params.key?(:ssl_verifyhost)
end
params.merge!(cookiejar: @cache_dir + '/cookie-jar') params.merge!(cookiejar: @cache_dir + '/cookie-jar')
params.merge!(cookiefile: @cache_dir + '/cookie-jar') params.merge!(cookiefile: @cache_dir + '/cookie-jar')
params.merge!(cookie: @cookie) if @cookie params.merge!(cookie: @cookie) if @cookie
params = Browser.remove_params_header_field(params, 'Accept') if @disable_accept_header
params = Browser.remove_params_header_field(params, 'Referer') if @disable_referer
params params
end end
@@ -178,4 +187,18 @@ class Browser
end end
params params
end end
# @param [ Hash ] params
# @param [ String ] field
# @param [ Mixed ] field_value
#
# @return [ Array ]
def self.remove_params_header_field(params = {}, field)
if !params.has_key?(:headers)
params = params.merge(:headers => { field => nil })
elsif !params[:headers].has_key?(field)
params[:headers][field] = nil
end
params
end
end end

View File

@@ -3,9 +3,8 @@
class Browser class Browser
module Options module Options
attr_accessor :cache_ttl, :request_timeout, :connect_timeout attr_accessor :request_timeout, :connect_timeout, :user_agent, :disable_accept_header, :disable_referer, :disable_tls_checks
attr_reader :basic_auth, :proxy, :proxy_auth, :throttle attr_reader :basic_auth, :cache_ttl, :proxy, :proxy_auth, :throttle
attr_writer :user_agent
# Sets the Basic Authentification credentials # Sets the Basic Authentification credentials
# Accepted format: # Accepted format:
@@ -25,6 +24,10 @@ class Browser
end end
end end
def cache_ttl=(ttl)
@cache_ttl = ttl.to_i
end
# @return [ Integer ] # @return [ Integer ]
def max_threads def max_threads
@max_threads || 1 @max_threads || 1

View File

@@ -23,9 +23,7 @@ class CacheFileStore
@storage_path = File.expand_path(File.join(storage_path, storage_dir)) @storage_path = File.expand_path(File.join(storage_path, storage_dir))
@serializer = serializer @serializer = serializer
# File.directory? for ruby <= 1.9 otherwise, unless Dir.exist?(@storage_path)
# it makes more sense to do Dir.exist? :/
unless File.directory?(@storage_path)
FileUtils.mkdir_p(@storage_path) FileUtils.mkdir_p(@storage_path)
end end
end end
@@ -51,7 +49,7 @@ class CacheFileStore
end end
def write_entry(key, data_to_store, cache_ttl) def write_entry(key, data_to_store, cache_ttl)
if cache_ttl > 0 if cache_ttl && cache_ttl > 0
File.open(get_entry_file_path(key), 'w') do |f| File.open(get_entry_file_path(key), 'w') do |f|
begin begin
f.write(@serializer.dump(data_to_store)) f.write(@serializer.dump(data_to_store))

0
lib/common/collections/wp_items.rb Executable file → Normal file
View File

6
lib/common/collections/wp_items/detectable.rb Executable file → Normal file
View File

@@ -95,6 +95,10 @@ class WpItems < Array
code = tag.text.to_s code = tag.text.to_s
next if code.empty? next if code.empty?
if ! code.valid_encoding?
code = code.encode('UTF-16be', :invalid => :replace, :replace => '?').encode('UTF-8')
end
code.scan(code_pattern(wp_target)).flatten.uniq.each do |item_name| code.scan(code_pattern(wp_target)).flatten.uniq.each do |item_name|
names << item_name names << item_name
end end
@@ -116,7 +120,7 @@ class WpItems < Array
wp_content_dir = wp_target.wp_content_dir wp_content_dir = wp_target.wp_content_dir
wp_content_url = wp_target.uri.merge(wp_content_dir).to_s wp_content_url = wp_target.uri.merge(wp_content_dir).to_s
url = /#{wp_content_url.gsub(%r{\A(?:http|https)}, 'https?').gsub('/', '\\\\\?\/')}/i url = wp_content_url.gsub(%r{\A(?:http|https)://}, '(?:https?:)?//').gsub('/', '\\\\\?\/')
content_dir = %r{(?:#{url}|\\?\/\\?\/?#{wp_content_dir})}i content_dir = %r{(?:#{url}|\\?\/\\?\/?#{wp_content_dir})}i
%r{#{content_dir}\\?/#{type}\\?/} %r{#{content_dir}\\?/#{type}\\?/}

0
lib/common/collections/wp_plugins.rb Executable file → Normal file
View File

View File

@@ -62,10 +62,14 @@ class WpPlugins < WpItems
wp_plugins.add('all-in-one-seo-pack', version: $1) wp_plugins.add('all-in-one-seo-pack', version: $1)
end end
if body =~ /<!-- This site is optimized with the Yoast WordPress SEO plugin v([^\s]+) -/i if body =~ /<!-- This site is optimized with the Yoast (?:WordPress )?SEO plugin v([^\s]+) -/i
wp_plugins.add('wordpress-seo', version: $1) wp_plugins.add('wordpress-seo', version: $1)
end end
if body =~ /<!-- Google Universal Analytics for WordPress v([^\s]+) -/i
wp_plugins.add('google-universal-analytics', version: $1)
end
wp_plugins wp_plugins
end end

0
lib/common/collections/wp_themes.rb Executable file → Normal file
View File

0
lib/common/collections/wp_timthumbs.rb Executable file → Normal file
View File

View File

@@ -41,7 +41,7 @@ class WpTimthumbs < WpItems
%w{ %w{
timthumb.php lib/timthumb.php inc/timthumb.php includes/timthumb.php timthumb.php lib/timthumb.php inc/timthumb.php includes/timthumb.php
scripts/timthumb.php tools/timthumb.php functions/timthumb.php scripts/timthumb.php tools/timthumb.php functions/timthumb.php thumb.php
}.each do |path| }.each do |path|
wp_timthumb.path = "$wp-content$/themes/#{theme_name}/#{path}" wp_timthumb.path = "$wp-content$/themes/#{theme_name}/#{path}"

0
lib/common/collections/wp_users.rb Executable file → Normal file
View File

0
lib/common/collections/wp_users/detectable.rb Executable file → Normal file
View File

View File

@@ -1,6 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
LIB_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..')) LIB_DIR = File.expand_path(File.join(__dir__, '..'))
ROOT_DIR = File.expand_path(File.join(LIB_DIR, '..')) # expand_path is used to get "wpscan/" instead of "wpscan/lib/../" ROOT_DIR = File.expand_path(File.join(LIB_DIR, '..')) # expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
DATA_DIR = File.join(ROOT_DIR, 'data') DATA_DIR = File.join(ROOT_DIR, 'data')
CONF_DIR = File.join(ROOT_DIR, 'conf') CONF_DIR = File.join(ROOT_DIR, 'conf')
@@ -28,7 +28,9 @@ LOCAL_FILES_XSD = File.join(DATA_DIR, 'local_vulnerable_files.xsd')
USER_AGENTS_FILE = File.join(DATA_DIR, 'user-agents.txt') USER_AGENTS_FILE = File.join(DATA_DIR, 'user-agents.txt')
LAST_UPDATE_FILE = File.join(DATA_DIR, '.last_update') LAST_UPDATE_FILE = File.join(DATA_DIR, '.last_update')
WPSCAN_VERSION = '2.9' MIN_RUBY_VERSION = '2.1.9'
WPSCAN_VERSION = '2.9.2'
$LOAD_PATH.unshift(LIB_DIR) $LOAD_PATH.unshift(LIB_DIR)
$LOAD_PATH.unshift(WPSCAN_LIB_DIR) $LOAD_PATH.unshift(WPSCAN_LIB_DIR)
@@ -42,6 +44,11 @@ def kali_linux?
end end
end end
# Determins if installed on Windows OS
def windows?
Gem.win_platform?
end
require 'environment' require 'environment'
def escape_glob(s) def escape_glob(s)
@@ -144,7 +151,7 @@ def banner
puts '_______________________________________________________________' puts '_______________________________________________________________'
puts ' __ _______ _____ ' puts ' __ _______ _____ '
puts ' \\ \\ / / __ \\ / ____| ' puts ' \\ \\ / / __ \\ / ____| '
puts ' \\ \\ /\\ / /| |__) | (___ ___ __ _ _ __ ' puts ' \\ \\ /\\ / /| |__) | (___ ___ __ _ _ __ ®'
puts ' \\ \\/ \\/ / | ___/ \\___ \\ / __|/ _` | \'_ \\ ' puts ' \\ \\/ \\/ / | ___/ \\___ \\ / __|/ _` | \'_ \\ '
puts ' \\ /\\ / | | ____) | (__| (_| | | | |' puts ' \\ /\\ / | | ____) | (__| (_| | | | |'
puts ' \\/ \\/ |_| |_____/ \\___|\\__,_|_| |_|' puts ' \\/ \\/ |_| |_____/ \\___|\\__,_|_| |_|'
@@ -223,7 +230,11 @@ end
# #
# @return [ Integer ] The number of lines in the given file # @return [ Integer ] The number of lines in the given file
def count_file_lines(file) def count_file_lines(file)
`wc -l #{file.shellescape}`.split[0].to_i if windows?
`findstr /R /N "^" #{file.shellescape} | find /C ":"`.split[0].to_i
else
`wc -l #{file.shellescape}`.split[0].to_i
end
end end
# Truncates a string to a specific length and adds ... at the end # Truncates a string to a specific length and adds ... at the end
@@ -257,3 +268,7 @@ end
def directory_listing_enabled?(url) def directory_listing_enabled?(url)
Browser.get(url.to_s).body[%r{<title>Index of}] ? true : false Browser.get(url.to_s).body[%r{<title>Index of}] ? true : false
end end
def url_encode(str)
CGI.escape(str).gsub("+", "%20")
end

View File

@@ -22,13 +22,15 @@ class DbUpdater
{ {
ssl_verifyhost: 2, ssl_verifyhost: 2,
ssl_verifypeer: true, ssl_verifypeer: true,
accept_encoding: 'gzip, deflate' accept_encoding: 'gzip, deflate',
timeout: 300,
connecttimeout: 20
} }
end end
# @return [ String ] The raw file URL associated with the given filename # @return [ String ] The raw file URL associated with the given filename
def remote_file_url(filename) def remote_file_url(filename)
"https://wpvulndb.com/data/#{filename}" "https://data.wpscan.org/#{filename}"
end end
# @return [ String ] The checksum of the associated remote filename # @return [ String ] The checksum of the associated remote filename
@@ -99,7 +101,7 @@ class DbUpdater
puts " [i] Database File Checksum : #{db_checksum}" if verbose puts " [i] Database File Checksum : #{db_checksum}" if verbose
unless dl_checksum == db_checksum unless dl_checksum == db_checksum
fail "#{filename}: checksums do not match" raise ChecksumError.new(File.read(local_file_path(filename))), "#{filename}: checksums do not match (local: #{dl_checksum} remote: #{db_checksum})"
end end
rescue => e rescue => e
puts ' [i] Restoring Backup due to error' if verbose puts ' [i] Restoring Backup due to error' if verbose

View File

@@ -31,3 +31,11 @@ class DownloadError < HttpError
"Unable to get #{failure_details}" "Unable to get #{failure_details}"
end end
end end
class ChecksumError < StandardError
attr_reader :file
def initialize(file)
@file = file
end
end

View File

@@ -1,35 +1,5 @@
# encoding: UTF-8 # encoding: UTF-8
# Since ruby 1.9.2, URI::escape is obsolete
# See http://rosettacode.org/wiki/URL_encoding#Ruby and http://www.ruby-forum.com/topic/207489
if RUBY_VERSION >= '1.9.2'
module URI
extend self
def escape(str)
URI::Parser.new.escape(str)
end
alias :encode :escape
end
end
if RUBY_VERSION < '1.9'
class Array
# Fix for grep with symbols in ruby <= 1.8.7
def _grep_(regexp)
matches = []
self.each do |value|
value = value.to_s
matches << value if value.match(regexp)
end
matches
end
alias_method :grep, :_grep_
end
end
# This is used in WpItem::Existable # This is used in WpItem::Existable
module Typhoeus module Typhoeus
class Response class Response
@@ -57,52 +27,11 @@ def puts(o = '')
super(o) super(o)
end end
module Terminal
class Table
def render
separator = Separator.new(self)
buffer = [separator]
unless @title.nil?
buffer << Row.new(self, [title_cell_options])
buffer << separator
end
unless @headings.cells.empty?
buffer << @headings
buffer << separator
end
buffer += @rows
buffer << separator
buffer.map { |r| style.margin_left + r.render }.join("\n")
end
alias :to_s :render
class Style
@@defaults = {
:border_x => '-', :border_y => '|', :border_i => '+',
:padding_left => 1, :padding_right => 1,
:margin_left => '',
:width => nil, :alignment => nil
}
attr_accessor :margin_left
attr_accessor :border_x
attr_accessor :border_y
attr_accessor :border_i
attr_accessor :padding_left
attr_accessor :padding_right
attr_accessor :width
attr_accessor :alignment
end
end
end
class Numeric class Numeric
def bytes_to_human def bytes_to_human
units = %w{B KB MB GB TB} units = %w{B KB MB GB TB}
e = (Math.log(self)/Math.log(1024)).floor e = (Math.log(abs)/Math.log(1024)).floor
s = '%.3f' % (to_f / 1024**e) s = '%.3f' % (abs.to_f / 1024**e)
s.sub(/\.?0*$/, ' ' + units[e]) s.sub(/\.?0*$/, ' ' + units[e])
end end
end end

0
lib/common/models/vulnerability.rb Executable file → Normal file
View File

4
lib/common/models/wp_item.rb Executable file → Normal file
View File

@@ -100,9 +100,7 @@ class WpItem
# #
# @return [ void ] # @return [ void ]
def path=(path) def path=(path)
@path = URI.encode( @path = path.gsub(/\$wp-plugins\$/i, wp_plugins_dir).gsub(/\$wp-content\$/i, wp_content_dir)
path.gsub(/\$wp-plugins\$/i, wp_plugins_dir).gsub(/\$wp-content\$/i, wp_content_dir)
)
end end
# @param [ WpItem ] other # @param [ WpItem ] other

0
lib/common/models/wp_item/existable.rb Executable file → Normal file
View File

3
lib/common/models/wp_item/findable.rb Executable file → Normal file
View File

@@ -9,8 +9,7 @@ class WpItem
# #
# @return [ void ] # @return [ void ]
def found_from=(method) def found_from=(method)
found = method[%r{find_from_(.*)}, 1] @found_from = method.to_s.gsub(/find_from_/, '').gsub(/_/, ' ')
@found_from = found.gsub('_', ' ') if found
end end
module Findable module Findable

0
lib/common/models/wp_item/versionable.rb Executable file → Normal file
View File

0
lib/common/models/wp_item/vulnerable.rb Executable file → Normal file
View File

2
lib/common/models/wp_plugin.rb Executable file → Normal file
View File

@@ -7,7 +7,7 @@ class WpPlugin < WpItem
# #
# @return [ void ] # @return [ void ]
def forge_uri(target_base_uri) def forge_uri(target_base_uri)
@uri = target_base_uri.merge(URI.encode(wp_plugins_dir + '/' + name + '/')) @uri = target_base_uri.merge("#{wp_plugins_dir}/#{url_encode(name)}/")
end end
def db_file def db_file

2
lib/common/models/wp_theme.rb Executable file → Normal file
View File

@@ -23,7 +23,7 @@ class WpTheme < WpItem
# #
# @return [ void ] # @return [ void ]
def forge_uri(target_base_uri) def forge_uri(target_base_uri)
@uri = target_base_uri.merge(URI.encode(wp_content_dir + '/themes/' + name + '/')) @uri = target_base_uri.merge("#{wp_content_dir}/themes/#{url_encode(name)}/")
end end
# @return [ String ] The url to the theme stylesheet # @return [ String ] The url to the theme stylesheet

2
lib/common/models/wp_theme/findable.rb Executable file → Normal file
View File

@@ -11,7 +11,7 @@ class WpTheme < WpItem
def find(target_uri) def find(target_uri)
methods.grep(/^find_from_/).each do |method| methods.grep(/^find_from_/).each do |method|
if wp_theme = self.send(method, target_uri) if wp_theme = self.send(method, target_uri)
wp_theme.found_from = method wp_theme.found_from = method.to_s
return wp_theme return wp_theme
end end

View File

@@ -12,7 +12,7 @@ class WpTheme
puts " | Referenced style.css: #{referenced_url}" if referenced_url && referenced_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 Name: #{@theme_name}" if @theme_name
puts " | Theme URI: #{@theme_uri}" if @theme_uri puts " | Theme URI: #{@theme_uri}" if @theme_uri
puts " | Description: #{theme_desc}" puts " | Description: #{theme_desc}" if theme_desc
puts " | Author: #{@theme_author}" if @theme_author puts " | Author: #{@theme_author}" if @theme_author
puts " | Author URI: #{@theme_author_uri}" if @theme_author_uri puts " | Author URI: #{@theme_author_uri}" if @theme_author_uri
puts " | Template: #{@theme_template}" if @theme_template and verbose puts " | Template: #{@theme_template}" if @theme_template and verbose

0
lib/common/models/wp_theme/versionable.rb Executable file → Normal file
View File

0
lib/common/models/wp_timthumb.rb Executable file → Normal file
View File

0
lib/common/models/wp_timthumb/versionable.rb Executable file → Normal file
View File

0
lib/common/models/wp_user.rb Executable file → Normal file
View File

View File

@@ -3,6 +3,8 @@
class WpUser < WpItem class WpUser < WpItem
module BruteForcable module BruteForcable
attr_reader :progress_bar
# Brute force the user with the wordlist supplied # Brute force the user with the wordlist supplied
# #
# It can take a long time to queue 2 million requests, # It can take a long time to queue 2 million requests,
@@ -25,7 +27,8 @@ class WpUser < WpItem
hydra = browser.hydra hydra = browser.hydra
queue_count = 0 queue_count = 0
found = false found = false
progress_bar = self.progress_bar(count_file_lines(wordlist)+1, options)
create_progress_bar(count_file_lines(wordlist)+1, options)
File.open(wordlist).each do |password| File.open(wordlist).each do |password|
password.chomp! password.chomp!
@@ -42,7 +45,7 @@ class WpUser < WpItem
request.on_complete do |response| request.on_complete do |response|
progress_bar.progress += 1 if options[:show_progression] && !found progress_bar.progress += 1 if options[:show_progression] && !found
puts "\n Trying Username : #{login} Password : #{password}" if options[:verbose] progress_bar.log(" Trying Username: #{login} Password: #{password}") if options[:verbose]
if valid_password?(response, password, redirect_url, options) if valid_password?(response, password, redirect_url, options)
found = true found = true
@@ -57,7 +60,7 @@ class WpUser < WpItem
if queue_count >= browser.max_threads if queue_count >= browser.max_threads
hydra.run hydra.run
queue_count = 0 queue_count = 0
puts "Sent #{browser.max_threads} requests ..." if options[:verbose] progress_bar.log(" Sent #{browser.max_threads} request/s ...") if options[:verbose]
end end
end end
@@ -71,9 +74,9 @@ class WpUser < WpItem
# #
# @return [ ProgressBar ] # @return [ ProgressBar ]
# :nocov: # :nocov:
def progress_bar(passwords_size, options) def create_progress_bar(passwords_size, options)
if options[:show_progression] if options[:show_progression]
ProgressBar.create( @progress_bar = ProgressBar.create(
format: '%t %a <%B> (%c / %C) %P%% %e', format: '%t %a <%B> (%c / %C) %P%% %e',
title: " Brute Forcing '#{login}'", title: " Brute Forcing '#{login}'",
total: passwords_size total: passwords_size
@@ -107,20 +110,20 @@ class WpUser < WpItem
progression = "#{info('[SUCCESS]')} Login : #{login} Password : #{password}\n\n" progression = "#{info('[SUCCESS]')} Login : #{login} Password : #{password}\n\n"
valid = true valid = true
elsif response.body =~ /login_error/i elsif response.body =~ /login_error/i
verbose = "\n Incorrect login and/or password." verbose = "Incorrect login and/or password."
elsif response.timed_out? elsif response.timed_out?
progression = critical('ERROR: Request timed out.') progression = critical('ERROR: Request timed out.')
elsif response.code == 0 elsif response.code == 0
progression = critical("ERROR: No response from remote server. WAF/IPS? (#{response.return_message})") progression = critical("ERROR: No response from remote server. WAF/IPS? (#{response.return_message})")
elsif response.code.to_s =~ /^50/ 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 or use the --throttle option.')
else 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") verbose = critical(" Code: #{response.code}\n Body: #{response.body}\n")
end end
puts "\n " + progression if progression && options[:show_progression] progress_bar.log(" #{progression}") if progression && options[:show_progression]
puts verbose if verbose && options[:verbose] progress_bar.log(" #{verbose}") if verbose && options[:verbose]
valid || false valid || false
end end

0
lib/common/models/wp_user/existable.rb Executable file → Normal file
View File

15
lib/common/models/wp_version.rb Executable file → Normal file
View File

@@ -8,7 +8,7 @@ class WpVersion < WpItem
include WpVersion::Output include WpVersion::Output
# The version number # The version number
attr_accessor :number attr_accessor :number, :metadata
alias_method :version, :number # Needed to have the right behaviour in Vulnerable#vulnerable_to? alias_method :version, :number # Needed to have the right behaviour in Vulnerable#vulnerable_to?
# @return [ Array ] # @return [ Array ]
@@ -35,4 +35,17 @@ class WpVersion < WpItem
a << node.text.to_s a << node.text.to_s
end end
end end
# @return [ Hash ] Metadata for specific WP version from WORDPRESSES_FILE
def metadata(version)
json = json(db_file)
metadata = {}
temp = json[version]
if !temp.nil?
metadata[:release_date] = temp['release_date']
metadata[:changelog_url] = temp['changelog_url']
end
metadata
end
end end

77
lib/common/models/wp_version/findable.rb Executable file → Normal file
View File

@@ -12,6 +12,7 @@ class WpVersion < WpItem
# #
# @return [ WpVersion ] # @return [ WpVersion ]
def find(target_uri, wp_content_dir, wp_plugins_dir, versions_xml) def find(target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
versions = {}
methods.grep(/^find_from_/).each do |method| methods.grep(/^find_from_/).each do |method|
if method === :find_from_advanced_fingerprinting if method === :find_from_advanced_fingerprinting
@@ -21,9 +22,21 @@ class WpVersion < WpItem
end end
if version if version
return new(target_uri, number: version, found_from: method) if versions.key?(version)
versions[version] << method.to_s
else
versions[version] = [ method.to_s ]
end
end end
end end
if versions.length > 0
determined_version = versions.max_by { |k, v| v.length }
if determined_version
return new(target_uri, number: determined_version[0], found_from: determined_version[1].join(', '))
end
end
nil nil
end end
@@ -114,34 +127,6 @@ class WpVersion < WpItem
) )
end 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 # Uses data/wp_versions.xml to try to identify a
# wordpress version. # wordpress version.
# #
@@ -158,8 +143,6 @@ class WpVersion < WpItem
def find_from_advanced_fingerprinting(target_uri, wp_content_dir, wp_plugins_dir, versions_xml) def find_from_advanced_fingerprinting(target_uri, wp_content_dir, wp_plugins_dir, versions_xml)
xml = xml(versions_xml) xml = xml(versions_xml)
# This wp_item will take care of encoding the path
# and replace variables like $wp-content$ & $wp-plugins$
wp_item = WpItem.new(target_uri, wp_item = WpItem.new(target_uri,
wp_content_dir: wp_content_dir, wp_content_dir: wp_content_dir,
wp_plugins_dir: wp_plugins_dir) wp_plugins_dir: wp_plugins_dir)
@@ -218,5 +201,37 @@ class WpVersion < WpItem
) )
end 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?
begin
uri = Addressable::URI.parse(attr_value)
rescue Addressable::URI::InvalidURIError
next
end
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
end end
end end

View File

@@ -4,8 +4,16 @@ class WpVersion < WpItem
module Output module Output
def output(verbose = false) def output(verbose = false)
metadata = self.metadata(self.number)
puts puts
puts info("WordPress version #{self.number} identified from #{self.found_from}") if verbose
puts info("WordPress version #{self.number} identified from #{self.found_from}")
puts " | Released: #{metadata[:release_date]}"
puts " | Changelog: #{metadata[:changelog_url]}"
else
puts info("WordPress version #{self.number} #{"(Released on #{metadata[:release_date]}) identified from #{self.found_from}" if metadata[:release_date]}")
end
vulnerabilities = self.vulnerabilities vulnerabilities = self.vulnerabilities

View File

@@ -3,8 +3,9 @@
require 'rubygems' require 'rubygems'
version = RUBY_VERSION.dup version = RUBY_VERSION.dup
if Gem::Version.create(version) < Gem::Version.create(1.9)
puts "Ruby >= 1.9 required to run wpscan (You have #{version})" if Gem::Version.create(version) < Gem::Version.create(MIN_RUBY_VERSION)
puts "Ruby >= #{MIN_RUBY_VERSION} required to run wpscan (You have #{version})"
exit(1) exit(1)
end end
@@ -29,6 +30,7 @@ begin
require 'shellwords' require 'shellwords'
require 'fileutils' require 'fileutils'
require 'pathname' require 'pathname'
require 'cgi'
# Third party libs # Third party libs
require 'typhoeus' require 'typhoeus'
require 'yajl/json_gem' require 'yajl/json_gem'

View File

@@ -21,6 +21,29 @@ class WebSite
@uri.to_s @uri.to_s
end end
# Checks if the remote website has ssl errors
def ssl_error?
return false unless @uri.scheme == 'https'
c = get_root_path_return_code
# http://www.rubydoc.info/github/typhoeus/ethon/Ethon/Easy:return_code
return (
c == :ssl_connect_error ||
c == :peer_failed_verification ||
c == :ssl_certproblem ||
c == :ssl_cipher ||
c == :ssl_cacert ||
c == :ssl_cacert_badfile ||
c == :ssl_issuer_error ||
c == :ssl_crl_badfile ||
c == :ssl_engine_setfailed ||
c == :ssl_engine_notfound
)
end
def get_root_path_return_code
Browser.get(@uri.to_s).return_code
end
# Checks if the remote website is up. # Checks if the remote website is up.
def online? def online?
Browser.get(@uri.to_s).code != 0 Browser.get(@uri.to_s).code != 0
@@ -68,15 +91,18 @@ class WebSite
end end
# Compute the MD5 of the page # Compute the MD5 of the page
# Comments are deleted from the page to avoid cache generation details # Comments and scripts are deleted from the page to avoid cache generation details
# #
# @param [ String, Typhoeus::Response ] page The url of the response of the page # @param [ String, Typhoeus::Response ] page The url of the response of the page
# #
# @return [ String ] The MD5 hash of the page # @return [ String ] The MD5 hash of the page
def self.page_hash(page) def self.page_hash(page)
page = Browser.get(page, { followlocation: true, cache_ttl: 0 }) unless page.is_a?(Typhoeus::Response) page = Browser.get(page, { followlocation: true, cache_ttl: 0 }) unless page.is_a?(Typhoeus::Response)
# remove comments
Digest::MD5.hexdigest(page.body.gsub(/<!--.*?-->/m, '')) page = page.body.gsub(/<!--.*?-->/m, '')
# remove javascript stuff
page = page.gsub(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/m, '')
Digest::MD5.hexdigest(page)
end end
def homepage_hash def homepage_hash

View File

@@ -135,6 +135,11 @@ class WpTarget < WebSite
@uri.merge("#{wp_content_dir}/uploads/").to_s @uri.merge("#{wp_content_dir}/uploads/").to_s
end end
# @return [ String ]
def includes_dir_url
@uri.merge("wp-includes/").to_s
end
# Script for replacing strings in wordpress databases # Script for replacing strings in wordpress databases
# reveals database credentials after hitting submit # reveals database credentials after hitting submit
# http://interconnectit.com/124/search-and-replace-for-wordpress-databases/ # http://interconnectit.com/124/search-and-replace-for-wordpress-databases/
@@ -153,4 +158,8 @@ class WpTarget < WebSite
def upload_directory_listing_enabled? def upload_directory_listing_enabled?
directory_listing_enabled?(upload_dir_url) directory_listing_enabled?(upload_dir_url)
end end
def include_directory_listing_enabled?
directory_listing_enabled?(includes_dir_url)
end
end end

View File

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

View File

@@ -1,6 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
require File.expand_path(File.dirname(__FILE__) + '/../common/common_helper') require File.expand_path(File.join(__dir__, '..', 'common', 'common_helper'))
require_files_from_directory(WPSCAN_LIB_DIR, '**/*.rb') require_files_from_directory(WPSCAN_LIB_DIR, '**/*.rb')
@@ -62,7 +62,7 @@ def help
puts puts
puts 'Some values are settable in a config file, see the example.conf.json' puts 'Some values are settable in a config file, see the example.conf.json'
puts puts
puts '--update Update to the database to the latest version.' puts '--update Update the database to the latest version.'
puts '--url | -u <target url> The WordPress URL/domain to scan.' puts '--url | -u <target url> The WordPress URL/domain to scan.'
puts '--force | -f Forces WPScan to not check if the remote site is running WordPress.' puts '--force | -f Forces WPScan to not check if the remote site is running WordPress.'
puts '--enumerate | -e [option(s)] Enumeration.' puts '--enumerate | -e [option(s)] Enumeration.'
@@ -84,12 +84,17 @@ def help
puts ' You do not need to provide the regexp delimiters, but you must write the quotes (simple or double).' puts ' You do not need to provide the regexp delimiters, but you must write the quotes (simple or double).'
puts '--config-file | -c <config file> Use the specified config file, see the example.conf.json.' puts '--config-file | -c <config file> Use the specified config file, see the example.conf.json.'
puts '--user-agent | -a <User-Agent> Use the specified User-Agent.' puts '--user-agent | -a <User-Agent> Use the specified User-Agent.'
puts '--cookie <String> String to read cookies from.' puts '--cookie <string> String to read cookies from.'
puts '--random-agent | -r Use a random User-Agent.' puts '--random-agent | -r Use a random User-Agent.'
puts '--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not' puts '--follow-redirection If the target url has a redirection, it will be followed without asking if you wanted to do so or not'
puts '--batch Never ask for user input, use the default behaviour.' puts '--batch Never ask for user input, use the default behaviour.'
puts '--no-color Do not use colors in the output.' puts '--no-color Do not use colors in the output.'
puts '--wp-content-dir <wp content dir> WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specified it.' puts '--log Creates a log.txt file with WPScan\'s output.'
puts '--no-banner Prevents the WPScan banner from being displayed.'
puts '--disable-accept-header Prevents WPScan sending the Accept HTTP header.'
puts '--disable-referer Prevents setting the Referer header.'
puts '--disable-tls-checks Disables SSL/TLS certificate verification.'
puts '--wp-content-dir <wp content dir> WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specify it.'
puts ' Subdirectories are allowed.' puts ' Subdirectories are allowed.'
puts '--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory.' puts '--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory.'
puts ' If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed' puts ' If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed'
@@ -100,10 +105,11 @@ def help
puts '--wordlist | -w <wordlist> Supply a wordlist for the password brute forcer.' puts '--wordlist | -w <wordlist> Supply a wordlist for the password brute forcer.'
puts '--username | -U <username> Only brute force the supplied username.' puts '--username | -U <username> Only brute force the supplied username.'
puts '--usernames <path-to-file> Only brute force the usernames from the file.' puts '--usernames <path-to-file> Only brute force the usernames from the file.'
puts '--threads | -t <number of threads> The number of threads to use when multi-threading requests.' puts '--cache-dir <cache-directory> Set the cache directory.'
puts '--cache-ttl <cache-ttl> Typhoeus cache TTL.' puts '--cache-ttl <cache-ttl> Typhoeus cache TTL.'
puts '--request-timeout <request-timeout> Request Timeout.' puts '--request-timeout <request-timeout> Request Timeout.'
puts '--connect-timeout <connect-timeout> Connect Timeout.' puts '--connect-timeout <connect-timeout> Connect Timeout.'
puts '--threads | -t <number of threads> The number of threads to use when multi-threading requests.'
puts '--max-threads <max-threads> Maximum Threads.' 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 '--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 '--help | -h This help screen.'

View File

@@ -43,7 +43,11 @@ class WpscanOptions
:connect_timeout, :connect_timeout,
:max_threads, :max_threads,
:no_banner, :no_banner,
:throttle :throttle,
:disable_accept_header,
:disable_referer,
:cache_dir,
:disable_tls_checks
] ]
attr_accessor *ACCESSOR_OPTIONS attr_accessor *ACCESSOR_OPTIONS
@@ -208,7 +212,9 @@ class WpscanOptions
enumerate_options_from_string(cli_value) enumerate_options_from_string(cli_value)
else else
raise "Unknow option : #{cli_option} with value #{cli_value}" text = "Unknown option : #{cli_option}"
text << " with value #{cli_value}" if (cli_value && !cli_value.empty?)
raise text
end end
end end
@@ -282,7 +288,11 @@ class WpscanOptions
['--cookie', GetoptLong::REQUIRED_ARGUMENT], ['--cookie', GetoptLong::REQUIRED_ARGUMENT],
['--log', GetoptLong::NO_ARGUMENT], ['--log', GetoptLong::NO_ARGUMENT],
['--no-banner', GetoptLong::NO_ARGUMENT], ['--no-banner', GetoptLong::NO_ARGUMENT],
['--throttle', GetoptLong::REQUIRED_ARGUMENT] ['--throttle', GetoptLong::REQUIRED_ARGUMENT],
['--disable-accept-header', GetoptLong::NO_ARGUMENT],
['--disable-referer', GetoptLong::NO_ARGUMENT],
['--cache-dir', GetoptLong::REQUIRED_ARGUMENT],
['--disable-tls-checks', GetoptLong::NO_ARGUMENT],
) )
end end

View File

@@ -124,11 +124,10 @@ describe Browser do
describe '#merge_request_params' do describe '#merge_request_params' do
let(:params) { {} } let(:params) { {} }
let(:cookie_jar) { CACHE_DIR + '/browser/cookie-jar' } let(:cookie_jar) { CACHE_DIR + '/browser/cookie-jar' }
let(:user_agent) { 'SomeUA' }
let(:default_expectation) { let(:default_expectation) {
{ {
cache_ttl: 250, cache_ttl: 250,
headers: { 'User-Agent' => 'SomeUA' },
ssl_verifypeer: false, ssl_verifyhost: 0,
cookiejar: cookie_jar, cookiefile: cookie_jar, cookiejar: cookie_jar, cookiefile: cookie_jar,
timeout: 60, connecttimeout: 10, timeout: 60, connecttimeout: 10,
maxredirs: 3, maxredirs: 3,
@@ -137,16 +136,25 @@ describe Browser do
} }
after :each do after :each do
browser.user_agent = 'SomeUA' browser.user_agent = user_agent
browser.cache_ttl = 250 browser.cache_ttl = 250
expect(browser.merge_request_params(params)).to eq @expected expect(browser.merge_request_params(params)).to eq @expected
expect(Typhoeus::Config.user_agent).to eq user_agent
end end
it 'sets the User-Agent header field and cache_ttl' do it 'sets the User-Agent header field and cache_ttl' do
@expected = default_expectation @expected = default_expectation
end end
context 'when @user_agent' do
let(:user_agent) { 'test' }
it 'sets the User-Agent' do
@expected = default_expectation
end
end
context 'when @proxy' do context 'when @proxy' do
let(:proxy) { '127.0.0.1:9050' } let(:proxy) { '127.0.0.1:9050' }
let(:proxy_expectation) { default_expectation.merge(proxy: proxy) } let(:proxy_expectation) { default_expectation.merge(proxy: proxy) }
@@ -177,7 +185,7 @@ describe Browser do
it 'appends the basic_auth' do it 'appends the basic_auth' do
browser.basic_auth = 'user:pass' browser.basic_auth = 'user:pass'
@expected = default_expectation.merge( @expected = default_expectation.merge(
headers: default_expectation[:headers].merge('Authorization' => 'Basic ' + Base64.encode64('user:pass').chomp) headers: { 'Authorization' => 'Basic ' + Base64.encode64('user:pass').chomp }
) )
end end
end end
@@ -206,6 +214,13 @@ describe Browser do
@expected = default_expectation.merge(cookie: cookie) @expected = default_expectation.merge(cookie: cookie)
end end
end end
context 'when @disable_tls_checks' do
it 'disables tls checks' do
browser.disable_tls_checks = true
@expected = default_expectation.merge(ssl_verifypeer: 0, ssl_verifyhost: 0)
end
end
end end
describe '#forge_request' do describe '#forge_request' do

View File

@@ -18,7 +18,7 @@ describe WpItems do
vulnerable_targets_items: [ WpItem.new(uri, name: 'mr-smith'), vulnerable_targets_items: [ WpItem.new(uri, name: 'mr-smith'),
WpItem.new(uri, name: 'neo')], WpItem.new(uri, name: 'neo')],
passive_detection: (1..13).reduce(WpItems.new) { |o, i| o << WpItem.new(uri, name: "detect-me-#{i}") } passive_detection: (1..15).reduce(WpItems.new) { |o, i| o << WpItem.new(uri, name: "detect-me-#{i}") }
} }
end end
end end

View File

@@ -100,6 +100,13 @@ describe 'WpPlugins::Detectable' do
expected.add('all-in-one-seo-pack', version: '2.0.3.1') expected.add('all-in-one-seo-pack', version: '2.0.3.1')
end end
end end
context 'when google-universal-analytics detected' do
it 'returns google-universal-analytics' do
@body = '<!-- Google Universal Analytics for WordPress v2.4.2 -->'
expected.add('google-universal-analytics', version: '2.4.2')
end
end
end end
end end

View File

@@ -26,10 +26,10 @@ describe 'WpTimthumbs::Detectable' do
def expected_targets_from_theme(theme_name) def expected_targets_from_theme(theme_name)
expected = [] expected = []
%w{ %w(
timthumb.php lib/timthumb.php inc/timthumb.php includes/timthumb.php timthumb.php lib/timthumb.php inc/timthumb.php includes/timthumb.php
scripts/timthumb.php tools/timthumb.php functions/timthumb.php scripts/timthumb.php tools/timthumb.php functions/timthumb.php thumb.php
}.each do |file| ).each do |file|
path = "$wp-content$/themes/#{theme_name}/#{file}" path = "$wp-content$/themes/#{theme_name}/#{file}"
expected << WpTimthumb.new(uri, path: path) expected << WpTimthumb.new(uri, path: path)
end end
@@ -46,7 +46,7 @@ describe 'WpTimthumbs::Detectable' do
after do after do
targets = subject.send(:targets_items_from_file, file, wp_target) targets = subject.send(:targets_items_from_file, file, wp_target)
expect(targets.map { |t| t.url }).to eq @expected.map { |t| t.url } expect(targets.map(&:url)).to eq @expected.map(&:url)
end end
context 'when an empty file' do context 'when an empty file' do
@@ -71,7 +71,7 @@ describe 'WpTimthumbs::Detectable' do
theme = 'hello-world' theme = 'hello-world'
targets = subject.send(:theme_timthumbs, theme, wp_target) targets = subject.send(:theme_timthumbs, theme, wp_target)
expect(targets.map { |t| t.url }).to eq expected_targets_from_theme(theme).map { |t| t.url } expect(targets.map(&:url)).to eq expected_targets_from_theme(theme).map(&:url)
end end
end end
@@ -81,7 +81,7 @@ describe 'WpTimthumbs::Detectable' do
after do after do
targets = subject.send(:targets_items, wp_target, options) targets = subject.send(:targets_items, wp_target, options)
expect(targets.map { |t| t.url }).to eq @expected.sort.map { |t| t.url } expect(targets.map(&:url)).to match_array(@expected.map(&:url))
end end
context 'when no :theme_name' do context 'when no :theme_name' do
@@ -101,7 +101,7 @@ describe 'WpTimthumbs::Detectable' do
end end
context 'when :theme_name' do context 'when :theme_name' do
let(:theme) { 'theme-name'} let(:theme) { 'theme-name' }
context 'when no :file' do context 'when no :file' do
let(:options) { { theme_name: theme } } let(:options) { { theme_name: theme } }

View File

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

View File

@@ -159,6 +159,22 @@ describe 'WpVersion::Findable' do
end end
end end
describe '::find_from_stylesheets_numbers' do
after do
fixture = fixtures_dir + 'stylesheet_numbers' + @fixture
stub_request_to_fixture(url: uri, fixture: fixture)
expect(WpVersion.send(:find_from_stylesheets_numbers, uri)).to eq @expected
end
context 'invalid url' do
it 'returns nil' do
@fixture = '/invalid_url.html'
@expected = nil
end
end
end
describe '::find' do describe '::find' do
# Stub all WpVersion::find_from_* to return nil # Stub all WpVersion::find_from_* to return nil
def stub_all_to_nil def stub_all_to_nil

View File

@@ -160,6 +160,11 @@ describe 'VersionCompare' do
@version1 = '.47' @version1 = '.47'
@version2 = '.50.3' @version2 = '.50.3'
end end
it 'returns true' do
@version1 = '2.5.9'
@version2 = '2.5.10'
end
end end
context 'version checked is older' do context 'version checked is older' do

View File

@@ -176,6 +176,17 @@ describe 'WebSite' do
@expected = "yolo\n\n\nworld!" @expected = "yolo\n\n\nworld!"
end end
end end
context 'when there are scripts' do
let(:page) {
body = "yolo\n\n<script type=\"text/javascript\">alert('Hi');</script>\nworld!"
Typhoeus::Response.new(body: body)
}
it 'removes them' do
@expected = "yolo\n\n\nworld!"
end
end
end end
describe '#homepage_hash' do describe '#homepage_hash' do

View File

@@ -1,6 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
require File.expand_path(File.dirname(__FILE__) + '/wpscan_helper') require File.expand_path(File.join(__dir__, 'wpscan_helper'))
describe WpTarget do describe WpTarget do
subject(:wp_target) { WpTarget.new(target_url, options) } subject(:wp_target) { WpTarget.new(target_url, options) }

View File

@@ -1,6 +1,6 @@
# encoding: UTF-8 # encoding: UTF-8
require File.expand_path(File.dirname(__FILE__) + '/wpscan_helper') require File.expand_path(File.join(__dir__, 'wpscan_helper'))
describe 'WpscanOptions' do describe 'WpscanOptions' do

View File

@@ -19,6 +19,8 @@
<script type='text/javascript' src='//wp-content/items/detect-me-5/main.js'></script> <script type='text/javascript' src='//wp-content/items/detect-me-5/main.js'></script>
<link rel='stylesheet' id='ai1ec_style-css' href='//example.com/wp-content/items/detect-me-15/test.css' type='text/css' media='all' />
<style type="text/css"> <style type="text/css">
#fancybox-loading.fancybox-ie div { #fancybox-loading.fancybox-ie div {
background: transparent; background: transparent;
@@ -32,6 +34,7 @@
@import url('/wp-content/items/detect-me-8/css/datatables.css?ver=1.9.4'); @import url('/wp-content/items/detect-me-8/css/datatables.css?ver=1.9.4');
@import url(/wp-content/items/detect-me-9/css/datatables.css?ver=1.9.4); @import url(/wp-content/items/detect-me-9/css/datatables.css?ver=1.9.4);
@import url(//wp-content/items/detect-me-10/css/datatables.css?ver=1.9.4); @import url(//wp-content/items/detect-me-10/css/datatables.css?ver=1.9.4);
@import url(//example.com/wp-content/items/detect-me-14/css/datatables.css?ver=1.9.4);
/* ]]> */ /* ]]> */
</style> </style>

0
spec/samples/common/models/wp_item/error_log Executable file → Normal file
View File

View File

View File

View File

View File

View File

@@ -0,0 +1,8 @@
<script src="//use.typekit.net/h3 {
font-family: &#039;Century Gothic&#039;, CenturyGothic, AppleGothic, sans-serif;
font-size: 14px;
font-style: normal;
font-variant: normal;
font-weight: 500;
line-height: 15px;.js"></script><script>try{Typekit.load();}catch(e){}</script>

0
spec/samples/wpscan/web_site/rss_url/wordpress-3.5.htm Executable file → Normal file
View File

0
spec/samples/wpscan/wp_target/wp-login.php Executable file → Normal file
View File

View File

View File

View File

View File

View File

View File

0
spec/samples/wpscan/wp_target/xmlrpc.php Executable file → Normal file
View File

View File

@@ -7,12 +7,6 @@ shared_examples 'WpItem::Findable#Found_From=' do
subject.found_from = @method subject.found_from = @method
expect(subject.found_from).to eq @expected expect(subject.found_from).to eq @expected
end end
context 'when the pattern is not found' do
it 'returns nil' do
@method = 'I_do_not_match'
@expected = nil
end
end
it 'replaces _ by space' do it 'replaces _ by space' do
@method = 'find_from_some_detection_method' @method = 'find_from_some_detection_method'

View File

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

View File

@@ -7,7 +7,7 @@ require 'simplecov' if RUBY_VERSION >= '1.9'
RSpec::Expectations.configuration.warn_about_potential_false_positives = false RSpec::Expectations.configuration.warn_about_potential_false_positives = false
require File.expand_path(File.dirname(__FILE__) + '/../lib/common/common_helper') require File.expand_path(File.join(__dir__, '..', 'lib', 'common', 'common_helper'))
SPEC_DIR = ROOT_DIR + '/spec' SPEC_DIR = ROOT_DIR + '/spec'
SPEC_LIB_DIR = SPEC_DIR + '/lib' SPEC_LIB_DIR = SPEC_DIR + '/lib'

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