Compare commits

..

107 Commits

Author SHA1 Message Date
erwanlr
adff971d62 Bumps version 2019-03-10 09:47:41 +00:00
erwanlr
23b22f71b8 Reduces confidence of wp-cron detection 2019-03-10 08:02:51 +00:00
erwanlr
fee3671e32 Adds wp-cron.php detection - Fixes #1299 2019-03-10 07:53:12 +00:00
erwanlr
26c6be7268 Fixes #1307 2019-03-10 07:11:48 +00:00
erwanlr
01c5bcf2be Adds DFs 2019-03-09 16:19:25 +00:00
erwanlr
1ab8a5ab98 Updates deps 2019-03-07 19:37:01 +00:00
erwanlr
b54aaca28a Adds missing lines 2019-03-04 07:40:45 +00:00
erwanlr
86a29ae000 Adds DF 2019-03-04 07:35:21 +00:00
erwanlr
a5dbee93ff Adds DFs 2019-03-02 10:43:45 +00:00
Christian Mehlmauer
e0465e6e10 remove line 2019-02-28 08:41:19 +01:00
Christian Mehlmauer
7da48b9dd1 readme linting 2019-02-28 08:18:01 +01:00
Christian Mehlmauer
a64895c3a6 remove UTF characters from license 2019-02-28 08:13:42 +01:00
erwanlr
21f1a5d4c4 Adds DFs 2019-02-23 08:27:27 +00:00
erwanlr
d60f79ca33 Adds DFs 2019-02-16 13:20:51 +00:00
Erwan
2d5cea5033 Adds missing #to_s calls again 2019-02-11 21:14:40 +01:00
erwanlr
b0615215fe Adds missing #to_s calls 2019-02-11 20:03:05 +00:00
erwanlr
7a0f98b2cb Uses Pathname#join rather than File#join when possible 2019-02-11 19:56:07 +00:00
erwanlr
cdc1dab4a6 Bumps version 2019-02-11 11:48:49 +00:00
erwanlr
431739ab19 Updates Rubocop dep 2019-02-11 10:44:29 +00:00
erwanlr
1780399050 Fixes #1277 2019-02-10 15:32:30 +00:00
erwanlr
eb75d38716 Fixes #1284 2019-02-10 13:47:19 +00:00
erwanlr
06f82d78f4 Ref #1285 - Adds comment about the pagination 2019-02-10 10:49:03 +00:00
erwanlr
dee4da1c0e Fixes #1285 2019-02-10 10:45:54 +00:00
erwanlr
e341ec7c60 Adds DFs 2019-02-10 09:44:17 +00:00
Erwan
9146609e4a Update Readme, Fixes #1286 2019-02-03 20:46:03 +01:00
erwanlr
f90615ca41 Adds DF 2019-02-03 07:08:05 +00:00
erwanlr
8a2a6a05ff Adds DFs 2019-01-27 10:54:13 +00:00
Erwan
5a787f8ed5 Adds a note about bug in Ruby 2.5.x, Ref #1283 2019-01-25 20:14:14 +00:00
erwanlr
a904053002 Adds DFs 2019-01-20 17:04:32 +00:00
Erwan
70ecd30dcc Merge pull request #1276 from wpscanteam/dependabot/bundler/rubocop-tw-0.63.0
Update rubocop requirement from ~> 0.62.0 to ~> 0.63.0
2019-01-17 09:32:24 +00:00
dependabot[bot]
b0976d7e47 Update rubocop requirement from ~> 0.62.0 to ~> 0.63.0
Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version.
- [Release notes](https://github.com/rubocop-hq/rubocop/releases)
- [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop-hq/rubocop/commits/v0.63.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-17 05:54:18 +00:00
erwanlr
bb5e55016c Adds DFs 2019-01-13 16:56:13 +00:00
erwanlr
abdf285c69 Bumps version 2019-01-11 11:53:11 +00:00
erwanlr
fd4da23d4f Creates simplecov exetrnal config 2019-01-11 11:13:49 +00:00
erwanlr
bb8f58c83b Updates deps 2019-01-11 11:12:34 +00:00
erwanlr
077da6ae86 Moves require spec_helper to config file 2019-01-11 11:11:56 +00:00
erwanlr
d5222d7e9a Adds DFs 2019-01-07 14:58:03 +00:00
erwanlr
01702c127b Tries to fix Travis again 2019-01-07 11:47:58 +00:00
Erwan
87902cbfb4 Tries to fix Travis builds 2019-01-07 10:54:05 +00:00
ethicalhack3r
fcaa393ffe Update license 2019-01-07 10:54:24 +01:00
ethicalhack3r
18bac6e792 Update to Ruby 2.6.0 2019-01-07 10:16:32 +01:00
erwanlr
9a21efebe3 Updates DFs 2018-12-28 22:50:05 +00:00
erwanlr
357182ef17 Adds DFs 2018-12-28 22:43:41 +00:00
erwanlr
5fad540a4c Bumps version 2018-12-28 13:35:01 +00:00
erwanlr
c1fc153420 Updates Deps, ref #1266 2018-12-28 11:17:37 +00:00
erwanlr
73a1974f85 Bumps version 2018-12-13 22:16:45 +00:00
erwanlr
dec73c21b6 Fixes #1264 2018-12-13 22:11:37 +00:00
erwanlr
46a00cc864 Adds DFs 2018-12-07 14:59:03 +00:00
erwanlr
62455be165 Deletes useless specs 2018-12-06 22:54:17 +00:00
erwanlr
17ef5ef918 Reverts spec changes 2018-12-06 22:52:10 +00:00
erwanlr
922b6fffd0 Fixes specs 2018-12-06 21:46:13 +00:00
erwanlr
b47bf006d0 Removes useless spec 2018-12-06 21:44:54 +00:00
erwanlr
d60269f4bc Adds DFs 2018-12-06 21:41:00 +00:00
erwanlr
1ce057a78e Adds DFs 2018-12-06 15:54:15 +00:00
erwanlr
a0fe04b990 Fixes #1260 2018-12-06 02:51:23 +00:00
erwanlr
31c9172e19 Removes false positive DFs 2018-12-03 15:37:09 +00:00
erwanlr
7f23cbef71 Adds DFs 2018-12-03 15:08:56 +00:00
Ryan Dewhurst
4884defaed Add some references to interesting findings 2018-11-22 15:04:43 +01:00
erwanlr
3039218c40 Adds DFs 2018-11-18 11:45:58 +00:00
erwanlr
8bbc2f32ae Bumps version 2018-11-12 16:11:14 +00:00
erwanlr
4ca46ab3ba Fixes #1241 2018-11-12 15:57:17 +00:00
erwanlr
7442c72d01 Fixes #1244 2018-11-08 20:28:24 +00:00
erwanlr
01cd8350bc Fixes 1242 2018-11-08 19:16:47 +00:00
erwanlr
8b5ea589db Ref #1241 2018-11-08 19:04:40 +00:00
Erwan
3555ca1d1e Merge pull request #1223 from taha-abbasi/patch-1
Added username enumeration instructions
2018-11-07 11:40:41 +00:00
erwanlr
ae034a47ed Removes FP DFs 2018-11-03 19:36:55 +00:00
erwanlr
ec3862c930 Adds DFs 2018-11-03 19:27:52 +00:00
erwanlr
c63804d1c5 Bumps version 2018-11-02 19:51:57 +00:00
erwanlr
c5e6752f75 Fixes #1232 2018-11-02 19:33:38 +00:00
erwanlr
e4f3e9d11c Fixes spec 2018-11-02 17:52:43 +00:00
erwanlr
f3713536b9 Adds missing spec files 2018-11-02 16:36:10 +00:00
erwanlr
fb751c0a51 Fixes #1228 2018-11-02 13:40:46 +00:00
erwanlr
9d3464055a Updates deps 2018-11-02 08:36:22 +00:00
erwanlr
0fea814f5d Fixes #1237 2018-11-02 08:33:21 +00:00
Ryan Dewhurst
ae70a6df9d Merge pull request #1233 from FenrirSec/fix_wordpress_hosted
Fixed pattern matching on target.wordpress_hosted
2018-10-29 13:46:50 +01:00
lp1
4afc756ccd Added spec for the new regex 2018-10-29 12:11:28 +01:00
lp1
adc5841261 Fixed too restrictive pattern matching on target.wordpress_hosted attribute 2018-10-25 18:09:04 +02:00
erwanlr
41cca5fb8a Bumps version 2018-10-20 13:43:20 +01:00
erwanlr
498da1a06b Merge branch 'advanced_help' 2018-10-20 13:41:17 +01:00
erwanlr
48dab90313 Displays the release date of the detected WP version 2018-10-20 12:37:46 +01:00
erwanlr
d1ff642957 Adds DFs 2018-10-20 11:56:47 +01:00
Erwan
2b5613d84a Update .travis.yml 2018-10-19 21:15:03 +01:00
Ryan Dewhurst
09d28fae26 Update Ruby 2018-10-19 09:47:02 +02:00
Ryan Dewhurst
7517e247d9 Update readme ruby version 2018-10-19 09:45:39 +02:00
erwanlr
998951e629 Re-enables rspec on build 2018-10-18 16:15:36 +01:00
erwanlr
d89fcbb68a Ref #1225 - Adds notes related to Medias enumeration 2018-10-18 16:15:04 +01:00
erwanlr
d3e0ff1e66 Adds simple and full help options 2018-10-18 16:01:28 +01:00
Taha Abbasi
804a8c34c6 Added username enumeration instructions
Added username enumeration instructions, and username enumeration with range instructions for use with Docker and without.
2018-10-08 13:39:11 -04:00
erwanlr
57942e1826 Adds DFs 2018-10-07 15:58:56 +01:00
Christian Mehlmauer
5657735b55 reduce image size 2018-10-04 16:28:21 +02:00
Christian Mehlmauer
791fce2424 smaller image 2018-10-03 07:36:06 +02:00
Christian Mehlmauer
c34fa45875 ruby 2018-10-03 07:17:44 +02:00
Christian Mehlmauer
e0fd79f800 fix docker build 2018-10-02 17:12:39 +02:00
Christian Mehlmauer
f9d9cda4a4 Merge pull request #1218 from mostafahussein/dockerfile-enhancements
Use LABEL and Make use of multi-stage build
2018-10-02 17:10:49 +02:00
Mostafa Hussein
d6f44b2f42 Remove unnecessary commands 2018-10-02 15:55:35 +02:00
Mostafa Hussein
bd90da7ed2 Remove runtime dependencies from build stage 2018-10-02 14:43:38 +02:00
Mostafa Hussein
3a1a976e35 Update Maintainers 2018-10-02 13:29:10 +02:00
Mostafa Hussein
db1309af83 Use LABEL and Make use of multistage build
Maintainer keyword should be replaced with LABEL, and Also using multistage build decreases the image from 139MB to 117MB

Signed-off-by: Mostafa Hussein <mostafa.hussein91@gmail.com>
2018-10-02 08:45:35 +02:00
Christian Mehlmauer
0e47441a36 dockerignore 2018-10-01 22:03:13 +02:00
Ryan Dewhurst
375bea9a8b Update README.md 2018-10-01 20:06:41 +02:00
erwanlr
3a42772879 Adds missing spec files 2018-09-30 13:57:46 +01:00
erwanlr
e9956593dc Adds DFs 2018-09-30 13:16:58 +01:00
erwanlr
fda6000c4c Merge branch 'master' of github.com:wpscanteam/wpscan 2018-09-30 11:23:26 +01:00
erwanlr
99b4eb969d Adds License and Readme files to gem 2018-09-30 11:20:28 +01:00
Christian Mehlmauer
dadd55ba32 another reference 2018-09-30 11:18:29 +02:00
Christian Mehlmauer
b40e06b2ea remove v3 references 2018-09-30 11:17:20 +02:00
Christian Mehlmauer
3f20edc41f gitignore 2018-09-30 11:15:48 +02:00
363 changed files with 73938 additions and 576 deletions

View File

@@ -8,6 +8,7 @@ spec/
.*
**/*.md
*.md
!README.md
Dockerfile
**/*.orig
*.orig

9
.gitignore vendored
View File

@@ -12,3 +12,12 @@ Gemfile.lock
_yardoc
doc/
.wpscan/
.ash_history
.DS_Store
.DS_Store?
.idea/
# Old files from v2
cache/
data/

1
.rspec
View File

@@ -1,2 +1,3 @@
--color
--fail-fast
--require spec_helper

View File

@@ -1 +1 @@
2.5.0
2.6.0

4
.simplecov Normal file
View File

@@ -0,0 +1,4 @@
SimpleCov.start do
add_filter '/spec/'
add_filter 'helper'
end

View File

@@ -10,16 +10,21 @@ rvm:
- 2.3.5
- 2.3.6
- 2.3.7
- 2.3.8
- 2.4.1
- 2.4.2
- 2.4.3
- 2.4.4
- 2.4.5
- 2.5.0
- 2.5.1
- 2.5.2
- 2.5.3
- 2.6.0
- ruby-head
before_install:
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
- "gem update --system"
- gem update --system
matrix:
allow_failures:
- rvm: ruby-head

View File

@@ -1,27 +1,38 @@
FROM ruby:2.5-alpine
MAINTAINER WPScan Team <team@wpscan.org>
FROM ruby:2.5.1-alpine AS builder
LABEL maintainer="WPScan Team <team@wpscan.org>"
ARG BUNDLER_ARGS="--jobs=8 --without test development"
RUN adduser -h /wpscan -g WPScan -D wpscan
RUN echo "gem: --no-ri --no-rdoc" > /etc/gemrc
COPY . /wpscan
RUN chown -R wpscan:wpscan /wpscan
# runtime dependencies
RUN apk add --no-cache libcurl procps sqlite-libs && \
# build dependencies
apk add --no-cache --virtual build-deps git libcurl ruby-dev libffi-dev make gcc musl-dev zlib-dev procps sqlite-dev && \
bundle install --system --gemfile=/wpscan/Gemfile $BUNDLER_ARGS && \
apk del --no-cache build-deps
RUN apk add --no-cache git libcurl ruby-dev libffi-dev make gcc musl-dev zlib-dev procps sqlite-dev && \
bundle install --system --clean --no-cache --gemfile=/wpscan/Gemfile $BUNDLER_ARGS && \
# temp fix for https://github.com/bundler/bundler/issues/6680
rm -rf /usr/local/bundle/cache
WORKDIR /wpscan
RUN rake install --trace
# needed so non superusers can read gems
RUN chmod -R a+r /usr/local/bundle
FROM ruby:2.5-alpine
LABEL maintainer="WPScan Team <team@wpscan.org>"
RUN adduser -h /wpscan -g WPScan -D wpscan
COPY --from=builder /usr/local/bundle /usr/local/bundle
RUN chown -R wpscan:wpscan /wpscan
# runtime dependencies
RUN apk add --no-cache libcurl procps sqlite-libs
USER wpscan
RUN /usr/local/bundle/bin/wpscan --update --verbose
ENTRYPOINT ["/usr/local/bundle/bin/wpscan"]
CMD ["--help"]

10
LICENSE
View File

@@ -1,14 +1,14 @@
WPScan Public Source License
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2018 WPScan Team.
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2019 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.
1. Definitions
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.3 WPScan Team means WPScans core developers, an updated list of whom can be found within the CREDITS file.
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.3 "WPScan Team" means WPScans core developers.
2. Commercialization
@@ -59,7 +59,7 @@ WPScan is provided under an AS-IS basis and without any support, updates or main
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

View File

@@ -1,4 +1,4 @@
![alt text](https://raw.githubusercontent.com/wpscanteam/wpscan/gh-pages/wpscan_logo_407x80.png "WPScan - WordPress Security Scanner")
![alt text](https://raw.githubusercontent.com/wpscanteam/wpscan/gh-pages/images/wpscan_logo.png "WPScan - WordPress Security Scanner")
[![Gem Version](https://badge.fury.io/rb/wpscan.svg)](https://badge.fury.io/rb/wpscan)
[![Build Status](https://travis-ci.org/wpscanteam/wpscan.svg?branch=master)](https://travis-ci.org/wpscanteam/wpscan)
@@ -7,23 +7,28 @@
# INSTALL
## Prerequisites:
## Prerequisites
- Ruby >= 2.2.2 - Recommended: 2.3.3
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
- (Optional but highly recommended: [RVM](https://rvm.io/rvm/install))
- Ruby >= 2.3 - Recommended: latest
- Ruby 2.5.0 to 2.5.3 can cause an 'undefined symbol: rmpd_util_str_to_d' error in some systems, see [#1283](https://github.com/wpscanteam/wpscan/issues/1283)
- Curl >= 7.21 - Recommended: latest
- The 7.29 has a segfault
- RubyGems - Recommended: latest
### From RubyGems:
### From RubyGems (Recommended)
```
```shell
gem install wpscan
```
### From sources:
On MacOSX, if a ```Gem::FilePermissionError``` is raised due to the Apple's System Integrity Protection (SIP), either install RVM and install wpscan again, or run ```sudo gem install -n /usr/local/bin wpscan``` (see [#1286](https://github.com/wpscanteam/wpscan/issues/1286))
### From sources (NOT Recommended)
Prerequisites: Git
```
```shell
git clone https://github.com/wpscanteam/wpscan
cd wpscan/
@@ -31,10 +36,30 @@ cd wpscan/
bundle install && rake install
```
# Updating
You can update the local database by using ```wpscan --update```
Updating WPScan itself is either done via ```gem update wpscan``` or the packages manager (this is quite important for distributions such as in Kali Linux: ```apt-get update && apt-get upgrade```) depending how WPScan was (pre)installed
# Docker
Pull the repo with ```docker pull wpscanteam/wpscan```
Enumerating usernames
```shell
docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u
```
Enumerating a range of usernames
```shell
docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u1-100
```
** replace u1-100 with a range of your choice.
# Usage
```wpscan --url blog.tld``` This will scan the blog using default options with a good compromise between speed and accuracy. For example, the plugins will be checked passively but their version with a mixed detection mode (passively + aggressively). Potential config backup files will also be checked, along with other interesting findings. If a more stealthy approach is required, then ```wpscan --stealthy --url blog.tld``` can be used.
@@ -46,29 +71,45 @@ The DB is located at ~/.wpscan/db
WPScan can load all options (including the --url) from configuration files, the following locations are checked (order: first to last):
* ~/.wpscan/cli_options.json
* ~/.wpscan/cli_options.yml
* pwd/.wpscan/cli_options.json
* pwd/.wpscan/cli_options.yml
- ~/.wpscan/cli_options.json
- ~/.wpscan/cli_options.yml
- pwd/.wpscan/cli_options.json
- pwd/.wpscan/cli_options.yml
If those files exist, options from them will be loaded and overridden if found twice.
e.g:
~/.wpscan/cli_options.yml:
```
```yml
proxy: 'http://127.0.0.1:8080'
verbose: true
```
pwd/.wpscan/cli_options.yml:
```
```yml
proxy: 'socks5://127.0.0.1:9090'
url: 'http://target.tld'
```
Running ```wpscan``` in the current directory (pwd), is the same as ```wpscan -v --proxy socks5://127.0.0.1:9090 --url http://target.tld```
Enumerating usernames
```shell
wpscan --url https://target.tld/ --enumerate u
```
Enumerating a range of usernames
```shell
wpscan --url https://target.tld/ --enumerate u1-100
```
** replace u1-100 with a range of your choice.
# PROJECT HOME
[https://wpscan.org](https://wpscan.org)
@@ -81,7 +122,7 @@ Running ```wpscan``` in the current directory (pwd), is the same as ```wpscan -v
## WPScan Public Source License
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2018 WPScan Team.
The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2019 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.
@@ -91,7 +132,7 @@ Cases that include commercialization of WPScan require a commercial, non-free li
1.2 "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns WPScan.
1.3 "WPScan Team" means WPScans core developers, an updated list of whom can be found within the CREDITS file.
1.3 "WPScan Team" means WPScans core developers.
### 2. Commercialization
@@ -99,30 +140,28 @@ A commercial use is one intended for commercial advantage or monetary compensati
Example cases of commercialization are:
- Using WPScan to provide commercial managed/Software-as-a-Service services.
- Distributing WPScan as a commercial product or as part of one.
- Using WPScan as a value added service/product.
- Using WPScan to provide commercial managed/Software-as-a-Service services.
- Distributing WPScan as a commercial product or as part of one.
- Using WPScan as a value added service/product.
Example cases which do not require a commercial license, and thus fall under the terms set out below, include (but are not limited to):
- Penetration testers (or penetration testing organizations) using WPScan as part of their assessment toolkit.
- Penetration Testing Linux Distributions including but not limited to Kali Linux, SamuraiWTF, BackBox Linux.
- Using WPScan to test your own systems.
- Any non-commercial use of WPScan.
- Penetration testers (or penetration testing organizations) using WPScan as part of their assessment toolkit.
- Penetration Testing Linux Distributions including but not limited to Kali Linux, SamuraiWTF, BackBox Linux.
- Using WPScan to test your own systems.
- Any non-commercial use of WPScan.
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
We may grant commercial licenses at no monetary cost at our own discretion if the commercial usage is deemed by the WPScan Team to significantly benefit WPScan.
Free-use Terms and Conditions;
### 3. Redistribution
Redistribution is permitted under the following conditions:
- Unmodified License is provided with WPScan.
- Unmodified Copyright notices are provided with WPScan.
- Does not conflict with the commercialization clause.
- Unmodified License is provided with WPScan.
- Unmodified Copyright notices are provided with WPScan.
- Does not conflict with the commercialization clause.
### 4. Copying

View File

@@ -5,15 +5,15 @@ module WPScan
# @return [ Array<OptParseValidator::Opt> ]
def cli_options
[OptURL.new(['--url URL', 'The URL of the blog to scan'],
required_unless: %i[update help version], default_protocol: 'http')] +
required_unless: %i[update help hh version], default_protocol: 'http')] +
super.drop(1) + # delete the --url from CMSScanner
[
OptChoice.new(['--server SERVER', 'Force the supplied server module to be loaded'],
choices: %w[apache iis nginx],
normalize: %i[downcase to_sym]),
normalize: %i[downcase to_sym],
advanced: true),
OptBoolean.new(['--force', 'Do not check if the target is running WordPress']),
OptBoolean.new(['--[no-]update', 'Wether or not to update the Database'],
required_unless: %i[url help version])
OptBoolean.new(['--[no-]update', 'Whether or not to update the Database'])
]
end
@@ -71,7 +71,7 @@ module WPScan
exit(WPScan::ExitCode::VULNERABLE)
end
raise NotWordPressError unless target.wordpress? || parsed_options[:force]
raise NotWordPressError unless target.wordpress?(parsed_options[:detection_mode]) || parsed_options[:force]
end
# Loads the related server module in the target

View File

@@ -13,19 +13,22 @@ module WPScan
def cli_enum_choices
[
OptMultiChoices.new(
['--enumerate [OPTS]', '-e', 'Enumeration Process'],
['-e', '--enumerate [OPTS]', 'Enumeration Process'],
choices: {
vp: OptBoolean.new(['--vulnerable-plugins']),
ap: OptBoolean.new(['--all-plugins']),
p: OptBoolean.new(['--plugins']),
vt: OptBoolean.new(['--vulnerable-themes']),
at: OptBoolean.new(['--all-themes']),
t: OptBoolean.new(['--themes']),
tt: OptBoolean.new(['--timthumbs']),
cb: OptBoolean.new(['--config-backups']),
vp: OptBoolean.new(['--vulnerable-plugins']),
ap: OptBoolean.new(['--all-plugins']),
p: OptBoolean.new(['--plugins']),
vt: OptBoolean.new(['--vulnerable-themes']),
at: OptBoolean.new(['--all-themes']),
t: OptBoolean.new(['--themes']),
tt: OptBoolean.new(['--timthumbs']),
cb: OptBoolean.new(['--config-backups']),
dbe: OptBoolean.new(['--db-exports']),
u: OptIntegerRange.new(['--users', 'User IDs range. e.g: u1-5'], value_if_empty: '1-10'),
m: OptIntegerRange.new(['--medias', 'Media IDs range. e.g m1-15'], value_if_empty: '1-100')
u: OptIntegerRange.new(['--users', 'User IDs range. e.g: u1-5'], value_if_empty: '1-10'),
m: OptIntegerRange.new(['--medias',
'Media IDs range. e.g m1-15',
'Note: Permalink setting must be set to "Plain" for those to be detected'],
value_if_empty: '1-100')
},
value_if_empty: 'vp,vt,tt,cb,dbe,u,m',
incompatible: [%i[vp ap p], %i[vt at t]],
@@ -45,7 +48,7 @@ module WPScan
# @return [ Array<OptParseValidator::OptBase> ]
def cli_plugins_opts
[
OptSmartList.new(['--plugins-list LIST', 'List of plugins to enumerate']),
OptSmartList.new(['--plugins-list LIST', 'List of plugins to enumerate'], advanced: true),
OptChoice.new(
['--plugins-detection MODE',
'Use the supplied mode to enumerate Plugins, instead of the global (--detection-mode) mode.'],
@@ -54,7 +57,8 @@ module WPScan
OptBoolean.new(
['--plugins-version-all',
'Check all the plugins version locations according to the choosen mode (--detection-mode, ' \
'--plugins-detection and --plugins-version-detection)']
'--plugins-detection and --plugins-version-detection)'],
advanced: true
),
OptChoice.new(
['--plugins-version-detection MODE',
@@ -68,22 +72,23 @@ module WPScan
# @return [ Array<OptParseValidator::OptBase> ]
def cli_themes_opts
[
OptSmartList.new(['--themes-list LIST', 'List of themes to enumerate']),
OptSmartList.new(['--themes-list LIST', 'List of themes to enumerate'], advanced: true),
OptChoice.new(
['--themes-detection MODE',
'Use the supplied mode to enumerate Themes, instead of the global (--detection-mode) mode.'],
choices: %w[mixed passive aggressive], normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
),
OptBoolean.new(
['--themes-version-all',
'Check all the themes version locations according to the choosen mode (--detection-mode, ' \
'--themes-detection and --themes-version-detection)']
'--themes-detection and --themes-version-detection)'],
advanced: true
),
OptChoice.new(
['--themes-version-detection MODE',
'Use the supplied mode to check themes versions instead of the --detection-mode ' \
'or --themes-detection modes.'],
choices: %w[mixed passive aggressive], normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
)
]
end
@@ -93,12 +98,12 @@ module WPScan
[
OptFilePath.new(
['--timthumbs-list FILE-PATH', 'List of timthumbs\' location to use'],
exists: true, default: File.join(DB_DIR, 'timthumbs-v3.txt')
exists: true, default: DB_DIR.join('timthumbs-v3.txt').to_s, advanced: true
),
OptChoice.new(
['--timthumbs-detection MODE',
'Use the supplied mode to enumerate Timthumbs, instead of the global (--detection-mode) mode.'],
choices: %w[mixed passive aggressive], normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
)
]
end
@@ -108,12 +113,12 @@ module WPScan
[
OptFilePath.new(
['--config-backups-list FILE-PATH', 'List of config backups\' filenames to use'],
exists: true, default: File.join(DB_DIR, 'config_backups.txt')
exists: true, default: DB_DIR.join('config_backups.txt').to_s, advanced: true
),
OptChoice.new(
['--config-backups-detection MODE',
'Use the supplied mode to enumerate Config Backups, instead of the global (--detection-mode) mode.'],
choices: %w[mixed passive aggressive], normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
)
]
end
@@ -123,12 +128,12 @@ module WPScan
[
OptFilePath.new(
['--db-exports-list FILE-PATH', 'List of DB exports\' paths to use'],
exists: true, default: File.join(DB_DIR, 'db_exports.txt')
exists: true, default: DB_DIR.join('db_exports.txt').to_s, advanced: true
),
OptChoice.new(
['--db-exports-detection MODE',
'Use the supplied mode to enumerate DB Exports, instead of the global (--detection-mode) mode.'],
choices: %w[mixed passive aggressive], normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
)
]
end
@@ -139,7 +144,7 @@ module WPScan
OptChoice.new(
['--medias-detection MODE',
'Use the supplied mode to enumerate Medias, instead of the global (--detection-mode) mode.'],
choices: %w[mixed passive aggressive], normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
)
]
end
@@ -149,12 +154,13 @@ module WPScan
[
OptSmartList.new(
['--users-list LIST',
'List of users to check during the users enumeration from the Login Error Messages']
'List of users to check during the users enumeration from the Login Error Messages'],
advanced: true
),
OptChoice.new(
['--users-detection MODE',
'Use the supplied mode to enumerate Users, instead of the global (--detection-mode) mode.'],
choices: %w[mixed passive aggressive], normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
)
]
end

View File

@@ -3,9 +3,10 @@ module WPScan
# Enumeration Methods
class Enumeration < CMSScanner::Controller::Base
# @param [ String ] type (plugins or themes)
# @param [ Symbol ] detection_mode
#
# @return [ String ] The related enumration message depending on the parsed_options and type supplied
def enum_message(type)
def enum_message(type, detection_mode)
return unless %w[plugins themes].include?(type)
details = if parsed_options[:enumerate][:"vulnerable_#{type}"]
@@ -16,7 +17,20 @@ module WPScan
'Most Popular'
end
"Enumerating #{details} #{type.capitalize}"
"Enumerating #{details} #{type.capitalize} #{enum_detection_message(detection_mode)}"
end
# @param [ Symbol ] detection_mode
#
# @return [ String ]
def enum_detection_message(detection_mode)
detection_method = if detection_mode == :mixed
'Passive and Aggressive'
else
detection_mode.to_s.capitalize
end
"(via #{detection_method} Methods)"
end
# @param [ String ] type (plugins, themes etc)
@@ -49,12 +63,15 @@ module WPScan
sort: true
)
output('@info', msg: enum_message('plugins')) if user_interaction?
output('@info', msg: enum_message('plugins', opts[:mode])) if user_interaction?
# Enumerate the plugins & find their versions to avoid doing that when #version
# is called in the view
plugins = target.plugins(opts)
output('@info', msg: 'Checking Plugin Versions') if user_interaction? && !plugins.empty?
if user_interaction? && !plugins.empty?
output('@info',
msg: "Checking Plugin Versions #{enum_detection_message(opts[:version_detection][:mode])}")
end
plugins.each(&:version)
@@ -92,12 +109,15 @@ module WPScan
sort: true
)
output('@info', msg: enum_message('themes')) if user_interaction?
output('@info', msg: enum_message('themes', opts[:mode])) if user_interaction?
# Enumerate the themes & find their versions to avoid doing that when #version
# is called in the view
themes = target.themes(opts)
output('@info', msg: 'Checking Theme Versions') if user_interaction? && !themes.empty?
if user_interaction? && !themes.empty?
output('@info',
msg: "Checking Theme Versions #{enum_detection_message(opts[:version_detection][:mode])}")
end
themes.each(&:version)
@@ -125,28 +145,33 @@ module WPScan
def enum_timthumbs
opts = default_opts('timthumbs').merge(list: parsed_options[:timthumbs_list])
output('@info', msg: 'Enumerating Timthumbs') if user_interaction?
output('@info', msg: "Enumerating Timthumbs #{enum_detection_message(opts[:mode])}") if user_interaction?
output('timthumbs', timthumbs: target.timthumbs(opts))
end
def enum_config_backups
opts = default_opts('config_backups').merge(list: parsed_options[:config_backups_list])
output('@info', msg: 'Enumerating Config Backups') if user_interaction?
output('@info', msg: "Enumerating Config Backups #{enum_detection_message(opts[:mode])}") if user_interaction?
output('config_backups', config_backups: target.config_backups(opts))
end
def enum_db_exports
opts = default_opts('db_exports').merge(list: parsed_options[:db_exports_list])
output('@info', msg: 'Enumerating DB Exports') if user_interaction?
output('@info', msg: "Enumerating DB Exports #{enum_detection_message(opts[:mode])}") if user_interaction?
output('db_exports', db_exports: target.db_exports(opts))
end
def enum_medias
opts = default_opts('medias').merge(range: parsed_options[:enumerate][:medias])
output('@info', msg: 'Enumerating Medias') if user_interaction?
if user_interaction?
output('@info',
msg: "Enumerating Medias #{enum_detection_message(opts[:mode])} "\
'(Permalink setting must be set to "Plain" for those to be detected)')
end
output('medias', medias: target.medias(opts))
end
@@ -163,7 +188,7 @@ module WPScan
list: parsed_options[:users_list]
)
output('@info', msg: 'Enumerating Users') if user_interaction?
output('@info', msg: "Enumerating Users #{enum_detection_message(opts[:mode])}") if user_interaction?
output('users', users: target.users(opts))
end

View File

@@ -7,8 +7,7 @@ module WPScan
OptChoice.new(
['--main-theme-detection MODE',
'Use the supplied mode for the Main theme detection, instead of the global (--detection-mode) mode.'],
choices: %w[mixed passive aggressive],
normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
)
]
end

View File

@@ -65,8 +65,12 @@ module WPScan
when :wp_login
WPScan::Finders::Passwords::WpLogin.new(target)
when :xmlrpc
raise XMLRPCNotDetected unless xmlrpc
WPScan::Finders::Passwords::XMLRPC.new(xmlrpc)
when :xmlrpc_multicall
raise XMLRPCNotDetected unless xmlrpc
WPScan::Finders::Passwords::XMLRPCMulticall.new(xmlrpc)
end
end

View File

@@ -4,13 +4,12 @@ module WPScan
class WpVersion < CMSScanner::Controller::Base
def cli_options
[
OptBoolean.new(['--wp-version-all', 'Check all the version locations']),
OptBoolean.new(['--wp-version-all', 'Check all the version locations'], advanced: true),
OptChoice.new(
['--wp-version-detection MODE',
'Use the supplied mode for the WordPress version detection, ' \
'instead of the global (--detection-mode) mode.'],
choices: %w[mixed passive aggressive],
normalize: :to_sym
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
)
]
end

View File

@@ -1,4 +1,5 @@
require_relative 'interesting_findings/readme'
require_relative 'interesting_findings/wp_cron'
require_relative 'interesting_findings/multisite'
require_relative 'interesting_findings/debug_log'
require_relative 'interesting_findings/backup_db'
@@ -23,7 +24,7 @@ module WPScan
%w[
Readme DebugLog FullPathDisclosure BackupDB DuplicatorInstallerLog
Multisite MuPlugins Registration UploadDirectoryListing TmmDbMigrate
UploadSQLDump EmergencyPwdResetScript
UploadSQLDump EmergencyPwdResetScript WPCron
].each do |f|
finders << InterestingFindings.const_get(f).new(target)
end

View File

@@ -11,7 +11,7 @@ module WPScan
return unless [200, 403].include?(res.code) && !target.homepage_or_404?(res)
WPScan::InterestingFinding.new(
WPScan::BackupDB.new(
url,
confidence: 70,
found_by: DIRECT_ACCESS,

View File

@@ -9,9 +9,10 @@ module WPScan
return unless target.debug_log?(path)
WPScan::InterestingFinding.new(
WPScan::DebugLog.new(
target.url(path),
confidence: 100, found_by: DIRECT_ACCESS
confidence: 100, found_by: DIRECT_ACCESS,
references: { url: 'https://codex.wordpress.org/Debugging_in_WordPress' }
)
end
end

View File

@@ -10,7 +10,7 @@ module WPScan
return unless res.body =~ /DUPLICATOR INSTALL-LOG/
WPScan::InterestingFinding.new(
WPScan::DuplicatorInstallerLog.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -10,7 +10,7 @@ module WPScan
return unless res.code == 200 && !target.homepage_or_404?(res)
WPScan::InterestingFinding.new(
WPScan::EmergencyPwdResetScript.new(
url,
confidence: res.body =~ /password/i ? 100 : 40,
found_by: DIRECT_ACCESS,

View File

@@ -10,11 +10,12 @@ module WPScan
return if fpd_entries.empty?
WPScan::InterestingFinding.new(
WPScan::FullPathDisclosure.new(
target.url(path),
confidence: 100,
found_by: DIRECT_ACCESS,
interesting_entries: fpd_entries
interesting_entries: fpd_entries,
references: { url: 'https://www.owasp.org/index.php/Full_Path_Disclosure' }
)
end
end

View File

@@ -12,7 +12,7 @@ module WPScan
url = target.url('wp-content/mu-plugins/')
return WPScan::InterestingFinding.new(
return WPScan::MuPlugins.new(
url,
confidence: 70,
found_by: 'URLs In Homepage (Passive Detection)',
@@ -35,7 +35,7 @@ module WPScan
target.mu_plugins = true
WPScan::InterestingFinding.new(
WPScan::MuPlugins.new(
url,
confidence: 80,
found_by: DIRECT_ACCESS,

View File

@@ -15,7 +15,7 @@ module WPScan
target.multisite = true
WPScan::InterestingFinding.new(
WPScan::Multisite.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -10,7 +10,7 @@ module WPScan
res = Browser.get(url)
if res.code == 200 && res.body =~ /wordpress/i
return WPScan::InterestingFinding.new(url, confidence: 100, found_by: DIRECT_ACCESS)
return WPScan::Readme.new(url, confidence: 100, found_by: DIRECT_ACCESS)
end
end
nil

View File

@@ -18,7 +18,7 @@ module WPScan
target.registration_enabled = true
WPScan::InterestingFinding.new(
WPScan::Registration.new(
res.effective_url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -11,7 +11,7 @@ module WPScan
return unless res.code == 200 && res.headers['Content-Type'] =~ %r{\Aapplication/zip}i
WPScan::InterestingFinding.new(
WPScan::TmmDbMigrate.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -11,7 +11,7 @@ module WPScan
url = target.url(path)
WPScan::InterestingFinding.new(
WPScan::UploadDirectoryListing.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -3,7 +3,7 @@ module WPScan
module InterestingFindings
# UploadSQLDump finder
class UploadSQLDump < CMSScanner::Finders::Finder
SQL_PATTERN = /(?:(?:(?:DROP|CREATE) TABLE)|INSERT INTO)/
SQL_PATTERN = /(?:(?:(?:DROP|CREATE) TABLE)|INSERT INTO)/.freeze
# @return [ InterestingFinding ]
def aggressive(_opts = {})
@@ -12,7 +12,7 @@ module WPScan
return unless res.code == 200 && res.body =~ SQL_PATTERN
WPScan::InterestingFinding.new(
WPScan::UploadSQLDump.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS

View File

@@ -0,0 +1,31 @@
module WPScan
module Finders
module InterestingFindings
# wp-cron.php finder
class WPCron < CMSScanner::Finders::Finder
# @return [ InterestingFinding ]
def aggressive(_opts = {})
res = Browser.get(wp_cron_url)
return unless res.code == 200
WPScan::WPCron.new(
wp_cron_url,
confidence: 60,
found_by: DIRECT_ACCESS,
references: {
url: [
'https://www.iplocation.net/defend-wordpress-from-ddos',
'https://github.com/wpscanteam/wpscan/issues/1299'
]
}
)
end
def wp_cron_url
@wp_cron_url ||= target.url('wp-cron.php')
end
end
end
end
end

View File

@@ -3,9 +3,9 @@ module WPScan
module MainTheme
# From the WooFramework meta generators
class WooFrameworkMetaGenerator < CMSScanner::Finders::Finder
THEME_PATTERN = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?"\s+/?>}
FRAMEWORK_PATTERN = %r{<meta name="generator" content="WooFramework\s?([^"]+)?"\s+/?>}
PATTERN = /#{THEME_PATTERN}\s+#{FRAMEWORK_PATTERN}/i
THEME_PATTERN = %r{<meta name="generator" content="([^\s"]+)\s?([^"]+)?"\s+/?>}.freeze
FRAMEWORK_PATTERN = %r{<meta name="generator" content="WooFramework\s?([^"]+)?"\s+/?>}.freeze
PATTERN = /#{THEME_PATTERN}\s+#{FRAMEWORK_PATTERN}/i.freeze
def passive(opts = {})
return unless target.homepage_res.body =~ PATTERN

View File

@@ -1,7 +1,7 @@
module WPScan
module Finders
module Medias
# Medias Finder
# Medias Finder, see https://github.com/wpscanteam/wpscan/issues/172
class AttachmentBruteForcing < CMSScanner::Finders::Finder
include CMSScanner::Finders::Finder::Enumerator

View File

@@ -4,6 +4,7 @@ require_relative 'users/oembed_api'
require_relative 'users/rss_generator'
require_relative 'users/author_id_brute_forcing'
require_relative 'users/login_error_messages'
require_relative 'users/yoast_seo_author_sitemap.rb'
module WPScan
module Finders
@@ -19,6 +20,7 @@ module WPScan
Users::WpJsonApi.new(target) <<
Users::OembedApi.new(target) <<
Users::RSSGenerator.new(target) <<
Users::YoastSeoAuthorSitemap.new(target) <<
Users::AuthorIdBruteForcing.new(target) <<
Users::LoginErrorMessages.new(target)
end

View File

@@ -14,29 +14,35 @@ module WPScan
# @param [ Hash ] opts
#
# TODO: make this code pretty :x
#
# @return [ Array<User> ]
def aggressive(_opts = {})
found = []
found_by_msg = 'Oembed API - %s (Aggressive Detection)'
oembed_data = JSON.parse(Browser.get(api_url).body)
details = user_details_from_oembed_data(oembed_data)
return [] unless details
[CMSScanner::User.new(details[0],
found_by: format(found_by_msg, details[1]),
confidence: details[2],
interesting_entries: [api_url])]
rescue JSON::ParserError
[]
end
def user_details_from_oembed_data(oembed_data)
return unless oembed_data
if oembed_data['author_url'] =~ %r{/author/([^/]+)/?\z}
details = [Regexp.last_match[1], 'Author URL', 90]
elsif oembed_data['author_name'] && !oembed_data['author_name'].empty?
details = [oembed_data['author_name'].delete(' '), 'Author Name', 70]
details = [oembed_data['author_name'], 'Author Name', 70]
end
return unless details
details
end
found << CMSScanner::User.new(details[0],
found_by: format(found_by_msg, details[1]),
confidence: details[2],
interesting_entries: [api_url])
rescue JSON::ParserError
found
def found_by_msg
'Oembed API - %s (Aggressive Detection)'
end
# @return [ String ] The URL of the API listing the Users

View File

@@ -4,20 +4,29 @@ module WPScan
# WP JSON API
#
# Since 4.7 - Need more investigation as it seems WP 4.7.1 reduces the exposure, see https://github.com/wpscanteam/wpscan/issues/1038)
# For the pagination, see https://github.com/wpscanteam/wpscan/issues/1285
#
class WpJsonApi < CMSScanner::Finders::Finder
MAX_PER_PAGE = 100 # See https://developer.wordpress.org/rest-api/using-the-rest-api/pagination/
# @param [ Hash ] opts
#
# @return [ Array<User> ]
def aggressive(_opts = {})
found = []
found = []
current_page = 0
JSON.parse(Browser.get(api_url).body)&.each do |user|
found << CMSScanner::User.new(user['slug'],
id: user['id'],
found_by: found_by,
confidence: 100,
interesting_entries: [api_url])
loop do
current_page += 1
res = Typhoeus.get(api_url, params: { per_page: MAX_PER_PAGE, page: current_page })
total_pages ||= res.headers['X-WP-TotalPages'].to_i
users_in_page = users_from_response(res)
found += users_in_page
break if current_page >= total_pages || users_in_page.empty?
end
found
@@ -25,6 +34,23 @@ module WPScan
found
end
# @param [ Typhoeus::Response ] response
#
# @return [ Array<User> ] The users from the response
def users_from_response(response)
found = []
JSON.parse(response.body)&.each do |user|
found << CMSScanner::User.new(user['slug'],
id: user['id'],
found_by: found_by,
confidence: 100,
interesting_entries: [response.effective_url])
end
found
end
# @return [ String ] The URL of the API listing the Users
def api_url
@api_url ||= target.url('wp-json/wp/v2/users/')

View File

@@ -0,0 +1,34 @@
module WPScan
module Finders
module Users
# The YOAST SEO plugin has an author-sitemap.xml which can leak usernames
# See https://github.com/wpscanteam/wpscan/issues/1228
class YoastSeoAuthorSitemap < CMSScanner::Finders::Finder
# @param [ Hash ] opts
#
# @return [ Array<User> ]
def aggressive(_opts = {})
found = []
Browser.get(sitemap_url).html.xpath('//url/loc').each do |user_tag|
username = user_tag.text.to_s[%r{/author/([^\/]+)/}, 1]
next unless username && !username.strip.empty?
found << CMSScanner::User.new(username,
found_by: found_by,
confidence: 100,
interesting_entries: [sitemap_url])
end
found
end
# @return [ String ] The URL of the author-sitemap
def sitemap_url
@sitemap_url ||= target.url('author-sitemap.xml')
end
end
end
end
end

View File

@@ -3,4 +3,46 @@ module WPScan
class InterestingFinding < CMSScanner::InterestingFinding
include References
end
#
# Empty classes for the #type to be correctly displayed (as taken from the self.class from the parent)
#
class BackupDB < InterestingFinding
end
class DebugLog < InterestingFinding
end
class DuplicatorInstallerLog < InterestingFinding
end
class EmergencyPwdResetScript < InterestingFinding
end
class FullPathDisclosure < InterestingFinding
end
class MuPlugins < InterestingFinding
end
class Multisite < InterestingFinding
end
class Readme < InterestingFinding
end
class Registration < InterestingFinding
end
class TmmDbMigrate < InterestingFinding
end
class UploadDirectoryListing < InterestingFinding
end
class UploadSQLDump < InterestingFinding
end
class WPCron < InterestingFinding
end
end

View File

@@ -50,5 +50,15 @@ module WPScan
@vulnerabilities
end
# @return [ String ]
def release_date
@release_date ||= db_data['release_date'] || 'Unknown'
end
# @return [ String ]
def status
@status ||= db_data['status'] || 'Unknown'
end
end
end

View File

@@ -1,5 +1,5 @@
<% if @version -%>
<%= info_icon %> WordPress version <%= @version.number %> identified.
<%= info_icon %> WordPress version <%= @version.number %> identified (<%= @version.status.capitalize %>, released on <%= @version.release_date %>).
<%= render('@finding', item: @version) -%>
<% else -%>
<%= notice_icon %> The WordPress version could not be detected.

View File

@@ -1,6 +1,8 @@
<% if @version -%>
"version": {
"number": <%= @version.number.to_json %>,
"release_date": <%= @version.release_date.to_json %>,
"status": <%= @version.status.to_json %>,
<%= render('@finding', item: @version) -%>
},
<% else -%>

View File

@@ -9,6 +9,6 @@ done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
cd $DIR/../
docker build -q -t wpscanv3:git .
docker run -it --rm wpscanv3:git "$@"
docker build -q -t wpscan:git .
docker run -it --rm wpscan:git "$@"

View File

@@ -10,7 +10,7 @@ DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
cd $DIR/../
if [[ -n "$WPSCAN_BUILD" ]]; then
docker build -q -t wpscanv3:git .
docker build -q -t wpscan:git .
fi
docker run -it --rm -v $DIR/../:/wpscan wpscanv3:git "$@"
docker run -it --rm -v $DIR/../:/wpscan wpscan:git "$@"

View File

@@ -16,9 +16,7 @@ require 'securerandom'
require 'wpscan/helper'
require 'wpscan/db'
require 'wpscan/version'
require 'wpscan/errors/wordpress'
require 'wpscan/errors/http'
require 'wpscan/errors/update'
require 'wpscan/errors'
require 'wpscan/browser'
require 'wpscan/target'
require 'wpscan/finders'
@@ -35,7 +33,7 @@ module WPScan
include CMSScanner
APP_DIR = Pathname.new(__FILE__).dirname.join('..', 'app').expand_path
DB_DIR = File.join(Dir.home, '.wpscan', 'db')
DB_DIR = Pathname.new(Dir.home).join('.wpscan', 'db')
# Override, otherwise it would be returned as 'wp_scan'
#

View File

@@ -5,7 +5,7 @@ module WPScan
# @return [ String ] The path to the user agents list
def user_agents_list
@user_agents_list ||= File.join(DB_DIR, 'user-agents.txt')
@user_agents_list ||= DB_DIR.join('user-agents.txt').to_s
end
# @return [ String ]

View File

@@ -4,7 +4,7 @@ module WPScan
class Base
# @return [ String ]
def self.db_file
@db_file ||= File.join(DB_DIR, 'dynamic_finders.yml')
@db_file ||= DB_DIR.join('dynamic_finders.yml').to_s
end
# @return [ Hash ]

View File

@@ -33,7 +33,7 @@ module WPScan
# @return [ String ]
def self.wp_fingerprints_path
@wp_fingerprints_path ||= File.join(DB_DIR, 'wp_fingerprints.json')
@wp_fingerprints_path ||= DB_DIR.join('wp_fingerprints.json').to_s
end
# @return [ Hash ]

View File

@@ -4,7 +4,7 @@ module WPScan
class Plugin < WpItem
# @return [ String ]
def self.db_file
@db_file ||= File.join(DB_DIR, 'plugins.json')
@db_file ||= DB_DIR.join('plugins.json').to_s
end
end
end

View File

@@ -4,7 +4,7 @@ module WPScan
class Theme < WpItem
# @return [ String ]
def self.db_file
@db_file ||= File.join(DB_DIR, 'themes.json')
@db_file ||= DB_DIR.join('themes.json').to_s
end
end
end

View File

@@ -15,11 +15,11 @@ module WPScan
attr_reader :repo_directory
def initialize(repo_directory)
@repo_directory = repo_directory
@repo_directory = Pathname.new(repo_directory).expand_path
FileUtils.mkdir_p(repo_directory) unless Dir.exist?(repo_directory)
FileUtils.mkdir_p(repo_directory.to_s) unless Dir.exist?(repo_directory.to_s)
raise "#{repo_directory} is not writable" unless Pathname.new(repo_directory).writable?
raise "#{repo_directory} is not writable" unless repo_directory.writable?
delete_old_files
end
@@ -41,7 +41,7 @@ module WPScan
# @return [ String ]
def last_update_file
@last_update_file ||= File.join(repo_directory, '.last_update')
@last_update_file ||= repo_directory.join('.last_update').to_s
end
# @return [ Boolean ]
@@ -54,18 +54,17 @@ module WPScan
# @return [ Boolean ]
def missing_files?
FILES.each do |file|
return true unless File.exist?(File.join(repo_directory, file))
return true unless File.exist?(repo_directory.join(file))
end
false
end
# @return [ Hash ] The params for Typhoeus::Request
# @note Those params can't be overriden by CLI options
def request_params
{
ssl_verifyhost: 2,
ssl_verifypeer: true,
timeout: 300,
connecttimeout: 120,
timeout: 600,
connecttimeout: 300,
accept_encoding: 'gzip, deflate',
cache_ttl: 0
}
@@ -86,16 +85,18 @@ module WPScan
res.body.chomp
end
# @return [ String ]
def local_file_path(filename)
File.join(repo_directory, filename.to_s)
repo_directory.join(filename.to_s).to_s
end
def local_file_checksum(filename)
Digest::SHA512.file(local_file_path(filename)).hexdigest
end
# @return [ String ]
def backup_file_path(filename)
File.join(repo_directory, "#{filename}.back")
repo_directory.join("#{filename}.back").to_s
end
def create_backup(filename)

View File

@@ -4,7 +4,7 @@ module WPScan
class Version < WpItem
# @return [ String ]
def self.db_file
@db_file ||= File.join(DB_DIR, 'wordpresses.json')
@db_file ||= DB_DIR.join('wordpresses.json').to_s
end
end
end

9
lib/wpscan/errors.rb Normal file
View File

@@ -0,0 +1,9 @@
module WPScan
class Error < StandardError
end
end
require_relative 'errors/http'
require_relative 'errors/update'
require_relative 'errors/wordpress'
require_relative 'errors/xmlrpc'

View File

@@ -1,6 +1,6 @@
module WPScan
# HTTP Error
class HTTPError < StandardError
class HTTPError < Error
attr_reader :response
# @param [ Typhoeus::Response ] res

View File

@@ -1,6 +1,6 @@
module WPScan
# Error raised when there is a missing DB file and --no-update supplied
class MissingDatabaseFile < StandardError
class MissingDatabaseFile < Error
def to_s
'Update required, you can not run a scan if a database file is missing.'
end

View File

@@ -1,20 +1,20 @@
module WPScan
# WordPress hosted (*.wordpress.com)
class WordPressHostedError < StandardError
class WordPressHostedError < Error
def to_s
'Scanning *.wordpress.com hosted blogs is not supported.'
end
end
# Not WordPress Error
class NotWordPressError < StandardError
class NotWordPressError < Error
def to_s
'The remote website is up, but does not seem to be running WordPress.'
end
end
# Invalid Wp Version (used in the WpVersion#new)
class InvalidWordPressVersion < StandardError
class InvalidWordPressVersion < Error
def to_s
'The WordPress version is invalid'
end

View File

@@ -0,0 +1,8 @@
module WPScan
# XML-RPC Not Detected
class XMLRPCNotDetected < Error
def to_s
'The XML-RPC Interface was not detected.'
end
end
end

View File

@@ -9,7 +9,7 @@ module WPScan
module WordPress
include CMSScanner::Target::Platform::PHP
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu\-)?plugins|uploads))|wp-includes)/}i
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu\-)?plugins|uploads))|wp-includes)/}i.freeze
# These methods are used in the associated interesting_findings finders
# to keep the boolean state of the finding rather than re-check the whole thing again
@@ -18,10 +18,10 @@ module WPScan
alias registration_enabled? registration_enabled
alias mu_plugins? mu_plugins
# @param [ Symbol ] detection_mode
#
# @return [ Boolean ]
def wordpress?
# res = Browser.get(url)
def wordpress?(detection_mode)
in_scope_urls(homepage_res) do |url|
return true if Addressable::URI.parse(url).path.match(WORDPRESS_PATTERN)
end
@@ -32,6 +32,14 @@ module WPScan
return true unless comments_from_page(/wordpress/i, homepage_res).empty?
if %i[mixed aggressive].include?(detection_mode)
%w[wp-admin/install.php wp-login.php].each do |path|
in_scope_urls(Browser.get_and_follow_location(url(path))).each do |url|
return true if Addressable::URI.parse(url).path.match(WORDPRESS_PATTERN)
end
end
end
false
end
@@ -41,7 +49,7 @@ module WPScan
end
def wordpress_hosted?
uri.host =~ /wordpress.com$/i ? true : false
uri.host =~ /\.wordpress\.com$/i ? true : false
end
# @param [ String ] username

View File

@@ -1,4 +1,4 @@
# Version
module WPScan
VERSION = '3.3.1'.freeze
VERSION = '3.4.5'.freeze
end

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Controller::Aliases do
subject(:controller) { described_class.new }
let(:target_url) { 'http://ex.lo/' }

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Controller::Core do
subject(:core) { described_class.new }
let(:target_url) { 'http://ex.lo/' }
@@ -22,7 +20,7 @@ describe WPScan::Controller::Core do
# Ensures the :url is the first one and is correctly setup
expect(cli_options.first.to_sym).to eql :url
expect(cli_options.first.required_unless).to match_array %i[update help version]
expect(cli_options.first.required_unless).to match_array %i[update help hh version]
end
end
@@ -167,7 +165,7 @@ describe WPScan::Controller::Core do
before do
expect(core).to receive(:load_server_module)
expect(core.target).to receive(:wordpress?).and_return(true)
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true)
end
it 'calls the formatter when started and finished to update the db' do
@@ -210,7 +208,7 @@ describe WPScan::Controller::Core do
context 'when wordpress' do
it 'does not raise an error' do
expect(core.target).to receive(:wordpress?).and_return(true)
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true)
expect { core.before_scan }.to_not raise_error
end
@@ -218,7 +216,7 @@ describe WPScan::Controller::Core do
context 'when not wordpress' do
it 'raises an error' do
expect(core.target).to receive(:wordpress?).and_return(false)
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(false)
expect { core.before_scan }.to raise_error(WPScan::NotWordPressError)
end
@@ -239,7 +237,7 @@ describe WPScan::Controller::Core do
context 'when wordpress' do
before do
expect(core).to receive(:load_server_module)
expect(core.target).to receive(:wordpress?).and_return(true)
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(true)
end
it 'does not raise any error' do
@@ -250,7 +248,7 @@ describe WPScan::Controller::Core do
context 'when not wordpress' do
before do
expect(core).to receive(:load_server_module)
expect(core.target).to receive(:wordpress?).and_return(false)
expect(core.target).to receive(:wordpress?).with(:mixed).and_return(false)
end
context 'when no --force' do

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Controller::CustomDirectories do
subject(:controller) { described_class.new }
let(:target_url) { 'http://ex.lo/' }

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Controller::Enumeration do
subject(:controller) { described_class.new }
let(:target_url) { 'http://wp.lab/' }
@@ -16,10 +14,11 @@ describe WPScan::Controller::Enumeration do
end
describe '#enum_message' do
after { expect(controller.enum_message(type)).to eql @expected }
after { expect(controller.enum_message(type, detection_mode)).to eql @expected }
context 'when type argument is incorrect' do
let(:type) { 'spec' }
let(:type) { 'spec' }
let(:detection_mode) { :mixed }
it 'returns nil' do
@expected = nil
@@ -28,29 +27,32 @@ describe WPScan::Controller::Enumeration do
%w[plugins themes].each do |t|
context "type = #{t}" do
let(:type) { t }
let(:type) { t }
let(:detection_mode) { :mixed }
context 'when vulnerable' do
let(:cli_args) { "#{super()} -e v#{type[0]}" }
it 'returns the expected string' do
@expected = "Enumerating Vulnerable #{type.capitalize}"
@expected = "Enumerating Vulnerable #{type.capitalize} (via Passive and Aggressive Methods)"
end
end
context 'when all' do
let(:cli_args) { "#{super()} -e a#{type[0]}" }
let(:cli_args) { "#{super()} -e a#{type[0]}" }
let(:detection_mode) { :passive }
it 'returns the expected string' do
@expected = "Enumerating All #{type.capitalize}"
@expected = "Enumerating All #{type.capitalize} (via Passive Methods)"
end
end
context 'when most popular' do
let(:cli_args) { "#{super()} -e #{type[0]}" }
let(:cli_args) { "#{super()} -e #{type[0]}" }
let(:detection_mode) { :aggressive }
it 'returns the expected string' do
@expected = "Enumerating Most Popular #{type.capitalize}"
@expected = "Enumerating Most Popular #{type.capitalize} (via Aggressive Methods)"
end
end
end

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Controller::PasswordAttack do
subject(:controller) { described_class.new }
let(:target_url) { 'http://ex.lo/' }
@@ -68,25 +66,49 @@ describe WPScan::Controller::PasswordAttack do
end
context 'when xmlrpc' do
before do
expect(controller.target).to receive(:xmlrpc).and_return(WPScan::XMLRPC.new("#{target_url}/xmlrpc.php"))
end
context 'when xmlrpc not detected on target' do
before do
expect(controller.target).to receive(:xmlrpc).and_return(nil)
end
context 'when single xmlrpc' do
let(:attack) { 'xmlrpc' }
context 'when single xmlrpc' do
let(:attack) { 'xmlrpc' }
it 'returns the correct object' do
expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC
expect(controller.attacker.target).to be_a WPScan::XMLRPC
it 'raises an error' do
expect { controller.attacker }.to raise_error(WPScan::XMLRPCNotDetected)
end
end
context 'when xmlrpc-multicall' do
let(:attack) { 'xmlrpc-multicall' }
it 'raises an error' do
expect { controller.attacker }.to raise_error(WPScan::XMLRPCNotDetected)
end
end
end
context 'when xmlrpc-multicall' do
let(:attack) { 'xmlrpc-multicall' }
context 'when xmlrpc detected on target' do
before do
expect(controller.target).to receive(:xmlrpc).and_return(WPScan::XMLRPC.new("#{target_url}/xmlrpc.php"))
end
it 'returns the correct object' do
expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPCMulticall
expect(controller.attacker.target).to be_a WPScan::XMLRPC
context 'when single xmlrpc' do
let(:attack) { 'xmlrpc' }
it 'returns the correct object' do
expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPC
expect(controller.attacker.target).to be_a WPScan::XMLRPC
end
end
context 'when xmlrpc-multicall' do
let(:attack) { 'xmlrpc-multicall' }
it 'returns the correct object' do
expect(controller.attacker).to be_a WPScan::Finders::Passwords::XMLRPCMulticall
expect(controller.attacker.target).to be_a WPScan::XMLRPC
end
end
end
end

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
def it_calls_the_formatter_with_the_correct_parameter(version)
it 'calls the formatter with the correct parameter' do
expect(controller.formatter).to receive(:output)

View File

@@ -1,11 +1,9 @@
require 'spec_helper'
describe WPScan::Finders::ConfigBackups::KnownFilenames do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'config_backups') }
let(:opts) { { list: File.join(WPScan::DB_DIR, 'config_backups.txt') } }
let(:fixtures) { FINDERS_FIXTURES.join('config_backups') }
let(:opts) { { list: WPScan::DB_DIR.join('config_backups.txt').to_s } }
describe '#aggressive' do
before do
@@ -25,7 +23,7 @@ describe WPScan::Finders::ConfigBackups::KnownFilenames do
context 'when some files exist' do
let(:files) { ['%23wp-config.php%23', 'wp-config.bak'] }
let(:config_backup) { File.read(File.join(fixtures, 'wp-config.php')) }
let(:config_backup) { File.read(fixtures.join('wp-config.php')) }
before do
files.each do |file|

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Finders::ConfigBackups::Base do
subject(:config_backups) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }

View File

@@ -1,11 +1,9 @@
require 'spec_helper'
describe WPScan::Finders::DbExports::KnownLocations do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://ex.lo/aa/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'db_exports') }
let(:opts) { { list: File.join(WPScan::DB_DIR, 'db_exports.txt') } }
let(:fixtures) { FINDERS_FIXTURES.join('db_exports') }
let(:opts) { { list: WPScan::DB_DIR.join('db_exports.txt').to_s } }
describe '#potential_urls' do
before do
@@ -42,7 +40,7 @@ describe WPScan::Finders::DbExports::KnownLocations do
context 'when some files exist' do
let(:files) { %w[ex.sql backups/db_backup.sql] }
let(:db_export) { File.read(File.join(fixtures, 'dump.sql')) }
let(:db_export) { File.read(fixtures.join('dump.sql')) }
before do
files.each do |file|

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Finders::DbExports::Base do
subject(:db_exports) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::BackupDB do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'backup_db') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'backup_db') }
let(:wp_content) { 'wp-content' }
let(:dir_url) { target.url("#{wp_content}/backup-db/") }
@@ -37,7 +35,7 @@ describe WPScan::Finders::InterestingFindings::BackupDB do
after do
found = finder.aggressive
expect(found).to eql WPScan::InterestingFinding.new(
expect(found).to eql WPScan::BackupDB.new(
dir_url,
confidence: 70,
found_by: described_class::DIRECT_ACCESS
@@ -53,7 +51,7 @@ describe WPScan::Finders::InterestingFindings::BackupDB do
end
context 'when directory listing enabled' do
let(:body) { File.read(File.join(fixtures, 'dir_listing.html')) }
let(:body) { File.read(fixtures.join('dir_listing.html')) }
it 'returns the expected interesting_findings attribute' do
@expected_entries = %w[sqldump.sql test.txt]

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::DebugLog do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'debug_log') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'debug_log') }
let(:wp_content) { 'wp-content' }
let(:log_url) { target.url("#{wp_content}/debug.log") }
@@ -20,10 +18,10 @@ describe WPScan::Finders::InterestingFindings::DebugLog do
end
context 'when a log file' do
let(:body) { File.read(File.join(fixtures, 'debug.log')) }
let(:body) { File.read(fixtures.join('debug.log')) }
it 'returns the InterestingFinding' do
expect(finder.aggressive).to eql WPScan::InterestingFinding.new(
expect(finder.aggressive).to eql WPScan::DebugLog.new(
log_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::DuplicatorInstallerLog do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'duplicator_installer_log') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'duplicator_installer_log') }
let(:filename) { 'installer-log.txt' }
let(:log_url) { target.url(filename) }
@@ -21,10 +19,10 @@ describe WPScan::Finders::InterestingFindings::DuplicatorInstallerLog do
end
context 'when the body matches' do
let(:body) { File.read(File.join(fixtures, filename)) }
let(:body) { File.read(fixtures.join(filename)) }
it 'returns the InterestingFinding' do
expect(finder.aggressive).to eql WPScan::InterestingFinding.new(
expect(finder.aggressive).to eql WPScan::DuplicatorInstallerLog.new(
log_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::EmergencyPwdResetScript do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'emergency_pwd_reset_script') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'emergency_pwd_reset_script') }
describe '#aggressive' do
xit

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::FullPathDisclosure do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'fpd') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'fpd') }
let(:file_url) { target.url('wp-includes/rss-functions.php') }
describe '#aggressive' do
@@ -20,12 +18,12 @@ describe WPScan::Finders::InterestingFindings::FullPathDisclosure do
end
context 'when a log file' do
let(:body) { File.read(File.join(fixtures, 'rss_functions.php')) }
let(:body) { File.read(fixtures.join('rss_functions.php')) }
it 'returns the InterestingFinding' do
found = finder.aggressive
expect(found).to eql WPScan::InterestingFinding.new(
expect(found).to eql WPScan::FullPathDisclosure.new(
file_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::MuPlugins do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'mu_plugins') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'mu_plugins') }
describe '#passive' do
xit

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::Multisite do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'multisite') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'multisite') }
describe '#aggressive' do
xit

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::Readme do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'readme') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'readme') }
describe '#aggressive' do
before do
@@ -22,12 +20,12 @@ describe WPScan::Finders::InterestingFindings::Readme do
# TODO: case when multiple files are present ? (should return only the first one found)
context 'when a file exists' do
let(:file) { finder.potential_files.sample }
let(:readme) { File.read(File.join(fixtures, 'readme-3.9.2.html')) }
let(:readme) { File.read(fixtures.join('readme-3.9.2.html')) }
before { stub_request(:get, target.url(file)).to_return(body: readme) }
it 'returns the expected InterestingFinding' do
expected = WPScan::InterestingFinding.new(
expected = WPScan::Readme.new(
target.url(file),
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::Registration do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'registration') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'registration') }
describe '#aggressive' do
xit

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::TmmDbMigrate do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'tmm_db_migrate') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'tmm_db_migrate') }
describe '#aggressive' do
xit

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::UploadDirectoryListing do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'upload_directory_listing') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'upload_directory_listing') }
let(:wp_content) { 'wp-content' }
describe '#aggressive' do

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::UploadSQLDump do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'interesting_findings', 'upload_sql_dump') }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'upload_sql_dump') }
let(:wp_content) { 'wp-content' }
describe '#aggressive' do
@@ -23,7 +21,7 @@ describe WPScan::Finders::InterestingFindings::UploadSQLDump do
context 'when a 200' do
before do
stub_request(:get, finder.dump_url)
.to_return(status: 200, body: File.read(File.join(fixtures, fixture)))
.to_return(status: 200, body: File.read(fixtures.join(fixture)))
end
context 'when the body does not match a SQL dump' do
@@ -38,7 +36,7 @@ describe WPScan::Finders::InterestingFindings::UploadSQLDump do
let(:fixture) { 'dump.sql' }
it 'returns the interesting findings' do
@expected = WPScan::InterestingFinding.new(
@expected = WPScan::UploadSQLDump.new(
finder.dump_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS

View File

@@ -0,0 +1,30 @@
describe WPScan::Finders::InterestingFindings::WPCron do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://ex.lo/' }
let(:wp_content) { 'wp-content' }
before { expect(target).to receive(:sub_dir).at_least(1).and_return(false) }
describe '#aggressive' do
before { stub_request(:get, finder.wp_cron_url).to_return(status: status) }
context 'when 200' do
let(:status) { 200 }
it 'returns the InterestingFinding' do
expect(finder.aggressive).to eql WPScan::WPCron.new(
finder.wp_cron_url,
confidence: 60,
found_by: described_class::DIRECT_ACCESS
)
end
end
context 'otherwise' do
let(:status) { 403 }
its(:aggressive) { should be_nil }
end
end
end

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Finders::InterestingFindings::Base do
subject(:files) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }

View File

@@ -1,14 +1,12 @@
require 'spec_helper'
describe WPScan::Finders::MainTheme::CssStyle do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'main_theme', 'css_style') }
let(:fixtures) { FINDERS_FIXTURES.join('main_theme', 'css_style') }
describe '#passive' do
after do
stub_request(:get, url).to_return(body: File.read(File.join(fixtures, fixture)))
stub_request(:get, url).to_return(body: File.read(fixtures.join(fixture)))
expect(finder.passive).to eql @expected
end

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::MainTheme::UrlsInHomepage do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'main_theme', 'urls_in_homepage') }
let(:fixtures) { FINDERS_FIXTURES.join('main_theme', 'urls_in_homepage') }
it_behaves_like 'App::Finders::WpItems::URLsInHomepage' do
let(:type) { 'themes' }
@@ -17,7 +15,7 @@ describe WPScan::Finders::MainTheme::UrlsInHomepage do
describe '#passive' do
before do
stub_request(:get, /.*.css/)
stub_request(:get, target.url).to_return(body: File.read(File.join(fixtures, 'found.html')))
stub_request(:get, target.url).to_return(body: File.read(fixtures.join('found.html')))
end
it 'returns the expected Themes' do

View File

@@ -1,14 +1,12 @@
require 'spec_helper'
describe WPScan::Finders::MainTheme::WooFrameworkMetaGenerator do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'main_theme', 'woo_framework_meta_generator') }
let(:fixtures) { FINDERS_FIXTURES.join('main_theme', 'woo_framework_meta_generator') }
describe '#passive' do
after do
stub_request(:get, url).to_return(body: File.read(File.join(fixtures, @file)))
stub_request(:get, url).to_return(body: File.read(fixtures.join(@file)))
expect(finder.passive).to eql @expected
end

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Finders::MainTheme::Base do
subject(:main_theme) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::Medias::AttachmentBruteForcing do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'medias', 'attachment_brute_forcing') }
let(:fixtures) { FINDERS_FIXTURES.join('medias', 'attachment_brute_forcing') }
describe '#aggressive' do
xit

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Finders::Medias::Base do
subject(:media) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::PluginVersion::Readme do
subject(:finder) { described_class.new(plugin) }
let(:plugin) { WPScan::Plugin.new('spec', target) }
let(:target) { WPScan::Target.new('http://wp.lab/') }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'plugin_version', 'readme') }
let(:fixtures) { FINDERS_FIXTURES.join('plugin_version', 'readme') }
def version(number, found_by, confidence)
WPScan::Version.new(
@@ -28,7 +26,7 @@ describe WPScan::Finders::PluginVersion::Readme do
after do
stub_request(:get, /.*/).to_return(status: 404)
stub_request(:get, readme_url).to_return(body: File.read(File.join(fixtures, @file)))
stub_request(:get, readme_url).to_return(body: File.read(fixtures.join(@file)))
expect(finder.aggressive).to eql @expected
end

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
# If this file is tested alone (rspec path-to-this-file), then there will be an error about
# constants not being intilialized. This is due to the Dynamic Finders.

View File

@@ -1,11 +1,9 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::BodyPattern do
it_behaves_like WPScan::Finders::DynamicFinder::WpItems::Finder do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(DYNAMIC_FINDERS_FIXTURES, 'plugin_version') }
let(:fixtures) { DYNAMIC_FINDERS_FIXTURES.join('plugin_version') }
let(:expected_all) { df_expected_all['plugins'] }
let(:item_class) { WPScan::Plugin }

View File

@@ -1,11 +1,9 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::Comment do
it_behaves_like WPScan::Finders::DynamicFinder::WpItems::Finder do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(DYNAMIC_FINDERS_FIXTURES, 'plugin_version') }
let(:fixtures) { DYNAMIC_FINDERS_FIXTURES.join('plugin_version') }
let(:expected_all) { df_expected_all['plugins'] }
let(:item_class) { WPScan::Plugin }

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::ConfigParser do
xit
@@ -7,7 +5,7 @@ describe WPScan::Finders::Plugins::ConfigParser do
# subject(:finder) { described_class.new(target) }
# let(:target) { WPScan::Target.new(url) }
# let(:url) { 'http://wp.lab/' }
# let(:fixtures) { File.join(DYNAMIC_FINDERS_FIXTURES, 'plugin_version') }
# let(:fixtures) { DYNAMIC_FINDERS_FIXTURES.join('plugin_version') }
#
# let(:expected_all) { df_expected_all['plugins'] }
# let(:item_class) { WPScan::Plugin }

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::HeaderPattern do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(DYNAMIC_FINDERS_FIXTURES, 'plugin_version') }
let(:fixtures) { DYNAMIC_FINDERS_FIXTURES.join('plugin_version') }
def plugin(slug)
WPScan::Plugin.new(slug, target)
@@ -31,7 +29,7 @@ describe WPScan::Finders::Plugins::HeaderPattern do
context 'when headers' do
before { expect(target).to receive(:content_dir).and_return('wp-content') }
let(:headers) { JSON.parse(File.read(File.join(fixtures, 'header_pattern_passive_all.html'))) }
let(:headers) { JSON.parse(File.read(fixtures.join('header_pattern_passive_all.html'))) }
it 'returns the expected plugins' do
@expected = []

View File

@@ -1,11 +1,9 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::JavascriptVar do
it_behaves_like WPScan::Finders::DynamicFinder::WpItems::Finder do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(DYNAMIC_FINDERS_FIXTURES, 'plugin_version') }
let(:fixtures) { DYNAMIC_FINDERS_FIXTURES.join('plugin_version') }
let(:expected_all) { df_expected_all['plugins'] }
let(:item_class) { WPScan::Plugin }

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::KnownLocations do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'plugins', 'known_locations') }
let(:fixtures) { FINDERS_FIXTURES.join('plugins', 'known_locations') }
describe '#aggressive' do
xit

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::QueryParameter do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(DYNAMIC_FINDERS_FIXTURES, 'plugin_version') }
let(:fixtures) { DYNAMIC_FINDERS_FIXTURES.join('plugin_version') }
describe '#passive' do
its(:passive) { should be nil }

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::UrlsInHomepage do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'plugins', 'urls_in_homepage') }
let(:fixtures) { FINDERS_FIXTURES.join('plugins', 'urls_in_homepage') }
it_behaves_like 'App::Finders::WpItems::URLsInHomepage' do
let(:type) { 'plugins' }
@@ -17,7 +15,7 @@ describe WPScan::Finders::Plugins::UrlsInHomepage do
describe '#passive' do
before do
stub_request(:get, finder.target.url)
.to_return(body: File.read(File.join(fixtures, 'found.html')))
.to_return(body: File.read(fixtures.join('found.html')))
expect(finder.target).to receive(:content_dir).at_least(1).and_return('wp-content')
end

View File

@@ -1,11 +1,9 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::Xpath do
it_behaves_like WPScan::Finders::DynamicFinder::WpItems::Finder do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { File.join(DYNAMIC_FINDERS_FIXTURES, 'plugin_version') }
let(:fixtures) { DYNAMIC_FINDERS_FIXTURES.join('plugin_version') }
let(:expected_all) { df_expected_all['plugins'] }
let(:item_class) { WPScan::Plugin }

View File

@@ -1,5 +1,3 @@
require 'spec_helper'
describe WPScan::Finders::Plugins::Base do
subject(:plugins) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }

View File

@@ -1,13 +1,11 @@
require 'spec_helper'
describe WPScan::Finders::ThemeVersion::Style do
subject(:finder) { described_class.new(theme) }
let(:theme) { WPScan::Theme.new('spec', target) }
let(:target) { WPScan::Target.new('http://wp.lab/') }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'theme_version', 'style') }
let(:fixtures) { FINDERS_FIXTURES.join('theme_version', 'style') }
before :all do
Typhoeus::Config.cache = WPScan::Cache::Typhoeus.new(File.join(SPECS, 'cache'))
Typhoeus::Config.cache = WPScan::Cache::Typhoeus.new(SPECS.join('cache'))
end
before do
@@ -79,7 +77,7 @@ describe WPScan::Finders::ThemeVersion::Style do
'no_version' => nil
}.each do |file, expected_version|
context "when #{file}" do
let(:style_body) { File.new(File.join(fixtures, "#{file}.css")) }
let(:style_body) { File.new(fixtures.join("#{file}.css")) }
it 'returns the expected version' do
expected = if expected_version

View File

@@ -1,10 +1,8 @@
require 'spec_helper'
describe WPScan::Finders::ThemeVersion::WooFrameworkMetaGenerator do
subject(:finder) { described_class.new(theme) }
let(:theme) { WPScan::Theme.new(slug, target) }
let(:target) { WPScan::Target.new('http://wp.lab/') }
let(:fixtures) { File.join(FINDERS_FIXTURES, 'theme_version', 'woo_framework_meta_generator') }
let(:fixtures) { FINDERS_FIXTURES.join('theme_version', 'woo_framework_meta_generator') }
before do
expect(target).to receive(:content_dir).and_return('wp-content')
@@ -13,7 +11,7 @@ describe WPScan::Finders::ThemeVersion::WooFrameworkMetaGenerator do
describe '#passive' do
after do
stub_request(:get, target.url).to_return(body: File.read(File.join(fixtures, 'editorial-1.3.5.html')))
stub_request(:get, target.url).to_return(body: File.read(fixtures.join('editorial-1.3.5.html')))
expect(finder.passive).to eql @expected
end

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