Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adff971d62 | ||
|
|
23b22f71b8 | ||
|
|
fee3671e32 | ||
|
|
26c6be7268 | ||
|
|
01c5bcf2be | ||
|
|
1ab8a5ab98 | ||
|
|
b54aaca28a | ||
|
|
86a29ae000 | ||
|
|
a5dbee93ff | ||
|
|
e0465e6e10 | ||
|
|
7da48b9dd1 | ||
|
|
a64895c3a6 | ||
|
|
21f1a5d4c4 | ||
|
|
d60f79ca33 | ||
|
|
2d5cea5033 | ||
|
|
b0615215fe | ||
|
|
7a0f98b2cb | ||
|
|
cdc1dab4a6 | ||
|
|
431739ab19 | ||
|
|
1780399050 | ||
|
|
eb75d38716 | ||
|
|
06f82d78f4 | ||
|
|
dee4da1c0e | ||
|
|
e341ec7c60 | ||
|
|
9146609e4a | ||
|
|
f90615ca41 | ||
|
|
8a2a6a05ff | ||
|
|
5a787f8ed5 | ||
|
|
a904053002 | ||
|
|
70ecd30dcc | ||
|
|
b0976d7e47 | ||
|
|
bb5e55016c | ||
|
|
abdf285c69 | ||
|
|
fd4da23d4f | ||
|
|
bb8f58c83b | ||
|
|
077da6ae86 | ||
|
|
d5222d7e9a | ||
|
|
01702c127b | ||
|
|
87902cbfb4 | ||
|
|
fcaa393ffe | ||
|
|
18bac6e792 | ||
|
|
9a21efebe3 | ||
|
|
357182ef17 | ||
|
|
5fad540a4c | ||
|
|
c1fc153420 | ||
|
|
73a1974f85 | ||
|
|
dec73c21b6 | ||
|
|
46a00cc864 | ||
|
|
62455be165 | ||
|
|
17ef5ef918 | ||
|
|
922b6fffd0 | ||
|
|
b47bf006d0 | ||
|
|
d60269f4bc | ||
|
|
1ce057a78e | ||
|
|
a0fe04b990 | ||
|
|
31c9172e19 | ||
|
|
7f23cbef71 | ||
|
|
4884defaed | ||
|
|
3039218c40 | ||
|
|
8bbc2f32ae | ||
|
|
4ca46ab3ba | ||
|
|
7442c72d01 | ||
|
|
01cd8350bc | ||
|
|
8b5ea589db | ||
|
|
3555ca1d1e | ||
|
|
ae034a47ed | ||
|
|
ec3862c930 | ||
|
|
c63804d1c5 | ||
|
|
c5e6752f75 | ||
|
|
e4f3e9d11c | ||
|
|
f3713536b9 | ||
|
|
fb751c0a51 | ||
|
|
9d3464055a | ||
|
|
0fea814f5d | ||
|
|
ae70a6df9d | ||
|
|
4afc756ccd | ||
|
|
adc5841261 | ||
|
|
41cca5fb8a | ||
|
|
498da1a06b | ||
|
|
48dab90313 | ||
|
|
d1ff642957 | ||
|
|
2b5613d84a | ||
|
|
09d28fae26 | ||
|
|
7517e247d9 | ||
|
|
998951e629 | ||
|
|
d89fcbb68a | ||
|
|
d3e0ff1e66 | ||
|
|
804a8c34c6 | ||
|
|
57942e1826 | ||
|
|
5657735b55 | ||
|
|
791fce2424 | ||
|
|
c34fa45875 | ||
|
|
e0fd79f800 | ||
|
|
f9d9cda4a4 | ||
|
|
d6f44b2f42 | ||
|
|
bd90da7ed2 | ||
|
|
3a1a976e35 | ||
|
|
db1309af83 | ||
|
|
0e47441a36 | ||
|
|
375bea9a8b | ||
|
|
3a42772879 | ||
|
|
e9956593dc | ||
|
|
fda6000c4c | ||
|
|
99b4eb969d | ||
|
|
dadd55ba32 | ||
|
|
b40e06b2ea | ||
|
|
3f20edc41f |
@@ -8,6 +8,7 @@ spec/
|
||||
.*
|
||||
**/*.md
|
||||
*.md
|
||||
!README.md
|
||||
Dockerfile
|
||||
**/*.orig
|
||||
*.orig
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -12,3 +12,12 @@ Gemfile.lock
|
||||
_yardoc
|
||||
doc/
|
||||
.wpscan/
|
||||
|
||||
.ash_history
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
.idea/
|
||||
|
||||
# Old files from v2
|
||||
cache/
|
||||
data/
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.5.0
|
||||
2.6.0
|
||||
|
||||
4
.simplecov
Normal file
4
.simplecov
Normal file
@@ -0,0 +1,4 @@
|
||||
SimpleCov.start do
|
||||
add_filter '/spec/'
|
||||
add_filter 'helper'
|
||||
end
|
||||
@@ -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
|
||||
|
||||
33
Dockerfile
33
Dockerfile
@@ -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
10
LICENSE
@@ -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 WPScan’s 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 WPScan’s 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
|
||||
|
||||
|
||||
95
README.md
95
README.md
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
[](https://badge.fury.io/rb/wpscan)
|
||||
[](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 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -15,7 +15,7 @@ module WPScan
|
||||
|
||||
target.multisite = true
|
||||
|
||||
WPScan::InterestingFinding.new(
|
||||
WPScan::Multisite.new(
|
||||
url,
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -11,7 +11,7 @@ module WPScan
|
||||
|
||||
url = target.url(path)
|
||||
|
||||
WPScan::InterestingFinding.new(
|
||||
WPScan::UploadDirectoryListing.new(
|
||||
url,
|
||||
confidence: 100,
|
||||
found_by: DIRECT_ACCESS,
|
||||
|
||||
@@ -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
|
||||
|
||||
31
app/finders/interesting_findings/wp_cron.rb
Normal file
31
app/finders/interesting_findings/wp_cron.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/')
|
||||
|
||||
34
app/finders/users/yoast_seo_author_sitemap.rb
Normal file
34
app/finders/users/yoast_seo_author_sitemap.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 -%>
|
||||
|
||||
@@ -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 "$@"
|
||||
|
||||
|
||||
@@ -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 "$@"
|
||||
|
||||
|
||||
@@ -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'
|
||||
#
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
9
lib/wpscan/errors.rb
Normal 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'
|
||||
@@ -1,6 +1,6 @@
|
||||
module WPScan
|
||||
# HTTP Error
|
||||
class HTTPError < StandardError
|
||||
class HTTPError < Error
|
||||
attr_reader :response
|
||||
|
||||
# @param [ Typhoeus::Response ] res
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
8
lib/wpscan/errors/xmlrpc.rb
Normal file
8
lib/wpscan/errors/xmlrpc.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Version
|
||||
module WPScan
|
||||
VERSION = '3.3.1'.freeze
|
||||
VERSION = '3.4.5'.freeze
|
||||
end
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Controller::Aliases do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe WPScan::Controller::CustomDirectories do
|
||||
subject(:controller) { described_class.new }
|
||||
let(:target_url) { 'http://ex.lo/' }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
30
spec/app/finders/interesting_findings/wp_cron_spec.rb
Normal file
30
spec/app/finders/interesting_findings/wp_cron_spec.rb
Normal 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
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user