Compare commits
250 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a94b8362a | ||
|
|
a25b493064 | ||
|
|
2acf88d83e | ||
|
|
baf3b4bc2b | ||
|
|
750411d9e1 | ||
|
|
aa7b922d30 | ||
|
|
fd660632e0 | ||
|
|
c7df7265ab | ||
|
|
42685a45b3 | ||
|
|
ce5d26a220 | ||
|
|
0e73774bd9 | ||
|
|
85b491472a | ||
|
|
4b382acbad | ||
|
|
12d15bfc7e | ||
|
|
ea1b6b9c17 | ||
|
|
5cb2d16601 | ||
|
|
913717bcf7 | ||
|
|
99fe1855d9 | ||
|
|
e2eb94be22 | ||
|
|
aca1b487ba | ||
|
|
5820c53d0f | ||
|
|
9298758acd | ||
|
|
a981c2b17b | ||
|
|
a783b53107 | ||
|
|
cf2881fda6 | ||
|
|
59368a72bd | ||
|
|
439900a1ea | ||
|
|
44557797b0 | ||
|
|
ba065d5974 | ||
|
|
105e9cbcac | ||
|
|
fe277c1e89 | ||
|
|
b5e3e6280e | ||
|
|
f90a64ce81 | ||
|
|
b9fa1e3587 | ||
|
|
4333ecb989 | ||
|
|
715d3d4ad6 | ||
|
|
38f70a88ae | ||
|
|
4b4b968710 | ||
|
|
3b94fc49a7 | ||
|
|
e41aab3a80 | ||
|
|
9450ba6cc5 | ||
|
|
ae3c164350 | ||
|
|
24e6820a90 | ||
|
|
0e05f77fb7 | ||
|
|
de960ff9db | ||
|
|
1d0128af72 | ||
|
|
285b1a1733 | ||
|
|
ab67816dd9 | ||
|
|
fea6665876 | ||
|
|
6cbc8c9924 | ||
|
|
f542a50213 | ||
|
|
fa430606ce | ||
|
|
05d27c64be | ||
|
|
0cd680bb29 | ||
|
|
ced94a7338 | ||
|
|
b65a4d0a60 | ||
|
|
2b85b44bd1 | ||
|
|
991c87a89e | ||
|
|
37a72f0c72 | ||
|
|
6c0a21c80d | ||
|
|
dc48008d43 | ||
|
|
5720d29492 | ||
|
|
358f3d59d8 | ||
|
|
b6c6a46d25 | ||
|
|
25c393d557 | ||
|
|
435fb34233 | ||
|
|
2c40913a64 | ||
|
|
e437b952da | ||
|
|
282c595b38 | ||
|
|
c2c8d63e75 | ||
|
|
ad21d97d11 | ||
|
|
5c27c78ed0 | ||
|
|
a53e9a5e12 | ||
|
|
c8036692ee | ||
|
|
b9535a3648 | ||
|
|
651c364fa9 | ||
|
|
958410d4c9 | ||
|
|
e9fba126d2 | ||
|
|
95d39cce5a | ||
|
|
32d9afdf9b | ||
|
|
7e9a4168ff | ||
|
|
9d6415a89b | ||
|
|
1499b07176 | ||
|
|
9c7188a312 | ||
|
|
b63e28c150 | ||
|
|
50d48902cf | ||
|
|
aa6899cbc5 | ||
|
|
94e6b2eab6 | ||
|
|
54c0e79c58 | ||
|
|
859d7f1c60 | ||
|
|
166112209e | ||
|
|
952395d0c1 | ||
|
|
c7061f8a51 | ||
|
|
0c71bce221 | ||
|
|
b2b4eebd78 | ||
|
|
5257a8b997 | ||
|
|
9844f9d8ab | ||
|
|
000f275263 | ||
|
|
e5077c490a | ||
|
|
d76968c15f | ||
|
|
289ef5b0dd | ||
|
|
7ec227873c | ||
|
|
1deccfd477 | ||
|
|
286e6bd51a | ||
|
|
8167fa2e17 | ||
|
|
c960df0bb1 | ||
|
|
ebf8d31c6c | ||
|
|
082ae650fc | ||
|
|
2f5599c863 | ||
|
|
a764bdd993 | ||
|
|
ef46d2c956 | ||
|
|
d2c2c1defb | ||
|
|
dede023ec8 | ||
|
|
d8a9b3aa77 | ||
|
|
ad364e6a2e | ||
|
|
523954e507 | ||
|
|
872bbdb8e0 | ||
|
|
3ca8727b64 | ||
|
|
1d3ca87772 | ||
|
|
90c42f42a1 | ||
|
|
641108e7eb | ||
|
|
0e87384b0a | ||
|
|
5175170c4b | ||
|
|
79864cae7b | ||
|
|
ca5f92ca61 | ||
|
|
d29de83c41 | ||
|
|
1f42ce6e2f | ||
|
|
0dc7128582 | ||
|
|
21f4de2ec1 | ||
|
|
d65567fc8f | ||
|
|
20af778fa1 | ||
|
|
5f77832386 | ||
|
|
6ccfe70775 | ||
|
|
6b0f687abb | ||
|
|
67ba526b5b | ||
|
|
e186ec7534 | ||
|
|
23ef1e75b3 | ||
|
|
8170390f92 | ||
|
|
c148295f64 | ||
|
|
37b99f9baa | ||
|
|
8e4643874d | ||
|
|
0522023fd4 | ||
|
|
711ee730a0 | ||
|
|
f3bd995528 | ||
|
|
beec0bd35a | ||
|
|
9d7f35f3b2 | ||
|
|
c7488e28f7 | ||
|
|
9150e0ca52 | ||
|
|
475288deeb | ||
|
|
82335d7399 | ||
|
|
338eacd63b | ||
|
|
0b9b79f55f | ||
|
|
5303b28957 | ||
|
|
11c05a3590 | ||
|
|
862c0a9014 | ||
|
|
487a483aa6 | ||
|
|
030c20a11b | ||
|
|
ec831f7fed | ||
|
|
50fa79b331 | ||
|
|
edab0e812a | ||
|
|
f0126ca860 | ||
|
|
01261d4d29 | ||
|
|
f97d3436a5 | ||
|
|
0bcb8b4b3b | ||
|
|
489545dd75 | ||
|
|
f6c152f58a | ||
|
|
16734418be | ||
|
|
b17ee20f58 | ||
|
|
aaee6f1e6d | ||
|
|
64d8240b8a | ||
|
|
0a6d430c9f | ||
|
|
7bf0314561 | ||
|
|
409897fec4 | ||
|
|
91b0d20665 | ||
|
|
f6644eebf9 | ||
|
|
88bddd4f87 | ||
|
|
c61b023fb7 | ||
|
|
1b5df8751f | ||
|
|
314c98f101 | ||
|
|
8274e2efe9 | ||
|
|
2bff063805 | ||
|
|
53d9956829 | ||
|
|
6e98678c3c | ||
|
|
f0f21f5ac2 | ||
|
|
aa233b1c4d | ||
|
|
93f9123f45 | ||
|
|
5c710d88e4 | ||
|
|
ded70ff743 | ||
|
|
9df7443aa4 | ||
|
|
8362975691 | ||
|
|
49771419ae | ||
|
|
d344f84824 | ||
|
|
89c0b8d4d0 | ||
|
|
3c74ee8d97 | ||
|
|
785c6efa5b | ||
|
|
4e2bf5322e | ||
|
|
54ed148c87 | ||
|
|
b08e298eba | ||
|
|
89e2088357 | ||
|
|
f3cc35bd74 | ||
|
|
a007d283e5 | ||
|
|
70902aa013 | ||
|
|
91151fc53b | ||
|
|
d4ee82dac5 | ||
|
|
88d3c26113 | ||
|
|
054a4ee6aa | ||
|
|
c291022753 | ||
|
|
2fc488b602 | ||
|
|
009ddd690e | ||
|
|
88b5cd8751 | ||
|
|
cfd19d02b1 | ||
|
|
19ce30d862 | ||
|
|
c6df6e0e89 | ||
|
|
e942a5bcf6 | ||
|
|
c0f5163d07 | ||
|
|
f5aa9f117f | ||
|
|
498d93377d | ||
|
|
52242e706b | ||
|
|
22d69a1bf9 | ||
|
|
0b1fa13696 | ||
|
|
19b15b5327 | ||
|
|
e63e96f5ed | ||
|
|
e8ac8f26a7 | ||
|
|
13e4327de4 | ||
|
|
c22a1ed12a | ||
|
|
be5662b5f1 | ||
|
|
6e840ca920 | ||
|
|
8492190f4c | ||
|
|
93ab6ee2a0 | ||
|
|
7075e01886 | ||
|
|
436a83434c | ||
|
|
d270391b56 | ||
|
|
7f2762eb6f | ||
|
|
2cc5bb0311 | ||
|
|
d697127261 | ||
|
|
825523a851 | ||
|
|
0f3f9cac33 | ||
|
|
f9b545b100 | ||
|
|
943bfc39b3 | ||
|
|
b1a8f445c6 | ||
|
|
5435df4345 | ||
|
|
8e9d29e94f | ||
|
|
1afa761f09 | ||
|
|
d626913ce9 | ||
|
|
9c52e4a5ee | ||
|
|
72c2c1992b | ||
|
|
e1b4b5e8e5 | ||
|
|
0243522854 | ||
|
|
5118c68f45 | ||
|
|
442884b5c5 |
21
.dockerignore
Normal file
21
.dockerignore
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
.*
|
||||||
|
bin/
|
||||||
|
dev/
|
||||||
|
spec/
|
||||||
|
*.md
|
||||||
|
Dockerfile
|
||||||
|
|
||||||
|
## TEMP
|
||||||
|
.idea/
|
||||||
|
.yardoc/
|
||||||
|
bundle/
|
||||||
|
cache/
|
||||||
|
coverage/
|
||||||
|
git/
|
||||||
|
**/*.md
|
||||||
|
**/*.orig
|
||||||
|
*.orig
|
||||||
|
CREDITS
|
||||||
|
data.zip
|
||||||
|
DISCLAIMER.txt
|
||||||
|
example.conf.json
|
||||||
32
.gitignore
vendored
32
.gitignore
vendored
@@ -1,15 +1,21 @@
|
|||||||
cache
|
# WPScan (If not using ~/.wpscan/)
|
||||||
coverage
|
cache/
|
||||||
|
data/
|
||||||
|
log.txt
|
||||||
|
output.txt
|
||||||
|
|
||||||
|
# WPScan (Deployment)
|
||||||
|
debug.log
|
||||||
|
rspec_results.html
|
||||||
|
wordlist.txt
|
||||||
|
|
||||||
|
# OS/IDE Rubbish
|
||||||
|
coverage/
|
||||||
|
.yardoc/
|
||||||
|
.idea/
|
||||||
|
*.sublime-*
|
||||||
|
.*.swp
|
||||||
|
.ash_history
|
||||||
.bundle
|
.bundle
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.DS_Store?
|
.DS_Store?
|
||||||
*.sublime-*
|
|
||||||
.idea
|
|
||||||
.*.swp
|
|
||||||
Gemfile.lock
|
|
||||||
log.txt
|
|
||||||
.yardoc
|
|
||||||
debug.log
|
|
||||||
wordlist.txt
|
|
||||||
rspec_results.html
|
|
||||||
data/
|
|
||||||
@@ -1 +1 @@
|
|||||||
2.3.1
|
2.5.1
|
||||||
|
|||||||
19
.travis.yml
19
.travis.yml
@@ -2,8 +2,7 @@ language: ruby
|
|||||||
sudo: false
|
sudo: false
|
||||||
cache: bundler
|
cache: bundler
|
||||||
rvm:
|
rvm:
|
||||||
# Still not in Travis :(
|
- 2.1.9
|
||||||
# - 2.1.9
|
|
||||||
- 2.2.0
|
- 2.2.0
|
||||||
- 2.2.1
|
- 2.2.1
|
||||||
- 2.2.2
|
- 2.2.2
|
||||||
@@ -11,9 +10,23 @@ rvm:
|
|||||||
- 2.2.4
|
- 2.2.4
|
||||||
- 2.3.0
|
- 2.3.0
|
||||||
- 2.3.1
|
- 2.3.1
|
||||||
|
- 2.3.2
|
||||||
|
- 2.3.3
|
||||||
|
- 2.4.1
|
||||||
|
- 2.4.2
|
||||||
|
- 2.5.0
|
||||||
|
- 2.5.1
|
||||||
|
- ruby-head
|
||||||
before_install:
|
before_install:
|
||||||
|
- "env"
|
||||||
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
|
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
|
||||||
script: bundle exec rspec
|
- "gem install bundler"
|
||||||
|
- "gem regenerate_binstubs"
|
||||||
|
- "bundle --version"
|
||||||
|
before_script:
|
||||||
|
- "unzip -o $TRAVIS_BUILD_DIR/data.zip -d $HOME/.wpscan/"
|
||||||
|
script:
|
||||||
|
- "bundle exec rspec"
|
||||||
notifications:
|
notifications:
|
||||||
email:
|
email:
|
||||||
- team@wpscan.org
|
- team@wpscan.org
|
||||||
|
|||||||
77
CHANGELOG.md
77
CHANGELOG.md
@@ -1,6 +1,81 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
## Master
|
## Master
|
||||||
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.9.1...master)
|
[Work in progress](https://github.com/wpscanteam/wpscan/compare/2.9.4...master)
|
||||||
|
|
||||||
|
## Version 2.9.4
|
||||||
|
Released: 2018-06-15
|
||||||
|
|
||||||
|
* Updated dependencies and required ruby version
|
||||||
|
* Improved CLI output
|
||||||
|
* Only show readme.html output when wp <= 4.8 #1127
|
||||||
|
* Cleanup README.md
|
||||||
|
* Fix bug "undefined method 'identifier' for nil:NilClass" #1149
|
||||||
|
* Since WP 4.7 readme.html only shows major version #1152
|
||||||
|
* Add checks for humans.txt and security.text (Thank you @g0tmi1k!)
|
||||||
|
* Add offline database update support (Thank you @g0tmi1k!)
|
||||||
|
* Check for API access and /wp-json/'s users output (Thank you @g0tmi1k!)
|
||||||
|
* Add RSS author information (Thank you @g0tmi1k!)
|
||||||
|
* Check HTTP status of each value in /robots.txt (Thank you @g0tmi1k!)
|
||||||
|
* Follow any redirections (e.g. http -> https) (Thank you @g0tmi1k!)
|
||||||
|
* Lots of other enhancements by @g0tmi1k & WPScan Team
|
||||||
|
* Database export file enumeration.
|
||||||
|
|
||||||
|
WPScan Database Statistics:
|
||||||
|
* Total tracked wordpresses: 319
|
||||||
|
* Total tracked plugins: 74896
|
||||||
|
* Total tracked themes: 16666
|
||||||
|
* Total vulnerable wordpresses: 305
|
||||||
|
* Total vulnerable plugins: 1645
|
||||||
|
* Total vulnerable themes: 286
|
||||||
|
* Total wordpress vulnerabilities: 8327
|
||||||
|
* Total plugin vulnerabilities: 2603
|
||||||
|
* Total theme vulnerabilities: 352
|
||||||
|
|
||||||
|
## Version 2.9.3
|
||||||
|
Released: 2017-07-19
|
||||||
|
|
||||||
|
* Updated dependencies and required ruby version
|
||||||
|
* Made some changes so wpscan works in ruby 2.4
|
||||||
|
* Added a Gemfile.lock to lock all dependencies
|
||||||
|
* You can now pass a wordlist from stdin via "--wordlist -"
|
||||||
|
* Improved version detection regexes
|
||||||
|
* Added an optional paramter to --log to specify a filename
|
||||||
|
|
||||||
|
WPScan Database Statistics:
|
||||||
|
* Total tracked wordpresses: 251
|
||||||
|
* Total tracked plugins: 68818
|
||||||
|
* Total tracked themes: 15132
|
||||||
|
* Total vulnerable wordpresses: 243
|
||||||
|
* Total vulnerable plugins: 1527
|
||||||
|
* Total vulnerable themes: 280
|
||||||
|
* Total wordpress vulnerabilities: 5263
|
||||||
|
* Total plugin vulnerabilities: 2406
|
||||||
|
* Total theme vulnerabilities: 349
|
||||||
|
|
||||||
|
## 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
|
## Version 2.9.1
|
||||||
Released: 2016-05-06
|
Released: 2016-05-06
|
||||||
|
|||||||
21
CREDITS
21
CREDITS
@@ -1,21 +0,0 @@
|
|||||||
**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 team@wpscan.org.
|
|
||||||
|
|
||||||
*WPScan Team*
|
|
||||||
|
|
||||||
Erwan.LR - @erwan_lr - (Project Developer)
|
|
||||||
Christian Mehlmauer - @_FireFart_ - (Project Developer)
|
|
||||||
Peter van der Laan - pvdl - (Project Developer)
|
|
||||||
Ryan Dewhurst - @ethicalhack3r (Project Lead)
|
|
||||||
|
|
||||||
*Other Contributors*
|
|
||||||
|
|
||||||
Henri Salo AKA fgeek - Reported lots of vulnerabilities
|
|
||||||
Alip AKA Undead - alip.aswalid at gmail.com
|
|
||||||
michee08 - Reported and gave potential solutions to bugs
|
|
||||||
Callum Pember - Implemented proxy support - callumpember at gmail.com
|
|
||||||
g0tmi1k - Additional timthumb checks + bug reports
|
|
||||||
Melvin Lammerts - Reported a couple of fake vulnerabilities - melvin at 12k.nl
|
|
||||||
Paolo Perego - @thesp0nge - Basic authentication
|
|
||||||
Gianluca Brindisi - @gbrindisi - Ex Project Developer
|
|
||||||
37
Dockerfile
Normal file
37
Dockerfile
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
FROM ruby:2.5-alpine
|
||||||
|
LABEL maintainer="WPScan Team <team@wpscan.org>"
|
||||||
|
|
||||||
|
ARG BUNDLER_ARGS="--jobs=8 --without test"
|
||||||
|
|
||||||
|
# Add a new user
|
||||||
|
RUN adduser -h /wpscan -g WPScan -D wpscan
|
||||||
|
|
||||||
|
# Setup gems
|
||||||
|
RUN echo "gem: --no-ri --no-rdoc" > /etc/gemrc
|
||||||
|
|
||||||
|
COPY Gemfile /wpscan
|
||||||
|
COPY Gemfile.lock /wpscan
|
||||||
|
|
||||||
|
# Runtime dependencies
|
||||||
|
RUN apk add --no-cache libcurl procps && \
|
||||||
|
# build dependencies
|
||||||
|
apk add --no-cache --virtual build-deps alpine-sdk ruby-dev libffi-dev zlib-dev && \
|
||||||
|
bundle install --system --gemfile=/wpscan/Gemfile $BUNDLER_ARGS && \
|
||||||
|
apk del --no-cache build-deps
|
||||||
|
|
||||||
|
# Copy over data & set permissions
|
||||||
|
COPY . /wpscan
|
||||||
|
RUN chown -R wpscan:wpscan /wpscan
|
||||||
|
|
||||||
|
# Switch directory
|
||||||
|
WORKDIR /wpscan
|
||||||
|
|
||||||
|
# Switch users
|
||||||
|
USER wpscan
|
||||||
|
|
||||||
|
# Update WPScan
|
||||||
|
RUN /wpscan/wpscan.rb --update --verbose --no-color
|
||||||
|
|
||||||
|
# Run WPScan
|
||||||
|
ENTRYPOINT ["/wpscan/wpscan.rb"]
|
||||||
|
CMD ["--help"]
|
||||||
24
Gemfile
24
Gemfile
@@ -1,18 +1,16 @@
|
|||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem 'typhoeus', '>=0.8.0'
|
gem 'addressable', '>=2.5.0'
|
||||||
gem 'nokogiri', '>=1.6.7.1'
|
gem 'nokogiri', '>=1.7.0.1'
|
||||||
gem 'addressable'
|
gem 'ruby-progressbar', '>=1.8.1'
|
||||||
gem 'yajl-ruby' # Better JSON parser regarding memory usage
|
gem 'rubyzip', '>=1.2.1'
|
||||||
# TODO: update the below when terminal-table 1.5.3+ is released.
|
gem 'terminal-table', '>=1.6.0'
|
||||||
# See issue #841 for details
|
gem 'typhoeus', '>=1.1.2'
|
||||||
# (and delete the Terminal module in lib/common/hacks.rb)
|
gem 'yajl-ruby', '>=1.3.0' # Better JSON parser regarding memory usage
|
||||||
gem 'terminal-table', '~>1.4.5'
|
|
||||||
gem 'ruby-progressbar', '>=1.6.0'
|
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'webmock', '>=1.17.2'
|
gem 'webmock', '>=2.3.2'
|
||||||
gem 'simplecov'
|
gem 'simplecov', '>=0.13.0'
|
||||||
gem 'rspec', '>=3.3.0'
|
gem 'rspec', '>=3.5.0'
|
||||||
gem 'rspec-its'
|
gem 'rspec-its', '>=1.2.0'
|
||||||
end
|
end
|
||||||
|
|||||||
8
LICENSE
8
LICENSE
@@ -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-2016 WPScan Team.
|
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2018 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.
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ Cases that include commercialization of WPScan require a commercial, non-free li
|
|||||||
|
|
||||||
1.1 “License” means this document.
|
1.1 “License” means this document.
|
||||||
1.2 “Contributor” means each individual or legal entity that creates, contributes to the creation of, or owns WPScan.
|
1.2 “Contributor” means each individual or legal entity that creates, contributes to the creation of, or owns WPScan.
|
||||||
1.3 “WPScan Team” means WPScan’s core developers, an updated list of whom can be found within the CREDITS file.
|
1.3 “WPScan Team” means WPScan’s core developers.
|
||||||
|
|
||||||
2. Commercialization
|
2. Commercialization
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|||||||
213
README.md
213
README.md
@@ -1,19 +1,21 @@
|
|||||||

|

|
||||||
|
|
||||||
|
|
||||||
[](https://travis-ci.org/wpscanteam/wpscan)
|
[](https://travis-ci.org/wpscanteam/wpscan)
|
||||||
[](https://codeclimate.com/github/wpscanteam/wpscan)
|
[](https://codeclimate.com/github/wpscanteam/wpscan)
|
||||||
[](https://gemnasium.com/wpscanteam/wpscan)
|
[](https://hub.docker.com/r/wpscanteam/wpscan/)
|
||||||
|
[](https://www.patreon.com/wpscan)
|
||||||
|
|
||||||
#### LICENSE
|

|
||||||
|
|
||||||
#### WPScan Public Source License
|
# LICENSE
|
||||||
|
|
||||||
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2016 WPScan Team.
|
## WPScan Public Source License
|
||||||
|
|
||||||
|
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2018 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 +23,7 @@ Cases that include commercialization of WPScan require a commercial, non-free li
|
|||||||
|
|
||||||
1.3 "WPScan Team" means WPScan’s core developers, an updated list of whom can be found within the CREDITS file.
|
1.3 "WPScan Team" means WPScan’s 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 +46,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 +54,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,115 +96,104 @@ 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:
|
On macOS WPScan is packaged by [Homebrew](https://brew.sh/) as [`wpscan`](http://braumeister.org/formula/wpscan).
|
||||||
|
|
||||||
- Ruby >= 2.1.9 - Recommended: 2.3.1
|
Windows is not supported
|
||||||
|
|
||||||
|
We suggest you use our official Docker image from https://hub.docker.com/r/wpscanteam/wpscan/ to avoid installation problems.
|
||||||
|
|
||||||
|
# DOCKER
|
||||||
|
## Install Docker
|
||||||
|
[https://docs.docker.com/engine/installation/](https://docs.docker.com/engine/installation/)
|
||||||
|
|
||||||
|
## Get the image
|
||||||
|
Pull the repo with `docker pull wpscanteam/wpscan`
|
||||||
|
|
||||||
|
## Start WPScan
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm wpscanteam/wpscan -u https://yourblog.com [options]
|
||||||
|
```
|
||||||
|
|
||||||
|
For the available Options, please see https://github.com/wpscanteam/wpscan#wpscan-arguments
|
||||||
|
|
||||||
|
If you run the git version of wpscan we included some binstubs in ./bin for easier start of wpscan.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Mount a local wordlist to the docker container and start a bruteforce attack for user admin
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it --rm -v ~/wordlists:/wordlists wpscanteam/wpscan --url https://yourblog.com --wordlist /wordlists/crackstation.txt --username admin
|
||||||
|
```
|
||||||
|
|
||||||
|
(This mounts the host directory `~/wordlists` to the container in the path `/wordlists`)
|
||||||
|
|
||||||
|
Use logfile option
|
||||||
|
```
|
||||||
|
# the file must exist prior to starting the container, otherwise docker will create a directory with the filename
|
||||||
|
touch ~/FILENAME
|
||||||
|
docker run -it --rm -v ~/FILENAME:/wpscan/output.txt wpscanteam/wpscan --url https://yourblog.com --log /wpscan/output.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Published on https://hub.docker.com/r/wpscanteam/wpscan/
|
||||||
|
|
||||||
|
# Manual install
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Ruby >= 2.1.9 - Recommended: 2.5.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 gcc 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
|
|
||||||
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-openssl-dev make zlib1g-dev
|
|
||||||
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 dnf install gcc ruby-devel libxml2 libxml2-devel libxslt libxslt-devel libcurl-devel patch rpm-build
|
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 Archlinux:
|
### Installing dependencies on Arch Linux
|
||||||
|
|
||||||
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 macOS
|
||||||
|
|
||||||
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 when doing a manual install)
|
||||||
cd wpscan
|
|
||||||
sudo gem install bundler && sudo bundle install --without test
|
|
||||||
|
|
||||||
####Installing with RVM (recommended):
|
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)
|
# 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.3.1
|
rvm install 2.5.1
|
||||||
rvm use 2.3.1 --default
|
rvm use 2.5.1 --default
|
||||||
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
|
echo "gem: --no-ri --no-rdoc" > ~/.gemrc
|
||||||
gem install bundler
|
|
||||||
git clone https://github.com/wpscanteam/wpscan.git
|
git clone https://github.com/wpscanteam/wpscan.git
|
||||||
cd wpscan
|
cd wpscan
|
||||||
gem install bundler
|
gem install bundler
|
||||||
bundle install --without test
|
bundle install --without test
|
||||||
|
|
||||||
#### KNOWN ISSUES
|
## Installing manually (not recommended)
|
||||||
|
|
||||||
- Typhoeus segmentation fault
|
git clone https://github.com/wpscanteam/wpscan.git
|
||||||
|
cd wpscan
|
||||||
|
sudo gem install bundler && bundle install --without test
|
||||||
|
|
||||||
Update cURL to version => 7.21 (may have to install from source)
|
# KNOWN ISSUES
|
||||||
|
|
||||||
- Proxy not working
|
|
||||||
|
|
||||||
Update cURL to version => 7.21.7 (may have to install from source).
|
|
||||||
|
|
||||||
Installation from sources :
|
|
||||||
|
|
||||||
Grab the sources from http://curl.haxx.se/download.html
|
|
||||||
Decompress the archive
|
|
||||||
Open the folder with the extracted files
|
|
||||||
Run ./configure
|
|
||||||
Run make
|
|
||||||
Run sudo make install
|
|
||||||
Run sudo ldconfig
|
|
||||||
|
|
||||||
|
|
||||||
- cannot load such file -- readline:
|
|
||||||
|
|
||||||
sudo aptitude install libreadline5-dev libncurses5-dev
|
|
||||||
|
|
||||||
Then, open the directory of the readline gem (you have to locate it)
|
|
||||||
|
|
||||||
cd ~/.rvm/src/ruby-XXXX/ext/readline
|
|
||||||
ruby extconf.rb
|
|
||||||
make
|
|
||||||
make install
|
|
||||||
|
|
||||||
|
|
||||||
See [http://vvv.tobiassjosten.net/ruby-on-rails/fixing-readline-for-the-ruby-on-rails-console/](http://vvv.tobiassjosten.net/ruby-on-rails/fixing-readline-for-the-ruby-on-rails-console/) for more details
|
|
||||||
|
|
||||||
- no such file to load -- rubygems
|
- no such file to load -- rubygems
|
||||||
|
|
||||||
@@ -208,7 +203,7 @@ 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 database to the latest version.
|
--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.
|
||||||
@@ -232,12 +227,17 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
|||||||
You do not need to provide the regexp delimiters, but you must write the quotes (simple or double).
|
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.
|
--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.
|
--user-agent | -a <User-Agent> Use the specified User-Agent.
|
||||||
--cookie <String> String to read cookies from.
|
--cookie <string> String to read cookies from.
|
||||||
--random-agent | -r Use a random 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
|
--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.
|
--batch Never ask for user input, use the default behaviour.
|
||||||
--no-color Do not use colors in the output.
|
--no-color Do not use colors in the output.
|
||||||
--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.
|
--log [filename] Creates a log.txt file with WPScan's output if no filename is supplied. Otherwise the filename is used for logging.
|
||||||
|
--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.
|
Subdirectories are allowed.
|
||||||
--wp-plugins-dir <wp plugins dir> Same thing than --wp-content-dir but for the plugins directory.
|
--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
|
If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed
|
||||||
@@ -246,19 +246,20 @@ Apple Xcode, Command Line Tools and the libffi are needed (to be able to install
|
|||||||
--proxy-auth <username:password> Supply the proxy login credentials.
|
--proxy-auth <username:password> Supply the proxy login credentials.
|
||||||
--basic-auth <username:password> Set the HTTP Basic authentication.
|
--basic-auth <username:password> Set the HTTP Basic authentication.
|
||||||
--wordlist | -w <wordlist> Supply a wordlist for the password brute forcer.
|
--wordlist | -w <wordlist> Supply a wordlist for the password brute forcer.
|
||||||
|
If the "-" option is supplied, the wordlist is expected via STDIN.
|
||||||
--username | -U <username> Only brute force the supplied username.
|
--username | -U <username> Only brute force the supplied username.
|
||||||
--usernames <path-to-file> Only brute force the usernames from the file.
|
--usernames <path-to-file> Only brute force the usernames from the file.
|
||||||
--threads | -t <number of threads> The number of threads to use when multi-threading requests.
|
--cache-dir <cache-directory> Set the cache directory.
|
||||||
--cache-ttl <cache-ttl> Typhoeus cache TTL.
|
--cache-ttl <cache-ttl> Typhoeus cache TTL.
|
||||||
--request-timeout <request-timeout> Request Timeout.
|
--request-timeout <request-timeout> Request Timeout.
|
||||||
--connect-timeout <connect-timeout> Connect Timeout.
|
--connect-timeout <connect-timeout> Connect Timeout.
|
||||||
--max-threads <max-threads> Maximum Threads.
|
--threads | -t <number of threads> The number of threads to use when multi-threading requests.
|
||||||
--throttle <milliseconds> Milliseconds to wait before doing another web request. If used, the --threads should be set to 1.
|
--throttle <milliseconds> Milliseconds to wait before doing another web request. If used, the --threads should be set to 1.
|
||||||
--help | -h This help screen.
|
--help | -h This help screen.
|
||||||
--verbose | -v Verbose output.
|
--verbose | -v Verbose output.
|
||||||
--version Output the current version and exit.
|
--version Output the current version and exit.
|
||||||
|
|
||||||
#### WPSCAN EXAMPLES
|
# WPSCAN EXAMPLES
|
||||||
|
|
||||||
Do 'non-intrusive' checks...
|
Do 'non-intrusive' checks...
|
||||||
|
|
||||||
@@ -268,6 +269,10 @@ Do wordlist password brute force on enumerated users using 50 threads...
|
|||||||
|
|
||||||
```ruby wpscan.rb --url www.example.com --wordlist darkc0de.lst --threads 50```
|
```ruby wpscan.rb --url www.example.com --wordlist darkc0de.lst --threads 50```
|
||||||
|
|
||||||
|
Do wordlist password brute force on enumerated users using STDIN as the wordlist...
|
||||||
|
|
||||||
|
```crunch 5 13 -f charset.lst mixalpha | ruby wpscan.rb --url www.example.com --wordlist -```
|
||||||
|
|
||||||
Do wordlist password brute force on the 'admin' username only...
|
Do wordlist password brute force on the 'admin' username only...
|
||||||
|
|
||||||
```ruby wpscan.rb --url www.example.com --wordlist darkc0de.lst --username admin```
|
```ruby wpscan.rb --url www.example.com --wordlist darkc0de.lst --username admin```
|
||||||
@@ -292,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)
|
|
||||||
|
|||||||
21
bin/rspec
Executable file
21
bin/rspec
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SOURCE="${BASH_SOURCE[0]}"
|
||||||
|
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
SOURCE="$(readlink "$SOURCE")"
|
||||||
|
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||||
|
done
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
|
||||||
|
cd $DIR/../
|
||||||
|
# always rebuild and include all GEMs
|
||||||
|
docker build --build-arg "BUNDLER_ARGS=--jobs=8" -t wpscan:rspec .
|
||||||
|
# update all gems (this updates Gemfile.lock on the host)
|
||||||
|
# this also needs some build dependencies
|
||||||
|
docker run --rm -u root -v $DIR/../Gemfile.lock:/wpscan/Gemfile.lock --entrypoint "" wpscan:rspec sh -c 'apk add --no-cache alpine-sdk ruby-dev libffi-dev zlib-dev && bundle update'
|
||||||
|
# rebuild image with latest GEMs
|
||||||
|
docker build --build-arg "BUNDLER_ARGS=--jobs=8" -t wpscan:rspec .
|
||||||
|
# run spec
|
||||||
|
docker run --rm -v $DIR/../:/wpscan --entrypoint "" wpscan:rspec rspec
|
||||||
|
|
||||||
12
bin/update_gems
Executable file
12
bin/update_gems
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SOURCE="${BASH_SOURCE[0]}"
|
||||||
|
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
SOURCE="$(readlink "$SOURCE")"
|
||||||
|
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||||
|
done
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
|
||||||
|
cd $DIR/../
|
||||||
|
docker run --rm -v "$DIR/../":/usr/src/app -w /usr/src/app ruby:2.5-alpine /bin/sh -c "gem install bundler; bundle lock --update"
|
||||||
14
bin/wpscan
Executable file
14
bin/wpscan
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SOURCE="${BASH_SOURCE[0]}"
|
||||||
|
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
SOURCE="$(readlink "$SOURCE")"
|
||||||
|
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||||
|
done
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
|
||||||
|
cd $DIR/../
|
||||||
|
docker build -q -t wpscan:git .
|
||||||
|
docker run -it --rm wpscan:git "$@"
|
||||||
|
|
||||||
16
bin/wpscan-dev
Executable file
16
bin/wpscan-dev
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SOURCE="${BASH_SOURCE[0]}"
|
||||||
|
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
SOURCE="$(readlink "$SOURCE")"
|
||||||
|
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||||
|
done
|
||||||
|
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
|
||||||
|
|
||||||
|
cd $DIR/../
|
||||||
|
if [[ -n "$WPSCAN_BUILD" ]]; then
|
||||||
|
docker build -q -t wpscan:git .
|
||||||
|
fi
|
||||||
|
docker run -it --rm -v $DIR/../:/wpscan wpscan:git "$@"
|
||||||
|
|
||||||
2
data/.gitignore
vendored
2
data/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
require File.dirname(__FILE__) + '/../lib/wpscan/wpscan_helper'
|
require File.expand_path(File.join(__dir__, '..', 'lib', 'wpscan', 'wpscan_helper'))
|
||||||
|
|
||||||
wordpress_json = json(WORDPRESSES_FILE)
|
wordpress_json = json(WORDPRESSES_FILE)
|
||||||
plugins_json = json(PLUGINS_FILE)
|
plugins_json = json(PLUGINS_FILE)
|
||||||
@@ -14,6 +14,6 @@ puts "* Total tracked themes: #{themes_json.count}"
|
|||||||
puts "* Total vulnerable wordpresses: #{wordpress_json.select { |item| !wordpress_json[item]['vulnerabilities'].empty? }.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 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 vulnerable themes: #{themes_json.select { |item| !themes_json[item]['vulnerabilities'].empty? }.count}"
|
||||||
puts "* Total wordpress vulnerabilities: #{}"
|
puts "* Total wordpress vulnerabilities: #{wordpress_json.map {|k,v| v['vulnerabilities'].count}.inject(:+)}"
|
||||||
puts "* Total plugin vulnerabilities: #{}"
|
puts "* Total plugin vulnerabilities: #{plugins_json.map {|k,v| v['vulnerabilities'].count}.inject(:+)}"
|
||||||
puts "* Total theme vulnerabilities: #{}"
|
puts "* Total theme vulnerabilities: #{themes_json.map {|k,v| v['vulnerabilities'].count}.inject(:+)}"
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,15 +124,9 @@ 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!(proxyuserpwd: @proxy_auth) if @proxy_auth
|
||||||
end
|
end
|
||||||
|
|
||||||
if @basic_auth
|
if @basic_auth
|
||||||
@@ -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) unless params.key?(:ssl_verifypeer)
|
if @disable_tls_checks
|
||||||
params.merge!(ssl_verifyhost: 0) unless params.key?(:ssl_verifyhost)
|
# 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
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
class Browser
|
class Browser
|
||||||
module Options
|
module Options
|
||||||
|
|
||||||
attr_accessor :request_timeout, :connect_timeout
|
attr_accessor :request_timeout, :connect_timeout, :user_agent, :disable_accept_header, :disable_referer, :disable_tls_checks
|
||||||
attr_reader :basic_auth, :cache_ttl, :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:
|
||||||
@@ -21,7 +20,7 @@ class Browser
|
|||||||
elsif auth =~ /\ABasic [a-zA-Z0-9=]+\z/
|
elsif auth =~ /\ABasic [a-zA-Z0-9=]+\z/
|
||||||
@basic_auth = auth
|
@basic_auth = auth
|
||||||
else
|
else
|
||||||
raise 'Invalid basic authentication format, "login:password" or "Basic base_64_encoded" expected'
|
raise "Invalid basic authentication format, \"login:password\" or \"Basic base_64_encoded\" expected. Your input: #{auth}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ class CacheFileStore
|
|||||||
unless Dir.exist?(@storage_path)
|
unless Dir.exist?(@storage_path)
|
||||||
FileUtils.mkdir_p(@storage_path)
|
FileUtils.mkdir_p(@storage_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unless Pathname.new(@storage_path).writable?
|
||||||
|
fail "#{@storage_path} is not writable"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def clean
|
def clean
|
||||||
|
|||||||
0
lib/common/collections/wp_items.rb
Executable file → Normal file
0
lib/common/collections/wp_items.rb
Executable file → Normal file
6
lib/common/collections/wp_items/detectable.rb
Executable file → Normal file
6
lib/common/collections/wp_items/detectable.rb
Executable file → Normal 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
0
lib/common/collections/wp_plugins.rb
Executable file → Normal 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
0
lib/common/collections/wp_themes.rb
Executable file → Normal file
0
lib/common/collections/wp_timthumbs.rb
Executable file → Normal file
0
lib/common/collections/wp_timthumbs.rb
Executable file → Normal 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
0
lib/common/collections/wp_users.rb
Executable file → Normal file
0
lib/common/collections/wp_users/detectable.rb
Executable file → Normal file
0
lib/common/collections/wp_users/detectable.rb
Executable file → Normal file
@@ -9,7 +9,7 @@ class WpUsers < WpItems
|
|||||||
# @return [ void ]
|
# @return [ void ]
|
||||||
def output(options = {})
|
def output(options = {})
|
||||||
rows = []
|
rows = []
|
||||||
headings = ['Id', 'Login', 'Name']
|
headings = ['ID', 'Login', 'Name']
|
||||||
headings << 'Password' if options[:show_password]
|
headings << 'Password' if options[:show_password]
|
||||||
|
|
||||||
remove_junk_from_display_names
|
remove_junk_from_display_names
|
||||||
|
|||||||
@@ -1,36 +1,34 @@
|
|||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
LIB_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
# Location directories
|
||||||
ROOT_DIR = File.expand_path(File.join(LIB_DIR, '..')) # expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
|
LIB_DIR = File.expand_path(File.join(__dir__, '..')) # wpscan/lib/
|
||||||
DATA_DIR = File.join(ROOT_DIR, 'data')
|
ROOT_DIR = File.expand_path(File.join(LIB_DIR, '..')) # wpscan/ - expand_path is used to get "wpscan/" instead of "wpscan/lib/../"
|
||||||
CONF_DIR = File.join(ROOT_DIR, 'conf')
|
USER_DIR = File.expand_path(Dir.home) # ~/
|
||||||
CACHE_DIR = File.join(ROOT_DIR, 'cache')
|
|
||||||
WPSCAN_LIB_DIR = File.join(LIB_DIR, 'wpscan')
|
|
||||||
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')
|
|
||||||
COLLECTIONS_LIB_DIR = File.join(COMMON_LIB_DIR, 'collections')
|
|
||||||
|
|
||||||
LOG_FILE = File.join(ROOT_DIR, 'log.txt')
|
# Core WPScan directories
|
||||||
|
CACHE_DIR = File.join(USER_DIR, '.wpscan/cache') # ~/.wpscan/cache/
|
||||||
|
DATA_DIR = File.join(USER_DIR, '.wpscan/data') # ~/.wpscan/data/
|
||||||
|
CONF_DIR = File.join(USER_DIR, '.wpscan/conf') # ~/.wpscan/conf/ - Not used ATM (only ref via ./spec/ for travis)
|
||||||
|
COMMON_LIB_DIR = File.join(LIB_DIR, 'common') # wpscan/lib/common/
|
||||||
|
WPSCAN_LIB_DIR = File.join(LIB_DIR, 'wpscan') # wpscan/lib/wpscan/
|
||||||
|
MODELS_LIB_DIR = File.join(COMMON_LIB_DIR, 'models') # wpscan/lib/common/models/
|
||||||
|
|
||||||
# Plugins directories
|
# Core WPScan files
|
||||||
COMMON_PLUGINS_DIR = File.join(COMMON_LIB_DIR, 'plugins')
|
DEFAULT_LOG_FILE = File.join(USER_DIR, '.wpscan/log.txt') # ~/.wpscan/log.txt
|
||||||
WPSCAN_PLUGINS_DIR = File.join(WPSCAN_LIB_DIR, 'plugins') # Not used ATM
|
DATA_FILE = File.join(ROOT_DIR, 'data.zip') # wpscan/data.zip
|
||||||
|
|
||||||
# Data files
|
# WPScan Data files (data.zip)
|
||||||
WORDPRESSES_FILE = File.join(DATA_DIR, 'wordpresses.json')
|
LAST_UPDATE_FILE = File.join(DATA_DIR, '.last_update') # ~/.wpscan/data/.last_update
|
||||||
PLUGINS_FILE = File.join(DATA_DIR, 'plugins.json')
|
PLUGINS_FILE = File.join(DATA_DIR, 'plugins.json') # ~/.wpscan/data/plugins.json
|
||||||
THEMES_FILE = File.join(DATA_DIR, 'themes.json')
|
THEMES_FILE = File.join(DATA_DIR, 'themes.json') # ~/.wpscan/data/themes.json
|
||||||
WP_VERSIONS_FILE = File.join(DATA_DIR, 'wp_versions.xml')
|
TIMTHUMBS_FILE = File.join(DATA_DIR, 'timthumbs.txt') # ~/.wpscan/data/timthumbs.txt
|
||||||
LOCAL_FILES_FILE = File.join(DATA_DIR, 'local_vulnerable_files.xml')
|
USER_AGENTS_FILE = File.join(DATA_DIR, 'user-agents.txt') # ~/.wpscan/data/user-agents.txt
|
||||||
WP_VERSIONS_XSD = File.join(DATA_DIR, 'wp_versions.xsd')
|
WORDPRESSES_FILE = File.join(DATA_DIR, 'wordpresses.json') # ~/.wpscan/data/wordpresses.json
|
||||||
LOCAL_FILES_XSD = File.join(DATA_DIR, 'local_vulnerable_files.xsd')
|
WP_VERSIONS_FILE = File.join(DATA_DIR, 'wp_versions.xml') # ~/.wpscan/data/wp_versions.xml
|
||||||
USER_AGENTS_FILE = File.join(DATA_DIR, 'user-agents.txt')
|
|
||||||
LAST_UPDATE_FILE = File.join(DATA_DIR, '.last_update')
|
|
||||||
|
|
||||||
MIN_RUBY_VERSION = '2.1.9'
|
MIN_RUBY_VERSION = '2.1.9'
|
||||||
|
|
||||||
WPSCAN_VERSION = '2.9.1'
|
WPSCAN_VERSION = '2.9.5-dev'
|
||||||
|
|
||||||
$LOAD_PATH.unshift(LIB_DIR)
|
$LOAD_PATH.unshift(LIB_DIR)
|
||||||
$LOAD_PATH.unshift(WPSCAN_LIB_DIR)
|
$LOAD_PATH.unshift(WPSCAN_LIB_DIR)
|
||||||
@@ -50,6 +48,7 @@ def windows?
|
|||||||
end
|
end
|
||||||
|
|
||||||
require 'environment'
|
require 'environment'
|
||||||
|
require 'zip'
|
||||||
|
|
||||||
def escape_glob(s)
|
def escape_glob(s)
|
||||||
s.gsub(/[\\\{\}\[\]\*\?]/) { |x| '\\' + x }
|
s.gsub(/[\\\{\}\[\]\*\?]/) { |x| '\\' + x }
|
||||||
@@ -78,13 +77,39 @@ def add_trailing_slash(url)
|
|||||||
url =~ /\/$/ ? url : "#{url}/"
|
url =~ /\/$/ ? url : "#{url}/"
|
||||||
end
|
end
|
||||||
|
|
||||||
def missing_db_file?
|
def missing_db_files?
|
||||||
DbUpdater::FILES.each do |db_file|
|
DbUpdater::FILES.each do |db_file|
|
||||||
return true unless File.exist?(File.join(DATA_DIR, db_file))
|
return true unless File.exist?(File.join(DATA_DIR, db_file))
|
||||||
end
|
end
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Find data.zip?
|
||||||
|
def has_db_zip?
|
||||||
|
return File.exist?(DATA_FILE)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Extract data.zip
|
||||||
|
def extract_db_zip
|
||||||
|
# Create data folder
|
||||||
|
FileUtils.mkdir_p(DATA_DIR)
|
||||||
|
|
||||||
|
Zip::File.open(DATA_FILE) do |zip_file|
|
||||||
|
zip_file.each do |f|
|
||||||
|
# Feedback to the user
|
||||||
|
#puts "[+] Extracting: #{File.basename(f.name)}"
|
||||||
|
f_path = File.join(DATA_DIR, File.basename(f.name))
|
||||||
|
|
||||||
|
# Delete if already there
|
||||||
|
#puts "[+] Deleting: #{File.basename(f.name)}" if File.exist?(f_path)
|
||||||
|
FileUtils.rm(f_path) if File.exist?(f_path)
|
||||||
|
|
||||||
|
# Extract
|
||||||
|
zip_file.extract(f, f_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def last_update
|
def last_update
|
||||||
date = nil
|
date = nil
|
||||||
if File.exists?(LAST_UPDATE_FILE)
|
if File.exists?(LAST_UPDATE_FILE)
|
||||||
@@ -94,9 +119,12 @@ def last_update
|
|||||||
date
|
date
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Was it 5 days ago?
|
||||||
def update_required?
|
def update_required?
|
||||||
date = last_update
|
date = last_update
|
||||||
(true if date.nil?) or (date < 5.days.ago)
|
day_seconds = 24 * 60 * 60
|
||||||
|
five_days_ago = Time.now - (5 * day_seconds)
|
||||||
|
(true if date.nil?) or (date < five_days_ago)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define colors
|
# Define colors
|
||||||
@@ -151,7 +179,7 @@ def banner
|
|||||||
puts '_______________________________________________________________'
|
puts '_______________________________________________________________'
|
||||||
puts ' __ _______ _____ '
|
puts ' __ _______ _____ '
|
||||||
puts ' \\ \\ / / __ \\ / ____| '
|
puts ' \\ \\ / / __ \\ / ____| '
|
||||||
puts ' \\ \\ /\\ / /| |__) | (___ ___ __ _ _ __ '
|
puts ' \\ \\ /\\ / /| |__) | (___ ___ __ _ _ __ ®'
|
||||||
puts ' \\ \\/ \\/ / | ___/ \\___ \\ / __|/ _` | \'_ \\ '
|
puts ' \\ \\/ \\/ / | ___/ \\___ \\ / __|/ _` | \'_ \\ '
|
||||||
puts ' \\ /\\ / | | ____) | (__| (_| | | | |'
|
puts ' \\ /\\ / | | ____) | (__| (_| | | | |'
|
||||||
puts ' \\/ \\/ |_| |_____/ \\___|\\__,_|_| |_|'
|
puts ' \\/ \\/ |_| |_____/ \\___|\\__,_|_| |_|'
|
||||||
@@ -159,7 +187,7 @@ def banner
|
|||||||
puts ' WordPress Security Scanner by the WPScan Team '
|
puts ' WordPress Security Scanner by the WPScan Team '
|
||||||
puts " Version #{WPSCAN_VERSION}"
|
puts " Version #{WPSCAN_VERSION}"
|
||||||
puts ' Sponsored by Sucuri - https://sucuri.net'
|
puts ' Sponsored by Sucuri - https://sucuri.net'
|
||||||
puts ' @_WPScan_, @ethicalhack3r, @erwan_lr, pvdl, @_FireFart_'
|
puts ' @_WPScan_, @ethicalhack3r, @erwan_lr, @_FireFart_'
|
||||||
puts '_______________________________________________________________'
|
puts '_______________________________________________________________'
|
||||||
puts
|
puts
|
||||||
end
|
end
|
||||||
@@ -251,14 +279,26 @@ end
|
|||||||
# @return [ String ] A random user-agent from data/user-agents.txt
|
# @return [ String ] A random user-agent from data/user-agents.txt
|
||||||
def get_random_user_agent
|
def get_random_user_agent
|
||||||
user_agents = []
|
user_agents = []
|
||||||
|
|
||||||
|
# If we can't access the file, die
|
||||||
|
raise('[ERROR] Missing user-agent data. Please re-run with just --update.') unless File.exist?(USER_AGENTS_FILE)
|
||||||
|
|
||||||
|
# Read in file
|
||||||
f = File.open(USER_AGENTS_FILE, 'r')
|
f = File.open(USER_AGENTS_FILE, 'r')
|
||||||
|
|
||||||
|
# Read every line in the file
|
||||||
f.each_line do |line|
|
f.each_line do |line|
|
||||||
# ignore comments
|
# Remove any End of Line issues, and leading/trailing spaces
|
||||||
|
line = line.strip.chomp
|
||||||
|
# Ignore empty files and comments
|
||||||
next if line.empty? or line =~ /^\s*(#|\/\/)/
|
next if line.empty? or line =~ /^\s*(#|\/\/)/
|
||||||
|
# Add to array
|
||||||
user_agents << line.strip
|
user_agents << line.strip
|
||||||
end
|
end
|
||||||
|
# Close file handler
|
||||||
f.close
|
f.close
|
||||||
# return ransom user-agent
|
|
||||||
|
# Return random user-agent
|
||||||
user_agents.sample
|
user_agents.sample
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -272,3 +312,21 @@ end
|
|||||||
def url_encode(str)
|
def url_encode(str)
|
||||||
CGI.escape(str).gsub("+", "%20")
|
CGI.escape(str).gsub("+", "%20")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Check valid JSON?
|
||||||
|
def valid_json?(json)
|
||||||
|
JSON.parse(json)
|
||||||
|
return true
|
||||||
|
rescue JSON::ParserError => e
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the HTTP response code
|
||||||
|
def get_http_status(url)
|
||||||
|
Browser.get(url.to_s).code
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check to see if we need a "s"
|
||||||
|
def grammar_s(size)
|
||||||
|
size.to_i >= 2 ? "s" : ""
|
||||||
|
end
|
||||||
|
|||||||
@@ -13,8 +13,13 @@ class DbUpdater
|
|||||||
def initialize(repo_directory)
|
def initialize(repo_directory)
|
||||||
@repo_directory = repo_directory
|
@repo_directory = repo_directory
|
||||||
|
|
||||||
fail "#{repo_directory} is not writable" unless \
|
unless Dir.exist?(@repo_directory)
|
||||||
Pathname.new(repo_directory).writable?
|
FileUtils.mkdir_p(@repo_directory)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless Pathname.new(@repo_directory).writable?
|
||||||
|
fail "#{@repo_directory} is not writable"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [ Hash ] The params for Typhoeus::Request
|
# @return [ Hash ] The params for Typhoeus::Request
|
||||||
@@ -83,7 +88,7 @@ class DbUpdater
|
|||||||
def update(verbose = false)
|
def update(verbose = false)
|
||||||
FILES.each do |filename|
|
FILES.each do |filename|
|
||||||
begin
|
begin
|
||||||
puts "[+] Checking #{filename}" if verbose
|
puts "[+] Checking: #{filename}" if verbose
|
||||||
db_checksum = remote_file_checksum(filename)
|
db_checksum = remote_file_checksum(filename)
|
||||||
|
|
||||||
# Checking if the file needs to be updated
|
# Checking if the file needs to be updated
|
||||||
@@ -95,13 +100,13 @@ class DbUpdater
|
|||||||
puts ' [i] Needs to be updated' if verbose
|
puts ' [i] Needs to be updated' if verbose
|
||||||
create_backup(filename)
|
create_backup(filename)
|
||||||
puts ' [i] Backup Created' if verbose
|
puts ' [i] Backup Created' if verbose
|
||||||
puts ' [i] Downloading new file' if verbose
|
puts " [i] Downloading new file: #{remote_file_url(filename)}" if verbose
|
||||||
dl_checksum = download(filename)
|
dl_checksum = download(filename)
|
||||||
puts " [i] Downloaded File Checksum: #{dl_checksum}" if verbose
|
puts " [i] Downloaded File Checksum: #{dl_checksum}" if verbose
|
||||||
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 (local: #{dl_checksum} remote: #{db_checksum})"
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -21,53 +21,12 @@ end
|
|||||||
def puts(o = '')
|
def puts(o = '')
|
||||||
if $log && o.respond_to?(:gsub)
|
if $log && o.respond_to?(:gsub)
|
||||||
temp = o.gsub(/\e\[\d+m/, '') # remove color for logging
|
temp = o.gsub(/\e\[\d+m/, '') # remove color for logging
|
||||||
File.open(LOG_FILE, 'a+') { |f| f.puts(temp) }
|
File.open($log, 'a+') { |f| f.puts(temp) }
|
||||||
end
|
end
|
||||||
|
|
||||||
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}
|
||||||
@@ -76,16 +35,3 @@ class Numeric
|
|||||||
s.sub(/\.?0*$/, ' ' + units[e])
|
s.sub(/\.?0*$/, ' ' + units[e])
|
||||||
end
|
end
|
||||||
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
|
|
||||||
|
|||||||
0
lib/common/models/vulnerability.rb
Executable file → Normal file
0
lib/common/models/vulnerability.rb
Executable file → Normal file
2
lib/common/models/wp_item.rb
Executable file → Normal file
2
lib/common/models/wp_item.rb
Executable file → Normal file
@@ -51,7 +51,7 @@ class WpItem
|
|||||||
end
|
end
|
||||||
|
|
||||||
def last_updated
|
def last_updated
|
||||||
db_data['last_ipdated']
|
db_data['last_updated']
|
||||||
end
|
end
|
||||||
|
|
||||||
def popular?
|
def popular?
|
||||||
|
|||||||
0
lib/common/models/wp_item/existable.rb
Executable file → Normal file
0
lib/common/models/wp_item/existable.rb
Executable file → Normal file
3
lib/common/models/wp_item/findable.rb
Executable file → Normal file
3
lib/common/models/wp_item/findable.rb
Executable file → Normal 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
|
||||||
|
|||||||
@@ -5,27 +5,6 @@ class WpItem
|
|||||||
# @uri is used instead of #uri to avoid the presence of the :path into it
|
# @uri is used instead of #uri to avoid the presence of the :path into it
|
||||||
module Infos
|
module Infos
|
||||||
|
|
||||||
# @return [ Boolean ]
|
|
||||||
def has_readme?
|
|
||||||
!readme_url.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [ String,nil ] The url to the readme file, nil if not found
|
|
||||||
def readme_url
|
|
||||||
# See https://github.com/wpscanteam/wpscan/pull/737#issuecomment-66375445
|
|
||||||
# for any question about the order
|
|
||||||
%w{readme.txt README.txt Readme.txt ReadMe.txt README.TXT readme.TXT}.each do |readme|
|
|
||||||
url = @uri.merge(readme).to_s
|
|
||||||
return url if url_is_200?(url)
|
|
||||||
end
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# @return [ Boolean ]
|
|
||||||
def has_changelog?
|
|
||||||
url_is_200?(changelog_url)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Checks if the url status code is 200
|
# Checks if the url status code is 200
|
||||||
#
|
#
|
||||||
# @param [ String ] url
|
# @param [ String ] url
|
||||||
@@ -35,9 +14,34 @@ class WpItem
|
|||||||
Browser.get(url).code == 200
|
Browser.get(url).code == 200
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def has_readme?
|
||||||
|
!readme_url.nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String,nil ] The url to the readme file, nil if not found
|
||||||
|
def readme_url
|
||||||
|
# See https://github.com/wpscanteam/wpscan/pull/737#issuecomment-66375445
|
||||||
|
# for any question about the order
|
||||||
|
%w{readme.txt README.txt README.md readme.md Readme.txt}.each do |readme|
|
||||||
|
url = @uri.merge(readme).to_s
|
||||||
|
return url if url_is_200?(url)
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def has_changelog?
|
||||||
|
!changelog_url.nil?
|
||||||
|
end
|
||||||
|
|
||||||
# @return [ String ] The url to the changelog file
|
# @return [ String ] The url to the changelog file
|
||||||
def changelog_url
|
def changelog_url
|
||||||
@uri.merge('changelog.txt').to_s
|
%w{changelog.txt CHANGELOG.md changelog.md}.each do |changelog|
|
||||||
|
url = @uri.merge(changelog).to_s
|
||||||
|
return url if url_is_200?(url)
|
||||||
|
end
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [ Boolean ]
|
# @return [ Boolean ]
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class WpItem
|
|||||||
|
|
||||||
if version.nil? && vulnerabilities.length > 0
|
if version.nil? && vulnerabilities.length > 0
|
||||||
puts
|
puts
|
||||||
puts warning('We could not determine a version so all vulnerabilities are printed out')
|
puts warning('We could not determine the version installed. All of the past known vulnerabilities will be output to allow you to do your own manual investigation.')
|
||||||
end
|
end
|
||||||
|
|
||||||
vulnerabilities.output
|
vulnerabilities.output
|
||||||
|
|||||||
0
lib/common/models/wp_item/versionable.rb
Executable file → Normal file
0
lib/common/models/wp_item/versionable.rb
Executable file → Normal file
0
lib/common/models/wp_item/vulnerable.rb
Executable file → Normal file
0
lib/common/models/wp_item/vulnerable.rb
Executable file → Normal file
0
lib/common/models/wp_plugin.rb
Executable file → Normal file
0
lib/common/models/wp_plugin.rb
Executable file → Normal file
0
lib/common/models/wp_theme.rb
Executable file → Normal file
0
lib/common/models/wp_theme.rb
Executable file → Normal file
4
lib/common/models/wp_theme/findable.rb
Executable file → Normal file
4
lib/common/models/wp_theme/findable.rb
Executable file → Normal 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
|
||||||
@@ -30,7 +30,7 @@ class WpTheme < WpItem
|
|||||||
response = Browser.get_and_follow_location(target_uri.to_s)
|
response = Browser.get_and_follow_location(target_uri.to_s)
|
||||||
|
|
||||||
# https + domain is optional because of relative links
|
# https + domain is optional because of relative links
|
||||||
return unless response.body =~ %r{(?:https?://[^"']+/)?([^/\s]+)/themes/([^"'/]+)[^"']*/style.css}i
|
return unless response.body =~ %r{(?:https?://[^"']+/)?([^"'/\s]+)/themes/([^"'/]+)[^"']*/style\.css}i
|
||||||
|
|
||||||
new(
|
new(
|
||||||
target_uri,
|
target_uri,
|
||||||
|
|||||||
0
lib/common/models/wp_theme/versionable.rb
Executable file → Normal file
0
lib/common/models/wp_theme/versionable.rb
Executable file → Normal file
0
lib/common/models/wp_timthumb.rb
Executable file → Normal file
0
lib/common/models/wp_timthumb.rb
Executable file → Normal file
0
lib/common/models/wp_timthumb/versionable.rb
Executable file → Normal file
0
lib/common/models/wp_timthumb/versionable.rb
Executable file → Normal file
0
lib/common/models/wp_user.rb
Executable file → Normal file
0
lib/common/models/wp_user.rb
Executable file → Normal file
@@ -28,9 +28,18 @@ class WpUser < WpItem
|
|||||||
queue_count = 0
|
queue_count = 0
|
||||||
found = false
|
found = false
|
||||||
|
|
||||||
create_progress_bar(count_file_lines(wordlist)+1, options)
|
if wordlist == '-'
|
||||||
|
words = ARGF
|
||||||
|
passwords_size = 10
|
||||||
|
options[:starting_at] = 0
|
||||||
|
else
|
||||||
|
words = File.open(wordlist)
|
||||||
|
passwords_size = count_file_lines(wordlist)+1
|
||||||
|
end
|
||||||
|
|
||||||
File.open(wordlist).each do |password|
|
create_progress_bar(passwords_size, options)
|
||||||
|
|
||||||
|
words.each do |password|
|
||||||
password.chomp!
|
password.chomp!
|
||||||
|
|
||||||
# A successfull login will redirect us to the redirect_to parameter
|
# A successfull login will redirect us to the redirect_to parameter
|
||||||
@@ -43,7 +52,13 @@ class WpUser < WpItem
|
|||||||
request = login_request(password, redirect_url)
|
request = login_request(password, redirect_url)
|
||||||
|
|
||||||
request.on_complete do |response|
|
request.on_complete do |response|
|
||||||
progress_bar.progress += 1 if options[:show_progression] && !found
|
if options[:show_progression] && !found
|
||||||
|
progress_bar.progress += 1
|
||||||
|
percentage = progress_bar.progress.fdiv(progress_bar.total)
|
||||||
|
if options[:starting_at] && percentage >= 0.8
|
||||||
|
progress_bar.total *= 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
progress_bar.log(" Trying Username: #{login} Password: #{password}") if options[:verbose]
|
progress_bar.log(" Trying Username: #{login} Password: #{password}") if options[:verbose]
|
||||||
|
|
||||||
@@ -79,7 +94,8 @@ class WpUser < WpItem
|
|||||||
@progress_bar = 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,
|
||||||
|
starting_at: options[:starting_at]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -118,7 +134,7 @@ class WpUser < WpItem
|
|||||||
elsif response.code.to_s =~ /^50/
|
elsif response.code.to_s =~ /^50/
|
||||||
progression = critical('ERROR: Server error, try reducing the number of threads or use the --throttle option.')
|
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 login: #{login} and password: #{password}")
|
||||||
verbose = critical(" Code: #{response.code}\n Body: #{response.body}\n")
|
verbose = critical(" Code: #{response.code}\n Body: #{response.body}\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
0
lib/common/models/wp_user/existable.rb
Executable file → Normal file
0
lib/common/models/wp_user/existable.rb
Executable file → Normal file
7
lib/common/models/wp_version.rb
Executable file → Normal file
7
lib/common/models/wp_version.rb
Executable file → Normal file
@@ -41,8 +41,11 @@ class WpVersion < WpItem
|
|||||||
json = json(db_file)
|
json = json(db_file)
|
||||||
|
|
||||||
metadata = {}
|
metadata = {}
|
||||||
metadata[:release_date] = json[version]['release_date']
|
temp = json[version]
|
||||||
metadata[:changelog_url] = json[version]['changelog_url']
|
if !temp.nil?
|
||||||
|
metadata[:release_date] = temp['release_date']
|
||||||
|
metadata[:changelog_url] = temp['changelog_url']
|
||||||
|
end
|
||||||
metadata
|
metadata
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
27
lib/common/models/wp_version/findable.rb
Executable file → Normal file
27
lib/common/models/wp_version/findable.rb
Executable file → Normal 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
|
||||||
|
|
||||||
@@ -155,11 +168,14 @@ class WpVersion < WpItem
|
|||||||
#
|
#
|
||||||
# @return [ String ] The version number
|
# @return [ String ] The version number
|
||||||
def find_from_readme(target_uri)
|
def find_from_readme(target_uri)
|
||||||
scan_url(
|
version = scan_url(
|
||||||
target_uri,
|
target_uri,
|
||||||
%r{<br />\sversion #{version_pattern}}i,
|
%r{<br />\sversion #{version_pattern}}i,
|
||||||
'readme.html'
|
'readme.html'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Since WP >= 4.7, the Readme only contains the major version
|
||||||
|
VersionCompare.lesser?(version, '4.7') ? version : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Attempts to find the WordPress version from the sitemap.xml file.
|
# Attempts to find the WordPress version from the sitemap.xml file.
|
||||||
@@ -199,7 +215,12 @@ class WpVersion < WpItem
|
|||||||
|
|
||||||
next if attr_value.nil? || attr_value.empty?
|
next if attr_value.nil? || attr_value.empty?
|
||||||
|
|
||||||
uri = Addressable::URI.parse(attr_value)
|
begin
|
||||||
|
uri = Addressable::URI.parse(attr_value)
|
||||||
|
rescue Addressable::URI::InvalidURIError
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
next unless uri.query && uri.query.match(pattern)
|
next unless uri.query && uri.query.match(pattern)
|
||||||
|
|
||||||
version = Regexp.last_match[1].to_s
|
version = Regexp.last_match[1].to_s
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class WpVersion < WpItem
|
|||||||
puts " | Released: #{metadata[:release_date]}"
|
puts " | Released: #{metadata[:release_date]}"
|
||||||
puts " | Changelog: #{metadata[:changelog_url]}"
|
puts " | Changelog: #{metadata[:changelog_url]}"
|
||||||
else
|
else
|
||||||
puts info("WordPress version #{self.number} identified from #{self.found_from} #{"(Released on #{metadata[:release_date]})" if metadata[:release_date]}")
|
puts info("WordPress version #{self.number} #{"(Released on #{metadata[:release_date]}) identified from #{self.found_from}" if metadata[:release_date]}")
|
||||||
end
|
end
|
||||||
|
|
||||||
vulnerabilities = self.vulnerabilities
|
vulnerabilities = self.vulnerabilities
|
||||||
|
|||||||
@@ -14,16 +14,15 @@ Encoding.default_external = Encoding::UTF_8
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
# Standard libs
|
# Standard libs
|
||||||
|
require 'readline'
|
||||||
require 'bundler/setup' unless kali_linux?
|
require 'bundler/setup' unless kali_linux?
|
||||||
require 'getoptlong'
|
require 'getoptlong'
|
||||||
require 'optparse' # Will replace getoptlong
|
require 'optparse' # Will replace getoptlong
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'time'
|
require 'time'
|
||||||
require 'resolv'
|
require 'resolv'
|
||||||
require 'xmlrpc/client'
|
|
||||||
require 'digest/md5'
|
require 'digest/md5'
|
||||||
require 'digest/sha1'
|
require 'digest/sha1'
|
||||||
require 'readline'
|
|
||||||
require 'base64'
|
require 'base64'
|
||||||
require 'rbconfig'
|
require 'rbconfig'
|
||||||
require 'pp'
|
require 'pp'
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
require 'web_site/robots_txt'
|
require 'web_site/humans_txt'
|
||||||
require 'web_site/interesting_headers'
|
require 'web_site/interesting_headers'
|
||||||
|
require 'web_site/robots_txt'
|
||||||
|
require 'web_site/security_txt'
|
||||||
|
require 'web_site/sitemap'
|
||||||
|
require 'web_site/sql_file_export'
|
||||||
|
|
||||||
class WebSite
|
class WebSite
|
||||||
include WebSite::RobotsTxt
|
include WebSite::HumansTxt
|
||||||
include WebSite::InterestingHeaders
|
include WebSite::InterestingHeaders
|
||||||
|
include WebSite::RobotsTxt
|
||||||
|
include WebSite::SecurityTxt
|
||||||
|
include WebSite::Sitemap
|
||||||
|
include WebSite::SqlFileExport
|
||||||
|
|
||||||
attr_reader :uri
|
attr_reader :uri
|
||||||
|
|
||||||
@@ -21,6 +29,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 +99,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
|
||||||
@@ -95,13 +129,6 @@ class WebSite
|
|||||||
@error_404_hash
|
@error_404_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
# Will try to find the rss url in the homepage
|
|
||||||
# Only the first one found is returned
|
|
||||||
def rss_url
|
|
||||||
homepage_body = Browser.get(@uri.to_s).body
|
|
||||||
homepage_body[%r{<link .* type="application/rss\+xml" .* href="([^"]+)" />}, 1]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Only the first 700 bytes are checked to avoid the download
|
# Only the first 700 bytes are checked to avoid the download
|
||||||
# of the whole file which can be very huge (like 2 Go)
|
# of the whole file which can be very huge (like 2 Go)
|
||||||
#
|
#
|
||||||
|
|||||||
13
lib/wpscan/web_site/humans_txt.rb
Normal file
13
lib/wpscan/web_site/humans_txt.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WebSite
|
||||||
|
module HumansTxt
|
||||||
|
|
||||||
|
# Gets the humans.txt URL
|
||||||
|
# @return [ String ]
|
||||||
|
def humans_url
|
||||||
|
@uri.clone.merge('humans.txt').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -18,49 +18,53 @@ class WebSite
|
|||||||
# Parse robots.txt
|
# Parse robots.txt
|
||||||
# @return [ Array ] URLs generated from robots.txt
|
# @return [ Array ] URLs generated from robots.txt
|
||||||
def parse_robots_txt
|
def parse_robots_txt
|
||||||
return unless has_robots?
|
|
||||||
|
|
||||||
return_object = []
|
return_object = []
|
||||||
|
|
||||||
|
# Make request
|
||||||
response = Browser.get(robots_url.to_s)
|
response = Browser.get(robots_url.to_s)
|
||||||
body = response.body
|
body = response.body
|
||||||
|
|
||||||
# Get all allow and disallow urls
|
# Get all allow and disallow urls
|
||||||
entries = body.scan(/^(?:dis)?allow:\s*(.*)$/i)
|
entries = body.scan(/^(?:dis)?allow:\s*(.*)$/i)
|
||||||
|
|
||||||
|
# Did we get something?
|
||||||
if entries
|
if entries
|
||||||
entries.flatten!
|
# Remove any rubbish
|
||||||
entries.compact.sort!
|
entries = clean_uri(entries)
|
||||||
entries.uniq!
|
|
||||||
|
# Sort
|
||||||
|
entries.sort!
|
||||||
|
|
||||||
|
# Wordpress URL
|
||||||
wordpress_path = @uri.path
|
wordpress_path = @uri.path
|
||||||
|
|
||||||
|
# Each "boring" value as defined below, remove
|
||||||
RobotsTxt.known_dirs.each do |d|
|
RobotsTxt.known_dirs.each do |d|
|
||||||
entries.delete(d)
|
entries.delete(d)
|
||||||
# also delete when wordpress is installed in subdir
|
# Also delete when wordpress is installed in subdir
|
||||||
dir_with_subdir = "#{wordpress_path}/#{d}".gsub(/\/+/, '/')
|
dir_with_subdir = "#{wordpress_path}/#{d}".gsub(/\/+/, '/')
|
||||||
entries.delete(dir_with_subdir)
|
entries.delete(dir_with_subdir)
|
||||||
end
|
end
|
||||||
|
|
||||||
entries.each do |d|
|
# Convert to full URIs
|
||||||
begin
|
return_object = full_uri(entries)
|
||||||
temp = @uri.clone
|
|
||||||
temp.path = d.strip
|
|
||||||
rescue URI::Error
|
|
||||||
temp = d.strip
|
|
||||||
end
|
|
||||||
return_object << temp.to_s
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return_object
|
return return_object
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
# Useful ~ "function do_robots()" -> https://github.com/WordPress/WordPress/blob/master/wp-includes/functions.php
|
||||||
|
#
|
||||||
# @return [ Array ]
|
# @return [ Array ]
|
||||||
def self.known_dirs
|
def self.known_dirs
|
||||||
%w{
|
%w{
|
||||||
/
|
/
|
||||||
/wp-admin/
|
/wp-admin/
|
||||||
|
/wp-admin/admin-ajax.php
|
||||||
/wp-includes/
|
/wp-includes/
|
||||||
/wp-content/
|
/wp-content/
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
13
lib/wpscan/web_site/security_txt.rb
Normal file
13
lib/wpscan/web_site/security_txt.rb
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WebSite
|
||||||
|
module SecurityTxt
|
||||||
|
|
||||||
|
# Gets the security.txt URL
|
||||||
|
# @return [ String ]
|
||||||
|
def security_url
|
||||||
|
@uri.clone.merge('.well-known/security.txt').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
53
lib/wpscan/web_site/sitemap.rb
Normal file
53
lib/wpscan/web_site/sitemap.rb
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WebSite
|
||||||
|
module Sitemap
|
||||||
|
|
||||||
|
# Checks if a sitemap.txt file exists
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def has_sitemap?
|
||||||
|
# Make the request
|
||||||
|
response = Browser.get(sitemap_url)
|
||||||
|
|
||||||
|
# Make sure its HTTP 200
|
||||||
|
return false unless response.code == 200
|
||||||
|
|
||||||
|
# Is there a sitemap value?
|
||||||
|
result = response.body.scan(/^sitemap\s*:\s*(.*)$/i)
|
||||||
|
return true if result[0]
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the robots.txt URL
|
||||||
|
# @return [ String ]
|
||||||
|
def sitemap_url
|
||||||
|
@uri.clone.merge('robots.txt').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse robots.txt
|
||||||
|
# @return [ Array ] URLs generated from robots.txt
|
||||||
|
def parse_sitemap
|
||||||
|
return_object = []
|
||||||
|
|
||||||
|
# Make request
|
||||||
|
response = Browser.get(sitemap_url.to_s)
|
||||||
|
|
||||||
|
# Get all allow and disallow urls
|
||||||
|
entries = response.body.scan(/^sitemap\s*:\s*(.*)$/i)
|
||||||
|
|
||||||
|
# Did we get something?
|
||||||
|
if entries
|
||||||
|
# Remove any rubbish
|
||||||
|
entries = clean_uri(entries)
|
||||||
|
|
||||||
|
# Sort
|
||||||
|
entries.sort!
|
||||||
|
|
||||||
|
# Convert to full URIs
|
||||||
|
return_object = full_uri(entries)
|
||||||
|
end
|
||||||
|
return return_object
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
35
lib/wpscan/web_site/sql_file_export.rb
Normal file
35
lib/wpscan/web_site/sql_file_export.rb
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WebSite
|
||||||
|
module SqlFileExport
|
||||||
|
|
||||||
|
# Checks if a .sql file exists
|
||||||
|
# @return [ Array ]
|
||||||
|
def sql_file_export
|
||||||
|
export_files = []
|
||||||
|
|
||||||
|
self.sql_file_export_urls.each do |url|
|
||||||
|
response = Browser.get(url)
|
||||||
|
export_files << url if response.code == 200 && response.body =~ /INSERT INTO/
|
||||||
|
end
|
||||||
|
|
||||||
|
export_files
|
||||||
|
end
|
||||||
|
|
||||||
|
# Gets a .sql export file URL
|
||||||
|
# @return [ Array ]
|
||||||
|
def sql_file_export_urls
|
||||||
|
urls = []
|
||||||
|
host = @uri.host[/(^[\w|-]+)/,1]
|
||||||
|
|
||||||
|
files = ["#{host}.sql", "#{host}.sql.gz", "#{host}.zip", 'db.sql', 'site.sql', 'database.sql',
|
||||||
|
'data.sql', 'backup.sql', 'dump.sql', 'db_backup.sql', 'dbdump.sql', 'wordpress.sql', 'mysql.sql']
|
||||||
|
|
||||||
|
files.each do |file|
|
||||||
|
urls << @uri.clone.merge(file).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
urls
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,22 +1,26 @@
|
|||||||
# encoding: UTF-8
|
# encoding: UTF-8
|
||||||
|
|
||||||
require 'web_site'
|
require 'web_site'
|
||||||
require 'wp_target/wp_readme'
|
require 'wp_target/wp_api'
|
||||||
require 'wp_target/wp_registrable'
|
|
||||||
require 'wp_target/wp_config_backup'
|
require 'wp_target/wp_config_backup'
|
||||||
require 'wp_target/wp_must_use_plugins'
|
|
||||||
require 'wp_target/wp_login_protection'
|
|
||||||
require 'wp_target/wp_custom_directories'
|
require 'wp_target/wp_custom_directories'
|
||||||
require 'wp_target/wp_full_path_disclosure'
|
require 'wp_target/wp_full_path_disclosure'
|
||||||
|
require 'wp_target/wp_login_protection'
|
||||||
|
require 'wp_target/wp_must_use_plugins'
|
||||||
|
require 'wp_target/wp_readme'
|
||||||
|
require 'wp_target/wp_registrable'
|
||||||
|
require 'wp_target/wp_rss'
|
||||||
|
|
||||||
class WpTarget < WebSite
|
class WpTarget < WebSite
|
||||||
include WpTarget::WpReadme
|
include WpTarget::WpAPI
|
||||||
include WpTarget::WpRegistrable
|
|
||||||
include WpTarget::WpConfigBackup
|
include WpTarget::WpConfigBackup
|
||||||
include WpTarget::WpMustUsePlugins
|
|
||||||
include WpTarget::WpLoginProtection
|
|
||||||
include WpTarget::WpCustomDirectories
|
include WpTarget::WpCustomDirectories
|
||||||
include WpTarget::WpFullPathDisclosure
|
include WpTarget::WpFullPathDisclosure
|
||||||
|
include WpTarget::WpLoginProtection
|
||||||
|
include WpTarget::WpMustUsePlugins
|
||||||
|
include WpTarget::WpReadme
|
||||||
|
include WpTarget::WpRegistrable
|
||||||
|
include WpTarget::WpRSS
|
||||||
|
|
||||||
attr_reader :verbose
|
attr_reader :verbose
|
||||||
|
|
||||||
@@ -155,6 +159,21 @@ class WpTarget < WebSite
|
|||||||
resp.code == 200 && resp.body[%r{by interconnect}i]
|
resp.code == 200 && resp.body[%r{by interconnect}i]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Script used to recover locked out admin users
|
||||||
|
# http://yoast.com/emergency-wordpress-access/
|
||||||
|
# https://codex.wordpress.org/User:MichaelH/Orphaned_Plugins_needing_Adoption/Emergency
|
||||||
|
#
|
||||||
|
# @return [ String ]
|
||||||
|
def emergency_url
|
||||||
|
@uri.merge('emergency.php').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def emergency_exists?
|
||||||
|
resp = Browser.get(emergency_url)
|
||||||
|
resp.code == 200 && resp.body[%r{password}i]
|
||||||
|
end
|
||||||
|
|
||||||
def upload_directory_listing_enabled?
|
def upload_directory_listing_enabled?
|
||||||
directory_listing_enabled?(upload_dir_url)
|
directory_listing_enabled?(upload_dir_url)
|
||||||
end
|
end
|
||||||
|
|||||||
86
lib/wpscan/wp_target/wp_api.rb
Normal file
86
lib/wpscan/wp_target/wp_api.rb
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module WpAPI
|
||||||
|
|
||||||
|
# Checks to see if the REST API is enabled
|
||||||
|
#
|
||||||
|
# This by default in a WordPress installation since 4.5+
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def has_api?(url)
|
||||||
|
# Make the request
|
||||||
|
response = Browser.get(url)
|
||||||
|
|
||||||
|
# Able to view the output?
|
||||||
|
if valid_json?(response.body) && response.body != ''
|
||||||
|
# Read in JSON
|
||||||
|
data = JSON.parse(response.body)
|
||||||
|
|
||||||
|
# If there is nothing there, return false
|
||||||
|
if data.empty?
|
||||||
|
return false
|
||||||
|
# WAF/API disabled response
|
||||||
|
elsif data.include?('message') and data['message'] =~ /Only authenticated users can access the REST API/
|
||||||
|
return false
|
||||||
|
# Success!
|
||||||
|
elsif response.code == 200
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Something went wrong
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String ] The API/JSON URL
|
||||||
|
def json_url
|
||||||
|
@uri.merge('/wp-json/').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String ] The API/JSON URL to show users
|
||||||
|
def json_users_url
|
||||||
|
@uri.merge('/wp-json/wp/v2/users').to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [ String ] The API/JSON URL to show users
|
||||||
|
def json_get_users(url)
|
||||||
|
# Variables
|
||||||
|
users = []
|
||||||
|
|
||||||
|
# Make the request
|
||||||
|
response = Browser.get(url)
|
||||||
|
|
||||||
|
# If not HTTP 200, return false
|
||||||
|
return false unless response.code == 200
|
||||||
|
|
||||||
|
# Able to view the output?
|
||||||
|
return false unless valid_json?(response.body)
|
||||||
|
|
||||||
|
# Read in JSON
|
||||||
|
data = JSON.parse(response.body)
|
||||||
|
|
||||||
|
# If there is nothing there, return false
|
||||||
|
return false if data.empty?
|
||||||
|
|
||||||
|
# Add to array
|
||||||
|
data.each do |child|
|
||||||
|
row = [ child['id'], child['name'], child['link'] ]
|
||||||
|
users << row
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sort and uniq
|
||||||
|
users = users.sort.uniq
|
||||||
|
|
||||||
|
if users and users.size >= 1
|
||||||
|
# Feedback
|
||||||
|
grammar = grammar_s(users.size)
|
||||||
|
puts warning("#{users.size} user#{grammar} exposed via API: #{json_users_url}")
|
||||||
|
|
||||||
|
# Print results
|
||||||
|
table = Terminal::Table.new(headings: ['ID', 'Name', 'URL'],
|
||||||
|
rows: users)
|
||||||
|
puts table
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
73
lib/wpscan/wp_target/wp_rss.rb
Normal file
73
lib/wpscan/wp_target/wp_rss.rb
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# encoding: UTF-8
|
||||||
|
|
||||||
|
class WpTarget < WebSite
|
||||||
|
module WpRSS
|
||||||
|
|
||||||
|
# Checks to see if there is an rss feed
|
||||||
|
# Will try to find the rss url in the homepage
|
||||||
|
# Only the first one found is returned
|
||||||
|
#
|
||||||
|
# This file comes by default in a WordPress installation
|
||||||
|
#
|
||||||
|
# @return [ Boolean ]
|
||||||
|
def rss_url
|
||||||
|
homepage_body = Browser.get(@uri.to_s).body
|
||||||
|
# Format: <link rel="alternate" type="application/rss+xml" title=".*" href=".*" />
|
||||||
|
homepage_body[%r{<link\s*.*\s*type=['|"]application\/rss\+xml['|"]\s*.*\stitle=".*" href=['|"]([^"]+)['|"]\s*\/?>}i, 1]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Gets all the authors from the RSS feed
|
||||||
|
#
|
||||||
|
# @return [ string ]
|
||||||
|
def rss_authors(url)
|
||||||
|
# Variables
|
||||||
|
users = []
|
||||||
|
|
||||||
|
# Make the request
|
||||||
|
response = Browser.get(url, followlocation: true)
|
||||||
|
|
||||||
|
# Valid repose to view? HTTP 200?
|
||||||
|
return false unless response.code == 200
|
||||||
|
|
||||||
|
# Get output
|
||||||
|
data = response.body
|
||||||
|
|
||||||
|
# If there is nothing there, return false
|
||||||
|
return false if data.empty?
|
||||||
|
|
||||||
|
begin
|
||||||
|
# Read in RSS/XML
|
||||||
|
xml = Nokogiri::XML(data)
|
||||||
|
rescue
|
||||||
|
puts critical("Missformed XML")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
# Look for <dc:creator> item
|
||||||
|
xml.xpath('//item/dc:creator').each do |node|
|
||||||
|
#Format: <dc:creator><![CDATA[.*]]></dc:creator>
|
||||||
|
users << [%r{.*}i.match(node).to_s]
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
puts critical("Missing Author field. Maybe non-standard WordPress RSS feed?")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sort and uniq
|
||||||
|
users = users.sort_by { |user| user.to_s.downcase }.uniq
|
||||||
|
|
||||||
|
if users and users.size >= 1
|
||||||
|
# Feedback
|
||||||
|
grammar = grammar_s(users.size)
|
||||||
|
puts warning("Detected #{users.size} user#{grammar} from RSS feed:")
|
||||||
|
|
||||||
|
# Print results
|
||||||
|
table = Terminal::Table.new(headings: ['Name'],
|
||||||
|
rows: users)
|
||||||
|
puts table
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -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')
|
||||||
|
|
||||||
@@ -28,9 +28,12 @@ def usage
|
|||||||
puts '-Enumerate installed themes ...'
|
puts '-Enumerate installed themes ...'
|
||||||
puts "ruby #{script_name} --url www.example.com --enumerate t"
|
puts "ruby #{script_name} --url www.example.com --enumerate t"
|
||||||
puts
|
puts
|
||||||
puts '-Enumerate users ...'
|
puts '-Enumerate users (from 1 - 10)...'
|
||||||
puts "ruby #{script_name} --url www.example.com --enumerate u"
|
puts "ruby #{script_name} --url www.example.com --enumerate u"
|
||||||
puts
|
puts
|
||||||
|
puts '-Enumerate users (from 1 - 20)...'
|
||||||
|
puts "ruby #{script_name} --url www.example.com --enumerate u[1-20]"
|
||||||
|
puts
|
||||||
puts '-Enumerate installed timthumbs ...'
|
puts '-Enumerate installed timthumbs ...'
|
||||||
puts "ruby #{script_name} --url www.example.com --enumerate tt"
|
puts "ruby #{script_name} --url www.example.com --enumerate tt"
|
||||||
puts
|
puts
|
||||||
@@ -46,7 +49,7 @@ def usage
|
|||||||
puts '-Use custom plugins directory ...'
|
puts '-Use custom plugins directory ...'
|
||||||
puts "ruby #{script_name} -u www.example.com --wp-plugins-dir wp-content/custom-plugins"
|
puts "ruby #{script_name} -u www.example.com --wp-plugins-dir wp-content/custom-plugins"
|
||||||
puts
|
puts
|
||||||
puts '-Update the DB ...'
|
puts '-Update the Database ...'
|
||||||
puts "ruby #{script_name} --update"
|
puts "ruby #{script_name} --update"
|
||||||
puts
|
puts
|
||||||
puts '-Debug output ...'
|
puts '-Debug output ...'
|
||||||
@@ -84,12 +87,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 [filename] Creates a log.txt file with WPScan\'s output if no filename is supplied. Otherwise the filename is used for logging.'
|
||||||
|
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,11 +108,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 '--max-threads <max-threads> Maximum Threads.'
|
puts '--threads | -t <number of threads> The number of threads to use when multi-threading requests.'
|
||||||
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.'
|
||||||
puts '--verbose | -v Verbose output.'
|
puts '--verbose | -v Verbose output.'
|
||||||
@@ -112,6 +120,58 @@ def help
|
|||||||
puts
|
puts
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def clean_uri(entries)
|
||||||
|
# Extract elements
|
||||||
|
entries.flatten!
|
||||||
|
# Remove any leading/trailing spaces
|
||||||
|
entries.collect{|x| x.strip || x }
|
||||||
|
# End Of Line issues
|
||||||
|
entries.collect{|x| x.chomp! || x }
|
||||||
|
# Remove nil's
|
||||||
|
entries.compact
|
||||||
|
# Unique values only
|
||||||
|
entries.uniq!
|
||||||
|
|
||||||
|
return entries
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the full URL
|
||||||
|
def full_uri(entries)
|
||||||
|
return_object = []
|
||||||
|
# Each value now, try and make it a full URL
|
||||||
|
entries.each do |d|
|
||||||
|
begin
|
||||||
|
temp = @uri.clone
|
||||||
|
temp.path = d.strip
|
||||||
|
rescue URI::Error
|
||||||
|
temp = d.strip
|
||||||
|
end
|
||||||
|
return_object << temp.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
return return_object
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse humans.txt
|
||||||
|
# @return [ Array ] URLs generated from humans.txt
|
||||||
|
def parse_txt(url)
|
||||||
|
return_object = []
|
||||||
|
response = Browser.get(url.to_s)
|
||||||
|
body = response.body
|
||||||
|
|
||||||
|
# Get all non-comments
|
||||||
|
entries = body.split(/\n/)
|
||||||
|
|
||||||
|
# Did we get something?
|
||||||
|
if entries
|
||||||
|
# Remove any rubbish
|
||||||
|
entries = clean_uri(entries)
|
||||||
|
end
|
||||||
|
return return_object
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# Hook to check if the target if down during the scan
|
# 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
|
# And have the number of requests performed to display at the end of the scan
|
||||||
# The target is considered down after 30 requests with status = 0
|
# The target is considered down after 30 requests with status = 0
|
||||||
@@ -130,3 +190,4 @@ Typhoeus.on_complete do |response|
|
|||||||
|
|
||||||
sleep(Browser.instance.throttle)
|
sleep(Browser.instance.throttle)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -71,7 +75,7 @@ class WpscanOptions
|
|||||||
end
|
end
|
||||||
|
|
||||||
def wordlist=(wordlist)
|
def wordlist=(wordlist)
|
||||||
if File.exists?(wordlist)
|
if File.exists?(wordlist) || wordlist == '-'
|
||||||
@wordlist = wordlist
|
@wordlist = wordlist
|
||||||
else
|
else
|
||||||
raise "The file #{wordlist} does not exist"
|
raise "The file #{wordlist} does not exist"
|
||||||
@@ -148,11 +152,6 @@ class WpscanOptions
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def basic_auth=(basic_auth)
|
|
||||||
raise 'Invalid basic authentication format, login:password expected' if basic_auth.index(':').nil?
|
|
||||||
@basic_auth = "Basic #{Base64.encode64(basic_auth).chomp}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def debug_output=(debug_output)
|
def debug_output=(debug_output)
|
||||||
Typhoeus::Config.verbose = debug_output
|
Typhoeus::Config.verbose = debug_output
|
||||||
end
|
end
|
||||||
@@ -208,7 +207,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
|
||||||
|
|
||||||
@@ -276,13 +277,16 @@ class WpscanOptions
|
|||||||
['--cache-ttl', GetoptLong::REQUIRED_ARGUMENT],
|
['--cache-ttl', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--request-timeout', GetoptLong::REQUIRED_ARGUMENT],
|
['--request-timeout', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--connect-timeout', GetoptLong::REQUIRED_ARGUMENT],
|
['--connect-timeout', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--max-threads', GetoptLong::REQUIRED_ARGUMENT],
|
|
||||||
['--batch', GetoptLong::NO_ARGUMENT],
|
['--batch', GetoptLong::NO_ARGUMENT],
|
||||||
['--no-color', GetoptLong::NO_ARGUMENT],
|
['--no-color', GetoptLong::NO_ARGUMENT],
|
||||||
['--cookie', GetoptLong::REQUIRED_ARGUMENT],
|
['--cookie', GetoptLong::REQUIRED_ARGUMENT],
|
||||||
['--log', GetoptLong::NO_ARGUMENT],
|
['--log', GetoptLong::OPTIONAL_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
|
||||||
|
|
||||||
|
|||||||
@@ -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) }
|
||||||
@@ -160,7 +168,7 @@ describe Browser do
|
|||||||
it 'sets the proxy_auth' do
|
it 'sets the proxy_auth' do
|
||||||
browser.proxy = proxy
|
browser.proxy = proxy
|
||||||
browser.proxy_auth = 'user:pass'
|
browser.proxy_auth = 'user:pass'
|
||||||
@expected = proxy_expectation.merge(proxyauth: 'user:pass')
|
@expected = proxy_expectation.merge(proxyuserpwd: 'user:pass')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ describe 'WpTimthumbs::Detectable' do
|
|||||||
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)
|
||||||
|
|||||||
@@ -45,9 +45,18 @@ describe 'WpTheme::Findable' do
|
|||||||
|
|
||||||
# FIXME: the style_url should be checked in WpTheme for absolute / relative
|
# FIXME: the style_url should be checked in WpTheme for absolute / relative
|
||||||
context 'when relative url is used' do
|
context 'when relative url is used' do
|
||||||
it 'returns the WpTheme' do
|
context 'when leading slash' do
|
||||||
@file = 'relative_urls.html'
|
it 'returns the WpTheme' do
|
||||||
@expected = WpTheme.new(uri, name: 'theme_name')
|
@file = 'relative_urls.html'
|
||||||
|
@expected = WpTheme.new(uri, name: 'theme_name')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when no leading slash' do
|
||||||
|
it 'returns the WpTheme' do
|
||||||
|
@file = 'relative_urls_missing_slash.html'
|
||||||
|
@expected = WpTheme.new(uri, name: 'theme_name')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,13 @@ describe 'WpVersion::Findable' do
|
|||||||
@fixture = '/3.3.2.html'
|
@fixture = '/3.3.2.html'
|
||||||
@expected = '3.3.2'
|
@expected = '3.3.2'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when version >= 4.7' do
|
||||||
|
it 'returns nil' do
|
||||||
|
@fixture = '/4.7.2.html'
|
||||||
|
@expected = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '::find_from_links_opml' do
|
describe '::find_from_links_opml' do
|
||||||
@@ -159,6 +166,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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -196,18 +207,6 @@ describe 'WebSite' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#rss_url' do
|
|
||||||
it 'returns nil if the url is not found' do
|
|
||||||
stub_request(:get, web_site.url).to_return(body: 'No RSS link in this body !')
|
|
||||||
expect(web_site.rss_url).to be_nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns 'http://lamp-wp/wordpress-3.5/?feed=rss2'" do
|
|
||||||
stub_request_to_fixture(url: web_site.url, fixture: fixtures_dir + '/rss_url/wordpress-3.5.htm')
|
|
||||||
expect(web_site.rss_url).to be === 'http://lamp-wp/wordpress-3.5/?feed=rss2'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '::has_log?' do
|
describe '::has_log?' do
|
||||||
let(:log_url) { web_site.uri.merge('log.txt').to_s }
|
let(:log_url) { web_site.uri.merge('log.txt').to_s }
|
||||||
let(:pattern) { %r{PHP Fatal error} }
|
let(:pattern) { %r{PHP Fatal error} }
|
||||||
|
|||||||
@@ -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) }
|
||||||
@@ -192,4 +192,27 @@ describe WpTarget do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#emergency_url' do
|
||||||
|
it 'returns the correct url' do
|
||||||
|
expect(wp_target.emergency_url).to eq 'http://example.localhost/emergency.php'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#emergency_exists?' do
|
||||||
|
it 'returns true' do
|
||||||
|
stub_request(:any, wp_target.emergency_url).to_return(status: 200, body: 'enter your password here')
|
||||||
|
expect(wp_target.emergency_exists?).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
stub_request(:any, wp_target.emergency_url).to_return(status: 500)
|
||||||
|
expect(wp_target.emergency_exists?).to be_falsey
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns false' do
|
||||||
|
stub_request(:any, wp_target.emergency_url).to_return(status: 500, body: 'enter your password here')
|
||||||
|
expect(wp_target.emergency_exists?).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -186,23 +186,6 @@ describe 'WpscanOptions' do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#basic_auth=' do
|
|
||||||
context 'invalid format' do
|
|
||||||
it 'should raise an error if the : is missing' do
|
|
||||||
expect { @wpscan_options.basic_auth = 'helloworld' }.to raise_error(
|
|
||||||
RuntimeError, 'Invalid basic authentication format, login:password expected'
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'valid format' do
|
|
||||||
it "should add the 'Basic' word and do the encode64. See RFC 2617" do
|
|
||||||
@wpscan_options.basic_auth = 'Aladdin:open sesame'
|
|
||||||
expect(@wpscan_options.basic_auth).to eq 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#has_options?' do
|
describe '#has_options?' do
|
||||||
it 'should return false' do
|
it 'should return false' do
|
||||||
expect(@wpscan_options.has_options?).to be_falsey
|
expect(@wpscan_options.has_options?).to be_falsey
|
||||||
|
|||||||
@@ -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
0
spec/samples/common/models/wp_item/error_log
Executable file → Normal file
0
spec/samples/common/models/wp_item/versionable/simple-login-lockdown-0.4.txt
Executable file → Normal file
0
spec/samples/common/models/wp_item/versionable/simple-login-lockdown-0.4.txt
Executable file → Normal file
0
spec/samples/common/models/wp_item/versionable/trunk-version.txt
Executable file → Normal file
0
spec/samples/common/models/wp_item/versionable/trunk-version.txt
Executable file → Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
=== WP Maintenance Mode ===
|
||||||
|
Contributors: Designmodo, GeorgeJipa
|
||||||
|
Plugin Name: WP Maintenance Mode
|
||||||
|
Plugin URI: http://designmodo.com/
|
||||||
|
Author: Designmodo
|
||||||
|
Author URI: http://designmodo.com/
|
||||||
|
Tags: maintenance mode, admin, administration, unavailable, coming soon, multisite, landing page, under construction, contact form, subscribe, countdown
|
||||||
|
Requires at least: 3.5
|
||||||
|
Tested up to: 4.7
|
||||||
|
License: GPL-2.0+
|
||||||
|
|
||||||
|
Adds a splash page to your site that lets visitors know your site is down for maintenance. It's perfect for a coming soon page.
|
||||||
|
|
||||||
|
== Description ==
|
||||||
|
|
||||||
|
Add a maintenance page to your blog that lets visitors know your blog is down for maintenance, or add a coming soon page for a new website. User with admin rights gets full access to the blog including the front end.
|
||||||
|
|
||||||
|
Activate the plugin and your blog is in maintenance-mode, works and only registered users with enough rights can see the front end. You can use a date with a countdown timer for visitor information or set a value and unit for information.
|
||||||
|
Also works with WordPress Multisite installs (each blog from the network has it's own maintenance settings).
|
||||||
|
|
||||||
|
= Features =
|
||||||
|
|
||||||
|
* Fully customizable (change colors, texts and backgrounds);
|
||||||
|
* Subscription form (export emails to .csv file);
|
||||||
|
* Countdown timer (remaining time);
|
||||||
|
* Contact form (receive emails from visitors);
|
||||||
|
* Coming soon page;
|
||||||
|
* Landing page templates;
|
||||||
|
* WordPress multisite;
|
||||||
|
* Responsive design;
|
||||||
|
* Social media icons;
|
||||||
|
* Works with any WordPress theme;
|
||||||
|
* SEO options;
|
||||||
|
* Exclude URLs from maintenance.
|
||||||
|
|
||||||
|
= Bugs, technical hints or contribute =
|
||||||
|
|
||||||
|
Please give us feedback, contribute and file technical bugs on [GitHub Repo](https://github.com/Designmodocom/WP-Maintenance-Mode).
|
||||||
|
|
||||||
|
= Credits =
|
||||||
|
|
||||||
|
Developed by [Designmodo](http://designmodo.com) & [StrictThemes – WordPress Themes](http://strictthemes.com/)
|
||||||
|
|
||||||
|
== Installation ==
|
||||||
|
|
||||||
|
1. Unpack the download package
|
||||||
|
2. Upload all files to the `/wp-content/plugins/` directory, include folders
|
||||||
|
3. Activate the plugin through the 'Plugins' menu in WordPress
|
||||||
|
4. Go to `Settings` page, where you can change what settings you need (pay attention to **Exclude** option!)
|
||||||
|
|
||||||
|
== Screenshots ==
|
||||||
|
|
||||||
|
1. Maintenance Mode example
|
||||||
|
2. Maintenance Mode example #2
|
||||||
|
3. Contact form
|
||||||
|
4. Dashboard General settings
|
||||||
|
5. Dashboard Design settings
|
||||||
|
6. Dashboard Modules settings
|
||||||
|
|
||||||
|
== Frequently Asked Questions ==
|
||||||
|
|
||||||
|
= How to use plugin filters =
|
||||||
|
See [GitHub Repo] (https://github.com/Designmodocom/WP-Maintenance-Mode) FAQ.
|
||||||
|
|
||||||
|
= Cache Plugin Support =
|
||||||
|
WP Maintenance Mode can be unstable due the cache plugins, we recommend to deactivate any cache plugin when maintenance mode is active.
|
||||||
|
|
||||||
|
= Exclude list =
|
||||||
|
If you change your login url, please add the new slug (url: http://domain.com/newlogin, then you should add: newlogin) to Exclude list from plugin settings -> General Tab.
|
||||||
|
|
||||||
|
== Changelog ==
|
||||||
|
|
||||||
|
= 2.0.9 (29/11/2016) =
|
||||||
|
* new hook (`wpmm_after_body`) in maintenance mode template (thanks @ [KarolÃna VyskoÄilová](https://github.com/vyskoczilova))
|
||||||
|
* pt_PT (portuguese) language update (thanks @ [Pedro Mendonça](https://github.com/pedro-mendonca))
|
||||||
|
* maintenance mode template can also be loaded from theme/child-theme folder (thanks @ [Florian Tiar](https://github.com/Mahjouba91) and [Lachlan Heywood](https://github.com/lachieh))
|
||||||
|
* new hooks for contact form (if you want to add new fields): `wpmm_contact_form_start`, `wpmm_contact_form_before_message`, `wpmm_contact_form_after_message`, `wpmm_contact_form_end`
|
||||||
|
* new hook for contact form validation (if you want to validate new fields): `wpmm_contact_validation`
|
||||||
|
* new hooks for contact form template (if you want to display new fields): `wpmm_contact_template_start`, `wpmm_contact_template_before_message`, `wpmm_contact_template_after_message`, `wpmm_contact_template_end`
|
||||||
|
* some javascript improvements
|
||||||
|
* small css fix for contact form (thanks @ [frontenddev](https://wordpress.org/support/topic/please-fix-modal-window-of-contact-form/))
|
||||||
|
|
||||||
|
= 2.0.8 (09/09/2016) =
|
||||||
|
* add wp_scripts() function (in helpers.php) to maintain backward compatibility (for those with WP < 4.2.0)
|
||||||
|
* css fix for subscribe button on maintenance page
|
||||||
|
* fix multisite administrator access issue
|
||||||
|
* pt_PT (portuguese) language update (thanks @ Pedro Mendonça)
|
||||||
|
* new hooks for Contact module: `wpmm_contact_template`, `wpmm_contact_subject`, `wpmm_contact_headers`
|
||||||
|
* jQuery (google cdn) path fix when SCRIPT_DEBUG is true
|
||||||
|
|
||||||
|
= 2.0.7 (06/07/2016) =
|
||||||
|
* reset_settings _wpnonce check (thanks # Wordfence)
|
||||||
|
* modules > google analytics code sanitization (thanks @ Wordfence)
|
||||||
|
* move sidebar banners from our servers to plugin folder... as WordPress staff requested
|
||||||
|
* Subscribe button error on Mobile version (thanks @ HostÃlio Thumbo)
|
||||||
|
* replace $wp_scripts global with wp_scripts() function
|
||||||
|
* de_DE language file update (thanks @ tt22tt)
|
||||||
|
|
||||||
|
= 2.0.6 (20/06/2016) =
|
||||||
|
* notifications update
|
||||||
|
* languages update
|
||||||
|
|
||||||
|
= 2.0.5 (17/06/2016) =
|
||||||
|
* roles (array) fix
|
||||||
|
|
||||||
|
= 2.0.4 (17/06/2016) =
|
||||||
|
* fixed issue: responsive subscribe form
|
||||||
|
* fixed issue: jQuery was loaded from a different folder on some WP installations
|
||||||
|
* fixed issue: errors after update (strstr on empty strings because of saving empty lines on exclude list)
|
||||||
|
* fixed issue: if "Redirection" from "General" tab is active, also redirects ajax calls
|
||||||
|
* fixed issue: settings page title was wrong placed
|
||||||
|
* "contact" feature update - nice email template + reply-to email header
|
||||||
|
* refactoring for some methods
|
||||||
|
* all assets are now minified
|
||||||
|
* rewrite count db records function (used on subscribers count)
|
||||||
|
* compatible with https://github.com/afragen/github-updater
|
||||||
|
* compatible with wp-cli http://wp-cli.org/
|
||||||
|
* improved responsivity
|
||||||
|
* improved roles access; now you can set multiple roles (editor, author, subscriber, contributor) and administrator will always have access to backend and frontend
|
||||||
|
* it_IT translation by benedettogit (https://github.com/benedettogit)
|
||||||
|
* updated all language files (need help for 100% translation)
|
||||||
|
|
||||||
|
|
||||||
|
= 2.0.3 (07/10/2014) =
|
||||||
|
* WP_Super_Cache issue was fixed
|
||||||
|
* fixed "Subscribe" button issue on Safari mobile
|
||||||
|
* fixed color of subscribe-success message (same color as subscribe_text)
|
||||||
|
* "Social networks" module edits: settings for links target + a new social network: linkedin
|
||||||
|
* new module "Google Analytics"
|
||||||
|
* loginform shortcode reintroduced
|
||||||
|
* dashboard link on maintenance page reintroduced
|
||||||
|
* the content editor accepts new css inline properties: min-height, max-height, min-width, max-width. Use them wisely! :)
|
||||||
|
* Settings & sidebar view + old translation files edited
|
||||||
|
* Update from old version 1.x to 2.x issue was fixed
|
||||||
|
* Translate on activation issue was fixed
|
||||||
|
* de_DE translation by Frank Bültge (http://bueltge.github.io/)
|
||||||
|
* pt_PT translation (100% translated) by Pedro Mendonça (http://www.pedromendonca.pt)
|
||||||
|
* ru_RU translation (100% translated) by affectiosus (https://github.com/affectiosus)
|
||||||
|
* nl_NL translation by dhunink (https://github.com/dhunink)
|
||||||
|
* es_ES translation (100% translated) by Erick Ruiz de Chavez (http://erickruizdechavez.com/)
|
||||||
|
* fr_FR translation by Florian TIAR (https://github.com/Mahjouba91)
|
||||||
|
* pt_BR translation by Jonatas Araújo (http://www.designworld.com.br/)
|
||||||
|
* sv_SE translation by Andréas Lundgren (http://adevade.com/)
|
||||||
|
|
||||||
|
= 2.0.2 (04/09/2014) =
|
||||||
|
* Removed "Author Link" option from General
|
||||||
|
* Countdown - save details fix
|
||||||
|
|
||||||
|
= 2.0.1 (02/09/2014) =
|
||||||
|
* Reintroduced some deprecated actions from old version (but still available in next 4 releases, after that will be removed) and replaced with new ones:
|
||||||
|
- `wm_head` -> `wpmm_head`
|
||||||
|
- `wm_footer` -> `wpmm_footer`
|
||||||
|
* Multisite settings link fix
|
||||||
|
* WP_Maintenance_Mode: init (array checking for custom_css arrays, move delete cache part into a helper, etc.), add_subscriber, send_contact, redirect fixes & optimizations
|
||||||
|
* WP_Maintenance_Mode_Admin: save_plugin_settings fixes, delete_cache (new method)
|
||||||
|
* Settings & Maintenance views fixes
|
||||||
|
* Readme.txt changes
|
||||||
|
|
||||||
|
= 2.0.0 (01/09/2014) =
|
||||||
|
* Changed design and functionality, new features
|
||||||
|
* Changed multisite behaviour: now you can activate maintenance individually (each blog from the network has it's own maintenance settings)
|
||||||
|
* Removed actions: `wm_header`, `wm_footer`, `wm_content`
|
||||||
|
* Removed filters: `wm_header`
|
||||||
|
* Removed [loginform] shortcode
|
||||||
|
* Some filters are deprecated (but still available in next 4 releases, after that will be removed) and replaced with new ones:
|
||||||
|
- `wm_heading` -> `wpmm_heading`,
|
||||||
|
- `wp_maintenance_mode_status_code` -> `wpmm_status_code`
|
||||||
|
- `wm_title` -> `wpmm_meta_title`
|
||||||
|
- `wm_meta_author` -> `wpmm_meta_author`
|
||||||
|
- `wm_meta_description` -> `wpmm_meta_description`
|
||||||
|
- `wm_meta_keywords` -> `wpmm_meta_keywords`
|
||||||
|
* Added new filters:
|
||||||
|
- `wpmm_backtime` - can be used to change the backtime from page header
|
||||||
|
- `wpmm_meta_robots` - can be used to change `Robots Meta Tag` option (from General)
|
||||||
|
- `wpmm_text` - can be used to change `Text` option (from Design > Content)
|
||||||
|
- `wpmm_scripts` - can be used to embed new javascripts files
|
||||||
|
- `wpmm_styles` - can be used to embed new css files
|
||||||
|
- `wpmm_search_bots` - if you have `Bypass for Search Bots` option (from General) activated, it can be used to add new bots (useragents)
|
||||||
|
* Removed themes and now we have a "Design" & "Modules" tabs, where the look and functionality of the maintenance page can be changed as you need
|
||||||
|
|
||||||
|
= 07/07/2014 =
|
||||||
|
* Switch to new owner, contributor
|
||||||
|
|
||||||
|
= 1.8.11 (07/25/2013) =
|
||||||
|
* Fixes for php notices in scrict mode
|
||||||
|
* Alternative for check url, if curl is not installed
|
||||||
|
|
||||||
|
= 1.8.10 (07/18/2013) =
|
||||||
|
* Add check for urls, Performance topics
|
||||||
|
* Change default setting of 'Support Link' to false
|
||||||
|
* Fix network settings php notices
|
||||||
|
|
||||||
|
= 1.8.9 (06/20/2013) =
|
||||||
|
* Allow empty header, title, heading string
|
||||||
|
* Small code changes
|
||||||
|
* Add Support function
|
||||||
|
* Remove preview, will include later in a new release with extra settings page
|
||||||
|
|
||||||
|
= 1.8.8 (06/05/2013) =
|
||||||
|
* Fix path to localized flash content
|
||||||
|
* Fix preview function
|
||||||
|
* Add ukrainian translation
|
||||||
|
* Add czech translation
|
||||||
|
* Fix exclude function for IP
|
||||||
|
* Security fix for save status via Ajax
|
||||||
|
|
||||||
|
= 1.8.7 (04/07/2013) =
|
||||||
|
* Add RTL support for splash page
|
||||||
|
* Add Filter Hook `wp_maintenance_mode_status_code` Status Code; default is 503
|
||||||
|
* Add support for custom splash page; leave a file with this name `wp-maintenance-mode.php` in the wp-content; the plugin use this file
|
||||||
|
The plugin checks in `WP_CONTENT_DIR . '/wp-maintenance-mode.php'`
|
||||||
|
* Small minor changes
|
||||||
|
* Add filter for more date on splash page
|
||||||
|
|
||||||
|
= 1.8.6 (02/22/2013) =
|
||||||
|
* Remove log inside console for JS
|
||||||
|
* Add support for time inside the countdown
|
||||||
|
* Add filter hook `wm_meta_author`for the meta data author
|
||||||
|
* Add filter hook `wm_meta_description` for custom description
|
||||||
|
* Add filter hook `wm_meta_keywords`for custom meta keys
|
||||||
|
|
||||||
|
= 1.8.5 (01/24/2013) =
|
||||||
|
* Added new settings for hide, view notices about the active maintenance mode
|
||||||
|
* Changes on source, codex
|
||||||
|
* Fix PHP Notices [Support Thread](http://wordpress.org/support/topic/error-message-in-settings-1)
|
||||||
|
* Change default settings, added ajax
|
||||||
|
* Fix Preview function
|
||||||
|
* Fix uninstall in WPMU
|
||||||
|
* Small updates on styles for login form
|
||||||
|
|
||||||
|
= 1.8.4 (12/06/2012) =
|
||||||
|
* Fix for include JS in frontend to use countdown
|
||||||
|
* Small mini fix for a php notice
|
||||||
|
* Add charset on spalsh page for strange databases
|
||||||
|
* Enhanced default exclude adresses
|
||||||
|
* Add shortcode `[loginform]` for easy use a login form in splash page
|
||||||
|
* Test with WordPress 3.5
|
||||||
|
|
||||||
|
= 1.8.3 =
|
||||||
|
* Fix for the forgotten update of JS-files; slow SVN :(
|
||||||
|
* Minor Fixes
|
||||||
|
|
||||||
|
= 1.8.2 =
|
||||||
|
* Add different access for Frontend and Backend
|
||||||
|
* Add Rewrite after Login for Frontend Access
|
||||||
|
* Different small changes
|
||||||
|
* Test for WP 3.5
|
||||||
|
|
||||||
|
= 1.8.1 =
|
||||||
|
* Add option for value of robots meta tag
|
||||||
|
* Add option for optional admin login
|
||||||
|
|
||||||
|
= 1.8.0 =
|
||||||
|
* Include all scripts in backend via function
|
||||||
|
* Update datepicker and countdown js
|
||||||
|
* Supportet IP as exclude for see the frontend
|
||||||
|
* Add support for flish cache od WP Super Cache and W3 Total Cache plugins
|
||||||
|
* Fix for changes in WP 3.3 Multisite
|
||||||
|
|
||||||
|
= 1.7.1 (12/05/2011) =
|
||||||
|
* fix for WP smaller 3.2* on Network
|
||||||
|
|
||||||
|
= 1.7.0 (12/02/2011) =
|
||||||
|
* add functionalities to use in WP Multisite
|
||||||
|
* remove message in header, current is not fixed the ticked in core and the message on Admin Bar an Notice is enough
|
||||||
|
* check on WP 3.3RC1
|
||||||
|
|
||||||
|
= 1.6.10 (08/30/2011) =
|
||||||
|
* add hint in Admin Bar, if active
|
||||||
|
* small changes for WP Codex
|
||||||
|
|
||||||
|
= 1.6.9 (06/13/2011) =
|
||||||
|
* Small fix for empty string on custom design
|
||||||
|
|
||||||
|
= 1.6.8 (04/05/2011) =
|
||||||
|
* Small changes on check for datepicker
|
||||||
|
* Fix for Design monster
|
||||||
|
|
||||||
|
= 1.6.7 (01/05/2011) =
|
||||||
|
* Bugfix: new check for files for different themes; hope this fix the server errors
|
||||||
|
* Bugfix: fix add default settings
|
||||||
|
* Maintenance: different changes on the syntax
|
||||||
|
* Feature: add check for Super Admin on WP Multisite; has allways the rights for access
|
||||||
|
* Feature: now it is possible to exclude feed from maintenance mode
|
||||||
|
* Maintenance: check with 3.0.4 and 3.1-RC2
|
||||||
|
* Maintenance: update language file: .pot, de_DE
|
||||||
|
* Bugfix: JavaScript error on Bulk Actions on plugins fixed
|
||||||
|
* Maintenance: fix all notice, if set no values
|
||||||
|
|
||||||
|
= 1.6.6. (10/09/2010) =
|
||||||
|
* Maintenance: many changes on the code; $locale and hook in side frontend
|
||||||
|
* Maintenance: change attribute_escaped to esc_attr with custom method for WP smaller 2.8
|
||||||
|
* Maintenance: Update german language files
|
||||||
|
* Feature: Shortcodes is now possible in the "Text" option
|
||||||
|
* Feature: no cache header rewrite
|
||||||
|
|
||||||
|
= 1.6.5 (09/16/2010) =
|
||||||
|
* add new design "Chemistry" by [elmastudio.de](http://www.elmastudio.de/ "elmastudio.de")
|
||||||
|
* changes for include methods od class for preview
|
||||||
|
* changes the possibility for include of language specific flash files
|
||||||
|
|
||||||
|
= 1.6.4 (09/13/2010) =
|
||||||
|
* add preview functions
|
||||||
|
* bugfix for list in wp-admin/plugins.php
|
||||||
|
* remove datepicker.regional - dont work fine
|
||||||
|
* different small changes
|
||||||
|
* new language file .pot
|
||||||
|
* add flash file and change on plugin for style "Animate" for spanish language
|
||||||
|
|
||||||
|
= 1.6.3 (07/27/2010) =
|
||||||
|
* bugfix to include stylesheet on maintenance mode message
|
||||||
|
|
||||||
|
= 1.6.2 (07/08/2010) =
|
||||||
|
* add functions for hint in the new UI of WP 3.0
|
||||||
|
* add more WP Codex standard source
|
||||||
|
* fix strings in the language and languages files
|
||||||
|
* add datetimepicker-de
|
||||||
|
|
||||||
|
= 1.6.1 (06/18/2010) =
|
||||||
|
* fix a problem with https://; see [Ticket #13941](http://core.trac.wordpress.org/ticket/13941)
|
||||||
|
|
||||||
|
= 1.6 (05/17/2010) =
|
||||||
|
* bugfix for exclude sites
|
||||||
|
|
||||||
|
= 1.5.9 (05/07/2010) =
|
||||||
|
* change different points
|
||||||
|
* add possibility to wotk with MySQLDumper
|
||||||
|
|
||||||
|
= 1.5.8 (21/03/2010)=
|
||||||
|
* fix exclude error
|
||||||
|
* add textareas for heading and header fields
|
||||||
|
|
||||||
|
= 1.5.7 (03/18/2010) =
|
||||||
|
* block admin-area via role
|
||||||
|
* add message for registered users with not enough rights
|
||||||
|
* add message on login-page
|
||||||
|
* different changes
|
||||||
|
|
||||||
|
= 1.5.6 (02/25/2010) =
|
||||||
|
* changes on css, site.php and different syntax on the plugin
|
||||||
|
|
||||||
|
= 1.5.5 (02/23/2010) =
|
||||||
|
* SORRY, small bug for the url to jQuery
|
||||||
|
|
||||||
|
= 1.5.4 (02/23/2010) =
|
||||||
|
* add time for countdown
|
||||||
|
* changes for WP 3.0
|
||||||
|
* changees on rights to see frontend
|
||||||
|
|
||||||
|
= 1.5.3 (01/05/2010) =
|
||||||
|
* Fix for JavaScript with WordPress 2.9
|
||||||
|
* Add new custom fields for fronted: title, header, heading
|
||||||
|
* Fix for setting userrole to see frontend
|
||||||
|
* Change laguage files
|
||||||
|
|
||||||
|
= 1.5.2 (01/04/2010) =
|
||||||
|
* add user-role setting
|
||||||
|
* correctly the de_DE language file
|
||||||
|
|
||||||
|
= 1.5.1 (10/04/2009) =
|
||||||
|
* add small fix
|
||||||
|
* add language files (en_ES, ro_RO)
|
||||||
|
|
||||||
|
= 1.5.0 (09/28/2009) =
|
||||||
|
* add countdown
|
||||||
|
* change options
|
||||||
|
* change default options
|
||||||
|
* add field for own adress to excerpt of the maintenance mode
|
||||||
|
* etc.
|
||||||
|
|
||||||
|
= 1.4.9 (07/09/2009) =
|
||||||
|
* also ready for WordPress 2.6
|
||||||
|
* add romanian language files
|
||||||
|
* add italian language file by [Gianni Diurno](http://gidibao.net/ "Gianni Diurno")
|
||||||
|
|
||||||
|
= 1.4.8 (03/09/2009) =
|
||||||
|
* add design "Damask" by [Fabian Letscher](http://fabianletscher.de/ "Fabian Letscher")
|
||||||
|
* add design "Lego" by [Alex Frison](http://www.afrison.com/ "Alex Frison")
|
||||||
|
|
||||||
|
= 1.4.7 (26/08/2009) =
|
||||||
|
* change doc-type to utf-8 without BOM
|
||||||
|
|
||||||
|
= v1.4.6 (24/08/2009) =
|
||||||
|
* add design "Animate (Flash)" by [Sebastian Schmiedel](http://www.cayou-media.de/ "Sebastian Schmiedel")
|
||||||
|
* add new hook for add content `wm_content` to include flash on content
|
||||||
|
* add frensh language files
|
||||||
|
|
||||||
|
= v1.4.5 (19/08/2009) =
|
||||||
|
* fix html string in text on frontend
|
||||||
|
* add design "Paint" by [Marvin Labod](http://bugeyes.de/ "Marvin Labod")
|
||||||
|
* add turkey language files
|
||||||
|
|
||||||
|
= v1.4.4 (18/08/2009) =
|
||||||
|
* add design "Chastely" by [Florian Andreas Vogelmaier](http://fv-web.de/ "Florian Andreas Vogelmaier")
|
||||||
|
* add design "Only Typo" by [Robert Pfotenhauer](http://krautsuppe.de/ "Robert Pfotenhauer")
|
||||||
|
|
||||||
|
= v1.4.3 (13/08/2009) =
|
||||||
|
* add option for the Text
|
||||||
|
* add option for active maintenance mode
|
||||||
|
* add design "The FF Error" by [Thomas Meschke](http://www.lokalnetz.com/ "Thomas Meschke")
|
||||||
|
* add design "Monster" by [Sebastian Sebald](http://www.backseatsurfer.de "Sebastian Sebald")
|
||||||
|
|
||||||
|
= v1.4.2 (10/08/2009) =
|
||||||
|
* add design "The Sun" by [Nicki Steiger](http://mynicki.net/ "Nicki Steiger")
|
||||||
|
* now it is possible to add own css and add in settings the url to the css-file
|
||||||
|
|
||||||
|
= v1.4.1 (07/08/2009) =
|
||||||
|
* small html-fix
|
||||||
|
|
||||||
|
= v1.4 (06/08/2009) =
|
||||||
|
* complety new code
|
||||||
|
* options menu
|
||||||
|
* new designs by [David Hellmann](http://www.davidhellmann.com/ "David Hellmann")
|
||||||
@@ -1 +1 @@
|
|||||||
<html><head><title>Test</title><link rel="stylesheet" type="text/css" href="/wp-content/themes/theme_name/style.css" /></head><body></body></html>
|
<html><head><title>Test</title><link rel="stylesheet" type="text/css" href="/wp-content/themes/theme_name/style.css" /></head><body></body></html>
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<html><head><title>Test</title><link rel="stylesheet" type="text/css" href="wp-content/themes/theme_name/style.css" /></head><body></body></html>
|
||||||
0
spec/samples/common/models/wp_theme/versionable/bueno-1.5.1.css
Executable file → Normal file
0
spec/samples/common/models/wp_theme/versionable/bueno-1.5.1.css
Executable file → Normal file
0
spec/samples/common/models/wp_theme/versionable/twentyeleven-1.3.css
Executable file → Normal file
0
spec/samples/common/models/wp_theme/versionable/twentyeleven-1.3.css
Executable file → Normal file
0
spec/samples/common/models/wp_theme/versionable/twentyeleven-unknow.css
Executable file → Normal file
0
spec/samples/common/models/wp_theme/versionable/twentyeleven-unknow.css
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/meta_generator/3.3.2.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/meta_generator/3.3.2.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/meta_generator/3.4-beta4.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/meta_generator/3.4-beta4.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/meta_generator/invalid_version.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/meta_generator/invalid_version.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/meta_generator/no_generator.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/meta_generator/no_generator.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/readme/3.3.2.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/readme/3.3.2.html
Executable file → Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>WordPress › ReadMe</title>
|
||||||
|
<link rel="stylesheet" href="wp-admin/css/install.css?ver=20100228" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 id="logo">
|
||||||
|
<a href="https://wordpress.org/"><img alt="WordPress" src="wp-admin/images/wordpress-logo.png" /></a>
|
||||||
|
<br /> Version 4.7
|
||||||
|
</h1>
|
||||||
|
<p style="text-align: center">Semantic Personal Publishing Platform</p>
|
||||||
|
|
||||||
|
<h2>First Things First</h2>
|
||||||
|
<p>Welcome. WordPress is a very special project to me. Every developer and contributor adds something unique to the mix, and together we create something beautiful that I’m proud to be a part of. Thousands of hours have gone into WordPress, and we’re dedicated to making it better every day. Thank you for making it part of your world.</p>
|
||||||
|
<p style="text-align: right">— Matt Mullenweg</p>
|
||||||
|
|
||||||
|
<h2>Installation: Famous 5-minute install</h2>
|
||||||
|
<ol>
|
||||||
|
<li>Unzip the package in an empty directory and upload everything.</li>
|
||||||
|
<li>Open <span class="file"><a href="wp-admin/install.php">wp-admin/install.php</a></span> in your browser. It will take you through the process to set up a <code>wp-config.php</code> file with your database connection details.
|
||||||
|
<ol>
|
||||||
|
<li>If for some reason this doesn’t work, don’t worry. It doesn’t work on all web hosts. Open up <code>wp-config-sample.php</code> with a text editor like WordPad or similar and fill in your database connection details.</li>
|
||||||
|
<li>Save the file as <code>wp-config.php</code> and upload it.</li>
|
||||||
|
<li>Open <span class="file"><a href="wp-admin/install.php">wp-admin/install.php</a></span> in your browser.</li>
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
<li>Once the configuration file is set up, the installer will set up the tables needed for your blog. If there is an error, double check your <code>wp-config.php</code> file, and try again. If it fails again, please go to the <a href="https://wordpress.org/support/" title="WordPress support">support forums</a> with as much data as you can gather.</li>
|
||||||
|
<li><strong>If you did not enter a password, note the password given to you.</strong> If you did not provide a username, it will be <code>admin</code>.</li>
|
||||||
|
<li>The installer should then send you to the <a href="wp-login.php">login page</a>. Sign in with the username and password you chose during the installation. If a password was generated for you, you can then click on “Profile” to change the password.</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Updating</h2>
|
||||||
|
<h3>Using the Automatic Updater</h3>
|
||||||
|
<p>If you are updating from version 2.7 or higher, you can use the automatic updater:</p>
|
||||||
|
<ol>
|
||||||
|
<li>Open <span class="file"><a href="wp-admin/update-core.php">wp-admin/update-core.php</a></span> in your browser and follow the instructions.</li>
|
||||||
|
<li>You wanted more, perhaps? That’s it!</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h3>Updating Manually</h3>
|
||||||
|
<ol>
|
||||||
|
<li>Before you update anything, make sure you have backup copies of any files you may have modified such as <code>index.php</code>.</li>
|
||||||
|
<li>Delete your old WordPress files, saving ones you’ve modified.</li>
|
||||||
|
<li>Upload the new files.</li>
|
||||||
|
<li>Point your browser to <span class="file"><a href="wp-admin/upgrade.php">/wp-admin/upgrade.php</a>.</span></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Migrating from other systems</h2>
|
||||||
|
<p>WordPress can <a href="https://codex.wordpress.org/Importing_Content">import from a number of systems</a>. First you need to get WordPress installed and working as described above, before using <a href="wp-admin/import.php" title="Import to WordPress">our import tools</a>.</p>
|
||||||
|
|
||||||
|
<h2>System Requirements</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://secure.php.net/">PHP</a> version <strong>5.2.4</strong> or higher.</li>
|
||||||
|
<li><a href="https://www.mysql.com/">MySQL</a> version <strong>5.0</strong> or higher.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Recommendations</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="https://secure.php.net/">PHP</a> version <strong>7</strong> or higher.</li>
|
||||||
|
<li><a href="https://www.mysql.com/">MySQL</a> version <strong>5.6</strong> or higher.</li>
|
||||||
|
<li>The <a href="https://httpd.apache.org/docs/2.2/mod/mod_rewrite.html">mod_rewrite</a> Apache module.</li>
|
||||||
|
<li><a href="https://wordpress.org/news/2016/12/moving-toward-ssl/">HTTPS</a> support.</li>
|
||||||
|
<li>A link to <a href="https://wordpress.org/">wordpress.org</a> on your site.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Online Resources</h2>
|
||||||
|
<p>If you have any questions that aren’t addressed in this document, please take advantage of WordPress’ numerous online resources:</p>
|
||||||
|
<dl>
|
||||||
|
<dt><a href="https://codex.wordpress.org/">The WordPress Codex</a></dt>
|
||||||
|
<dd>The Codex is the encyclopedia of all things WordPress. It is the most comprehensive source of information for WordPress available.</dd>
|
||||||
|
<dt><a href="https://wordpress.org/news/">The WordPress Blog</a></dt>
|
||||||
|
<dd>This is where you’ll find the latest updates and news related to WordPress. Recent WordPress news appears in your administrative dashboard by default.</dd>
|
||||||
|
<dt><a href="https://planet.wordpress.org/">WordPress Planet</a></dt>
|
||||||
|
<dd>The WordPress Planet is a news aggregator that brings together posts from WordPress blogs around the web.</dd>
|
||||||
|
<dt><a href="https://wordpress.org/support/">WordPress Support Forums</a></dt>
|
||||||
|
<dd>If you’ve looked everywhere and still can’t find an answer, the support forums are very active and have a large community ready to help. To help them help you be sure to use a descriptive thread title and describe your question in as much detail as possible.</dd>
|
||||||
|
<dt><a href="https://codex.wordpress.org/IRC">WordPress <abbr title="Internet Relay Chat">IRC</abbr> Channel</a></dt>
|
||||||
|
<dd>There is an online chat channel that is used for discussion among people who use WordPress and occasionally support topics. The above wiki page should point you in the right direction. (<a href="irc://irc.freenode.net/wordpress">irc.freenode.net #wordpress</a>)</dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<h2>Final Notes</h2>
|
||||||
|
<ul>
|
||||||
|
<li>If you have any suggestions, ideas, or comments, or if you (gasp!) found a bug, join us in the <a href="https://wordpress.org/support/">Support Forums</a>.</li>
|
||||||
|
<li>WordPress has a robust plugin <abbr title="application programming interface">API</abbr> that makes extending the code easy. If you are a developer interested in utilizing this, see the <a href="https://developer.wordpress.org/plugins/">Plugin Developer Handbook</a>. You shouldn’t modify any of the core code.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Share the Love</h2>
|
||||||
|
<p>WordPress has no multi-million dollar marketing campaign or celebrity sponsors, but we do have something even better—you. If you enjoy WordPress please consider telling a friend, setting it up for someone less knowledgable than yourself, or writing the author of a media article that overlooks us.</p>
|
||||||
|
|
||||||
|
<p>WordPress is the official continuation of <a href="http://cafelog.com/">b2/cafélog</a>, which came from Michel V. The work has been continued by the <a href="https://wordpress.org/about/">WordPress developers</a>. If you would like to support WordPress, please consider <a href="https://wordpress.org/donate/" title="Donate to WordPress">donating</a>.</p>
|
||||||
|
|
||||||
|
<h2>License</h2>
|
||||||
|
<p>WordPress is free software, and is released under the terms of the <abbr title="GNU General Public License">GPL</abbr> version 2 or (at your option) any later version. See <a href="license.txt">license.txt</a>.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
0
spec/samples/common/models/wp_version/findable/readme/empty_version.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/readme/empty_version.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/readme/invalid_version.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/readme/invalid_version.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/rss_generator/3.3.2.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/rss_generator/3.3.2.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/rss_generator/3.4-beta4.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/rss_generator/3.4-beta4.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/rss_generator/invalid_version.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/rss_generator/invalid_version.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/rss_generator/no_generator.html
Executable file → Normal file
0
spec/samples/common/models/wp_version/findable/rss_generator/no_generator.html
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user