Compare commits

...

222 Commits

Author SHA1 Message Date
erwanlr
5fd7e0ed22 Bumps version 2020-09-10 14:34:44 +02:00
erwanlr
d9f6c71015 Adds specs 2020-09-10 13:41:01 +02:00
Erwan
61a3106b3b Merge pull request #1540 from dwisiswant0/installer-log-regex-update
Update regex for WP Duplicator plugin installer-log
2020-09-10 13:22:15 +02:00
Dwi Siswanto
20eb2d825d 🔨 Update regex 2020-09-10 17:30:36 +07:00
Dwi Siswanto
906557d2ec 🔨 Update regex for WP Duplicator plugin installer-log 2020-09-10 04:38:19 +07:00
erwanlr
c1e278ea80 Updates rubocop-perf 2020-09-07 09:19:13 +02:00
erwanlr
e2d616a53f Adds DFs 2020-09-05 11:14:47 +02:00
erwanlr
c6802ccdd2 Updates rubocop 2020-09-03 11:05:36 +02:00
erwanlr
abd50fd037 Adds DFs 2020-08-29 12:02:20 +02:00
erwanlr
4515be53b4 Adds Dfs 2020-08-22 17:51:39 +02:00
Erwan
920a25bb25 Merge pull request #1536 from wpscanteam/dependabot/bundler/simplecov-tw-0.19.0
Update simplecov requirement from ~> 0.18.2 to ~> 0.19.0
2020-08-17 09:10:44 +02:00
dependabot-preview[bot]
648dd05069 Update simplecov requirement from ~> 0.18.2 to ~> 0.19.0
Updates the requirements on [simplecov](https://github.com/simplecov-ruby/simplecov) to permit the latest version.
- [Release notes](https://github.com/simplecov-ruby/simplecov/releases)
- [Changelog](https://github.com/simplecov-ruby/simplecov/blob/main/CHANGELOG.md)
- [Commits](https://github.com/simplecov-ruby/simplecov/compare/v0.18.2...v0.19.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-08-17 05:57:47 +00:00
erwanlr
713edcecca Adds DFs 2020-08-15 12:02:27 +02:00
erwanlr
ac16a951c5 Bumps version 2020-08-08 14:21:50 +02:00
erwanlr
1043bcb267 Adds Dfs 2020-08-08 13:25:15 +02:00
erwanlr
22979a1a77 Adds DFs 2020-08-07 09:39:50 +02:00
erwanlr
3039d2e7eb More rubocop fixes 2020-08-06 12:47:00 +02:00
erwanlr
557dee2d8c Updates rubocop 2020-08-06 11:43:26 +02:00
erwanlr
a506adcb64 Fixes #1529 2020-08-06 10:51:08 +02:00
erwanlr
3bfb120646 Adds DF for monarch - Ref #1527 2020-08-04 14:42:50 +02:00
erwanlr
43e613aa52 Updates Gravityforms detection - Ref #1526 2020-08-03 12:33:14 +02:00
erwanlr
0d930ed605 Adds Dfs 2020-08-01 10:45:42 +02:00
erwanlr
2014f1e4b3 Bumps version 2020-07-25 17:13:29 +02:00
erwanlr
4889d17e0a Ref #1510 2020-07-25 16:24:59 +02:00
erwanlr
494d31215d Ref #1510 2020-07-25 16:21:03 +02:00
erwanlr
582bdea431 Adds DFs 2020-07-25 11:51:59 +02:00
erwanlr
ecf7df9c01 Ref #1510 2020-07-24 15:32:41 +02:00
erwanlr
a9760e8817 Ref #1510 2020-07-24 15:26:22 +02:00
erwanlr
b32e990dd4 Bumps version 2020-07-20 15:11:31 +02:00
erwanlr
4320d2436f Fixes #1522 2020-07-20 13:15:54 +02:00
erwanlr
cba6e74b13 Fixes #1520 2020-07-18 13:35:53 +02:00
erwanlr
981bcf5fa2 Adds Dfs 2020-07-18 13:17:37 +02:00
erwanlr
1d79bc37d3 Bumps version 2020-07-17 10:50:15 +02:00
erwanlr
2fae3336ba Removes useless statement 2020-07-16 17:53:59 +02:00
erwanlr
cfb98c5139 Ref #1518 for XMLrPC multicall 2020-07-16 17:50:43 +02:00
erwanlr
b0260327c4 Updates XMLRPC MultiCall attack to reflect use changes. Ref #1518 2020-07-16 14:56:08 +02:00
erwanlr
f65532e347 Uses an enumerator to read wordlist during pwd attack. Fixes #1518 2020-07-16 14:39:09 +02:00
erwanlr
ff574b046c Checks if the wp-login.php is available before attacking it - Fixes #1519 2020-07-16 10:22:45 +02:00
erwanlr
97c995b64c Adds additional check just in case (related to Rubocop offences fixed) 2020-07-15 20:07:47 +02:00
erwanlr
8361ec97e4 Rubocop: Adds new cops by default, fixes offences 2020-07-15 16:45:36 +02:00
erwanlr
7a0bbc0acb Updates cms_scanner dep 2020-07-15 14:24:22 +02:00
Erwan
66f5eca841 Merge pull request #1517 from wpscanteam/dependabot/bundler/rubocop-tw-0.88.0
Update rubocop requirement from ~> 0.87.0 to ~> 0.88.0
2020-07-14 09:11:47 +02:00
dependabot-preview[bot]
b53e6d1888 Update rubocop requirement from ~> 0.87.0 to ~> 0.88.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/compare/v0.87.0...v0.88.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-14 05:38:06 +00:00
erwanlr
4b68fa8b60 Adds DFs 2020-07-11 10:19:52 +02:00
Erwan
54770c5a50 Merge pull request #1516 from wpscanteam/dependabot/bundler/rubocop-tw-0.87.0
Update rubocop requirement from ~> 0.86.0 to ~> 0.87.0
2020-07-07 09:34:54 +02:00
dependabot-preview[bot]
39fb2167f7 Update rubocop requirement from ~> 0.86.0 to ~> 0.87.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/compare/v0.86.0...v0.87.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-07 06:44:44 +00:00
Erwan
c33fef9c98 Merge pull request #1515 from wpscanteam/dependabot/bundler/rubocop-performance-tw-1.7.0
Update rubocop-performance requirement from ~> 1.6.0 to ~> 1.7.0
2020-07-07 08:43:34 +02:00
dependabot-preview[bot]
08a1117edf Update rubocop-performance requirement from ~> 1.6.0 to ~> 1.7.0
Updates the requirements on [rubocop-performance](https://github.com/rubocop-hq/rubocop-performance) to permit the latest version.
- [Release notes](https://github.com/rubocop-hq/rubocop-performance/releases)
- [Changelog](https://github.com/rubocop-hq/rubocop-performance/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop-hq/rubocop-performance/compare/v1.6.0...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-07-07 05:40:52 +00:00
erwanlr
e14cbed56e Adds DF 2020-07-04 11:27:49 +02:00
erwanlr
56e2ab16cc Adds DFs 2020-06-27 11:39:00 +02:00
erwanlr
d76d4b70f5 Updates rubocop & fixes offences 2020-06-23 20:30:48 +02:00
erwanlr
e223936a81 Adds Dfs 2020-06-20 12:00:20 +02:00
Christian Mehlmauer
60d067c421 fix docker install 2020-06-14 20:16:14 +02:00
Christian Mehlmauer
4102cf4688 fix #1487 2020-06-14 19:49:58 +02:00
erwanlr
dc977e6630 Adds missing spec files 2020-06-13 22:10:27 +02:00
erwanlr
05deabd775 Adds DF 2020-06-13 21:32:23 +02:00
erwanlr
549ab4aa15 Bumps version 2020-06-08 20:53:15 +02:00
erwanlr
b189c71682 Typo 2020-06-08 20:14:02 +02:00
erwanlr
b909856933 Fixes #1504 2020-06-08 20:13:18 +02:00
erwanlr
5de9084901 Updates deps 2020-06-08 19:59:04 +02:00
erwanlr
384ef0b44c Adds DFs 2020-06-06 10:48:34 +02:00
Erwan
9307772dc3 Merge pull request #1502 from wpscanteam/dependabot/bundler/rubocop-tw-0.85.0
Update rubocop requirement from ~> 0.84.0 to ~> 0.85.0
2020-06-02 08:26:24 +02:00
dependabot-preview[bot]
730c71d103 Update rubocop requirement from ~> 0.84.0 to ~> 0.85.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/compare/v0.84.0...v0.85.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-02 05:43:24 +00:00
erwanlr
5c710b96f5 Adds Dfs 2020-05-30 11:36:59 +02:00
erwanlr
fe63d0eadf Adds DFs 2020-05-29 10:05:59 +02:00
erwanlr
a6ca95159a Adds DFs 2020-05-23 11:13:57 +02:00
erwanlr
677d32fef5 Updates rubocop-performance and config 2020-05-23 09:17:41 +02:00
Erwan
14abd05969 Merge pull request #1498 from wpscanteam/dependabot/bundler/rubocop-tw-0.84.0
Update rubocop requirement from ~> 0.83.0 to ~> 0.84.0
2020-05-22 08:39:17 +02:00
dependabot-preview[bot]
2e680be34f Update rubocop requirement from ~> 0.83.0 to ~> 0.84.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/compare/v0.83.0...v0.84.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-22 05:32:38 +00:00
erwanlr
fe29942bf4 Adds Dfs 2020-05-16 15:30:47 +02:00
Erwan
c8fb717ac1 Updates Readme's Install section 2020-05-13 21:30:09 +02:00
Erwan
1ff7fcc913 Merge pull request #1493 from wpscanteam/dependabot/bundler/rubocop-tw-0.83.0
Update rubocop requirement from ~> 0.82.0 to ~> 0.83.0
2020-05-12 10:21:34 +02:00
Erwan
419c32702a Fixes rubocop offenses 2020-05-12 09:32:02 +02:00
dependabot-preview[bot]
9b63714caa Update rubocop requirement from ~> 0.82.0 to ~> 0.83.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/compare/v0.82.0...v0.83.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-12 05:35:24 +00:00
erwanlr
f034233607 Adds DFs 2020-05-09 11:46:23 +02:00
erwanlr
be6fcb51b6 Adds Dfs 2020-05-02 12:44:05 +02:00
Ryan Dewhurst
e49a682f00 Add user documentation URL to readme 2020-05-01 11:35:39 +02:00
Erwan
23ad3141a1 Fixes rubocop offence 2020-04-27 09:31:47 +02:00
Erwan
5347e374e0 Fixes rubocop last offence! 2020-04-27 09:26:26 +02:00
Erwan
1a49a628de Fixes rubocop offences 2020-04-27 09:22:59 +02:00
Erwan
8def256d7e Fixes potential InvalidProgressBar error in Pwd Attack 2020-04-27 09:15:37 +02:00
erwanlr
1cd8e6bad7 Adds Dfs 2020-04-25 17:43:29 +02:00
erwanlr
7a03c0db25 Adds DFs 2020-04-18 10:49:21 +02:00
Erwan
e7e3657d1f Merge pull request #1485 from wpscanteam/dependabot/bundler/rubocop-tw-0.82.0
Update rubocop requirement from ~> 0.81.0 to ~> 0.82.0
2020-04-17 10:03:28 +02:00
dependabot-preview[bot]
734dfcc9bc Update rubocop requirement from ~> 0.81.0 to ~> 0.82.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/compare/v0.81.0...v0.82.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-17 05:28:29 +00:00
erwanlr
b0db15099d Bumps version 2020-04-16 11:02:15 +02:00
erwanlr
6fbd2369ba Merge branch 'master' of github.com:wpscanteam/wpscan 2020-04-16 10:20:09 +02:00
erwanlr
f4a6674eed Better code related to CVSS 2020-04-16 10:06:28 +02:00
Ryan Dewhurst
c0567ad4f5 Change wording to security scanner 2020-04-16 09:37:15 +02:00
erwanlr
f146ee7e9f Adds YT references and CVSS to output 2020-04-15 17:02:44 +02:00
erwanlr
e606f4ce18 Adds YT references in output 2020-04-15 14:46:06 +02:00
Ryan Dewhurst
945b589a58 Update README.md
SEO tweak
2020-04-14 09:44:36 +02:00
Erwan
b18042c4a8 Reverts previous commit. spec folder already excluded via .dockerignore 2020-04-13 16:44:27 +02:00
Erwan
a9ff39104b Removes specs folder from Docker build 2020-04-13 16:21:17 +02:00
erwanlr
f6af6e5880 Bumps version 2020-04-12 19:02:57 +02:00
erwanlr
57c6c2d471 Removes support for Ruby 2.4 as EOL 2020-04-12 18:21:23 +02:00
erwanlr
c362527903 Adds DFs 2020-04-11 13:46:37 +02:00
erwanlr
a7acbd0738 Adds DFs 2020-04-04 12:23:40 +02:00
Erwan
f67192ebce Updates help messages regarding plugins-*-detection - Ref #1472 2020-04-02 12:37:09 +02:00
Erwan
c44fde83e4 Merge pull request #1473 from wpscanteam/dependabot/bundler/rubocop-tw-0.81.0
Update rubocop requirement from ~> 0.80.0 to ~> 0.81.0
2020-04-02 09:33:07 +02:00
dependabot-preview[bot]
50119285ef Update rubocop requirement from ~> 0.80.0 to ~> 0.81.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/compare/v0.80.0...v0.81.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-02 05:29:02 +00:00
Erwan
6216916fed Updates ruby version 2020-04-01 09:55:54 +02:00
Erwan
2952380200 Fixes typo 2020-03-30 20:47:12 +02:00
erwanlr
fb42b82e0d Adds DFs 2020-03-28 13:07:39 +01:00
Erwan
6d381ab88d Display valid credentials identified with the [!] icon - Ref #1470 2020-03-23 09:59:24 +01:00
erwanlr
c5c1de32bc Adds DFs 2020-03-21 16:56:16 +01:00
erwanlr
8077ad9bcd Adds DF - Ref #1469 2020-03-19 20:48:41 +01:00
erwanlr
6f22ba350f Adds DFs 2020-03-14 11:33:08 +01:00
erwanlr
f23d0c0157 Bumps version 2020-03-13 16:58:35 +01:00
erwanlr
a9a38edf24 Remove cache action from build 2020-03-13 16:06:33 +01:00
erwanlr
a5534f1e49 Merge branch 'master' of github.com:wpscanteam/wpscan 2020-03-13 16:04:11 +01:00
erwanlr
1c6469f384 Updates deps 2020-03-13 16:03:59 +01:00
erwanlr
8cfdbc1196 Adds DF 2020-03-13 16:03:21 +01:00
Erwan
88737ca6ea Fixes error with reference when debug log identified 2020-03-13 08:12:35 +01:00
Erwan
45bebc60bd Fixes typo in Readme 2020-03-12 22:25:23 +01:00
Erwan
4f7dec4635 Fixes detection of Users via WP JSON when blog uses basic auth 2020-03-12 13:12:40 +01:00
erwanlr
98739cce5a Adds DF 2020-03-10 20:45:08 +01:00
erwanlr
0bfbfacc27 Fixes #1465 2020-03-10 20:31:42 +01:00
erwanlr
73cd862e83 Bumps version 2020-03-09 20:02:44 +01:00
erwanlr
3305e9b74f Adds DFs 2020-03-09 19:24:43 +01:00
erwanlr
c37ec0e8d0 Updates deps 2020-03-09 18:11:47 +01:00
erwanlr
0b005477c1 Adds additional message when checksums mismatch during an update 2020-03-08 20:48:01 +01:00
erwanlr
a1467f8dac Merge branch 'dfs' 2020-03-07 14:56:23 +01:00
erwanlr
40d2c34347 Adds DFs 2020-03-07 14:56:10 +01:00
Erwan
528270e767 Updates Target#wordpress_from_meta_comments_or_scripts Xpath check 2020-03-06 16:17:40 +01:00
erwanlr
f4a04b2387 Adds DFs 2020-03-03 18:54:41 +01:00
erwanlr
14ed6ae109 Fixes #1461 2020-03-02 15:34:51 +01:00
erwanlr
4fd43694ae Typo 2020-03-02 11:57:37 +01:00
erwanlr
552d731e6a Updates Readme to reflect #1460 2020-03-02 11:56:42 +01:00
erwanlr
49ac3ef528 Supports loading api token from ENV, Fixes #1460 2020-03-02 11:45:50 +01:00
erwanlr
4379313f12 Bumps version 2020-02-29 14:24:53 +01:00
erwanlr
3901949f36 Updates deps 2020-02-29 13:27:52 +01:00
erwanlr
a3d8593fed Adds DFs 2020-02-29 12:28:55 +01:00
erwanlr
7c5baeb9c7 Adds DF 2020-02-26 10:55:06 +01:00
erwanlr
c692db5f85 Adds DFs 2020-02-24 13:29:13 +01:00
erwanlr
9130196ffc Runs rubocop before rspec 2020-02-19 14:38:31 +01:00
erwanlr
dad4a65118 Adds Coveralls GH Action 2020-02-19 14:16:31 +01:00
Erwan
4c34c2feb7 Merge pull request #1457 from wpscanteam/dependabot/bundler/rubocop-tw-0.80.0
Update rubocop requirement from ~> 0.79.0 to ~> 0.80.0
2020-02-19 12:32:39 +01:00
dependabot-preview[bot]
23522f7775 Update rubocop requirement from ~> 0.79.0 to ~> 0.80.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/compare/v0.79.0...v0.80.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-02-19 05:23:43 +00:00
Christian Mehlmauer
82c61398ba change name 2020-02-17 07:32:20 +01:00
Christian Mehlmauer
02871050a6 change step names 2020-02-17 06:34:39 +01:00
Christian Mehlmauer
7d3b1fea6b ruby-head not supported 2020-02-17 06:32:18 +01:00
Christian Mehlmauer
24917fa2a6 wrong key 2020-02-17 06:29:14 +01:00
Christian Mehlmauer
de3d8e4a23 Use a build matrix 2020-02-17 06:28:09 +01:00
erwanlr
1502845d65 Adds DFs 2020-02-15 13:20:54 +01:00
erwanlr
af3f10f74e Reduces time to detect the sub_dir when a lot of urls 2020-02-13 20:07:25 +00:00
erwanlr
c100372b31 Reduces time to detect non WP sites 2020-02-13 19:15:30 +00:00
erwanlr
72d699b39a Adds more improvements re passive scanning when there are a lot of urls 2020-02-13 15:36:42 +00:00
erwanlr
7d2b8a2a8b Reduces time to detect non WP sites when there are a lof of links in pages 2020-02-12 19:50:00 +00:00
erwanlr
8729c68e22 Removes useless params set 2020-02-11 16:39:01 +00:00
erwanlr
e2d48bedd9 Better fix for #1451, adds target IP address to output, ref #1088 2020-02-11 16:28:59 +00:00
erwanlr
6b241ce9b3 Avoids merging CLI params when calling VulnAPI - Ref #1451 2020-02-11 09:14:14 +00:00
erwanlr
1b68bdb36c Bumps version 2020-02-09 13:31:40 +00:00
erwanlr
fb82538441 Fixes #1451 2020-02-09 12:40:48 +00:00
erwanlr
2709d0869a Adds DFs 2020-02-08 10:28:19 +00:00
erwanlr
343f87bbe7 Adds DFs 2020-02-01 10:02:38 +00:00
erwanlr
ecbfc6004c Adds DFs 2020-01-25 12:28:16 +00:00
erwanlr
c57eecc81b Adds DF 2020-01-23 12:30:27 +00:00
erwanlr
7ea14dc03f Adds DF 2020-01-23 11:15:28 +00:00
erwanlr
4340d27258 Bumps version 2020-01-21 16:14:26 +00:00
erwanlr
e911be8f14 Removes ruby 2.7.0 from failure matrix 2020-01-21 16:12:38 +00:00
erwanlr
a4c650cdff Fixes incorrect RDF URLs detection 2020-01-21 15:06:29 +00:00
erwanlr
31a58f8a8f Fixes rare crash 2020-01-21 14:47:17 +00:00
erwanlr
ba4f15f111 Adds DFs 2020-01-18 11:20:43 +00:00
Erwan
206a913eb9 Merge pull request #1447 from wpscanteam/dependabot/bundler/webmock-tw-3.8.0
Update webmock requirement from ~> 3.7.0 to ~> 3.8.0
2020-01-16 10:17:09 +01:00
dependabot-preview[bot]
21ba490073 Update webmock requirement from ~> 3.7.0 to ~> 3.8.0
Updates the requirements on [webmock](https://github.com/bblimke/webmock) to permit the latest version.
- [Release notes](https://github.com/bblimke/webmock/releases)
- [Changelog](https://github.com/bblimke/webmock/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bblimke/webmock/compare/v3.7.0...v3.8.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-16 05:23:09 +00:00
erwanlr
2a29e2ed95 Adds DFs 2020-01-11 19:54:12 +00:00
Erwan
9517d14fd3 Fixes rubocop offence 2020-01-10 20:06:33 +01:00
Erwan
3deaa896df Fix rubocop typo 2020-01-10 19:28:01 +01:00
Erwan
c117007dc0 Merge pull request #1445 from wpscanteam/dependabot/bundler/rubocop-tw-0.79.0
Update rubocop requirement from ~> 0.78.0 to ~> 0.79.0
2020-01-07 08:53:56 +01:00
dependabot-preview[bot]
50baa238b9 Update rubocop requirement from ~> 0.78.0 to ~> 0.79.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/compare/v0.78.0...v0.79.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-07 05:21:29 +00:00
erwanlr
0e2d771660 Adds DFs 2020-01-04 12:17:51 +00:00
erwanlr
32b4670755 Bumps version 2020-01-02 15:28:33 +00:00
erwanlr
4a032d5e12 Merge branch 'ruby-2.7' 2020-01-02 14:53:46 +00:00
erwanlr
5887fede15 Updates deps 2020-01-02 13:53:28 +00:00
erwanlr
ad4eeb9f81 Fixes specs 2020-01-02 13:29:30 +00:00
erwanlr
a62c16d7cc Fixes Rubocop warning 2020-01-02 13:04:01 +00:00
erwanlr
e766e7392a Updates CMScanner dep 2020-01-02 12:52:03 +00:00
erwanlr
025c9c24ca Fixes GH Action 2020-01-02 12:51:23 +00:00
erwanlr
ab052add27 Allows Travis to fail on ruby-2.7 2019-12-28 13:14:19 +00:00
erwanlr
15cb99977b Fixes #1444 2019-12-28 13:10:57 +00:00
erwanlr
82d5af926f Adds Ruby 2.7.0 to Travis 2019-12-28 12:27:20 +00:00
erwanlr
76f73f3dc8 Adds DFs 2019-12-28 11:38:10 +00:00
erwanlr
575b22320e Merge branch 'df' 2019-12-21 10:49:32 +00:00
erwanlr
d20c07dc85 Adds DFs 2019-12-21 10:49:11 +00:00
erwanlr
f89071b87a Adds DFs, Updates rspec behaviour regarding --fail-fast 2019-12-20 15:47:56 +00:00
erwanlr
8b4e90f285 Updates deps + fixes travis 2019-12-19 13:56:06 +00:00
erwanlr
9c4f57c786 Adds DFs 2019-12-19 13:55:21 +00:00
erwanlr
902ec24b77 Adds DFs 2019-12-19 07:55:39 +00:00
erwanlr
7eba77fa63 Displays status code for KnownLocation finders 2019-12-15 09:00:30 +00:00
erwanlr
0753bbf7b3 Adds DF 2019-12-15 08:47:47 +00:00
erwanlr
6b2333614a Adds DFs 2019-12-14 12:14:37 +00:00
erwanlr
80b7f458f5 Fixes #1435 2019-12-08 10:34:18 +00:00
erwanlr
dbd8e59cf4 Typo 2019-12-07 15:46:31 +00:00
erwanlr
9948230ea0 Adds Dfs 2019-12-07 15:40:40 +00:00
Erwan
e2c858ac69 Merge pull request #1434 from crweiner/api-link
Fix links to API signup page
2019-12-03 13:37:55 +01:00
chandler
bac8b613e6 Fix links to API signup page 2019-12-02 17:03:36 -05:00
erwanlr
abbae15c6f Adds DFs 2019-11-30 12:47:32 +00:00
erwanlr
1548e8bfc1 Updates DFs 2019-11-28 20:19:25 +00:00
erwanlr
dc8cf3fc34 Disables a DF causing FP when checking the WP version 2019-11-28 17:15:05 +00:00
Erwan
c3cd815567 Merge pull request #1433 from wpscanteam/dependabot/bundler/rubocop-tw-0.77.0
Update rubocop requirement from ~> 0.76.0 to ~> 0.77.0
2019-11-28 08:50:50 +01:00
dependabot-preview[bot]
ce543b9384 Update rubocop requirement from ~> 0.76.0 to ~> 0.77.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/compare/v0.76.0...v0.77.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-11-28 05:21:09 +00:00
Christian Mehlmauer
9755c8cf42 Delete rubocop.yml 2019-11-26 23:33:52 +01:00
Christian Mehlmauer
434a210fb5 Update and rename rspec.yml to ruby.yml 2019-11-26 23:33:37 +01:00
Christian Mehlmauer
587602665a Update rubocop.yml 2019-11-26 23:29:30 +01:00
Christian Mehlmauer
bfec63df41 Update rspec.yml 2019-11-26 23:26:35 +01:00
Christian Mehlmauer
3b150df1af Update rspec.yml 2019-11-26 23:25:36 +01:00
Christian Mehlmauer
f24ecf0537 Update rubocop.yml 2019-11-26 23:23:13 +01:00
Christian Mehlmauer
9ddecbcc0a Update and rename ruby.yml to rspec.yml 2019-11-26 23:22:46 +01:00
Christian Mehlmauer
947bb8d3d5 Update ruby.yml 2019-11-26 23:18:33 +01:00
Christian Mehlmauer
30cbf87b35 Update gempush.yml 2019-11-26 23:14:53 +01:00
Christian Mehlmauer
69c3aab35a Update .dockerignore 2019-11-26 23:06:05 +01:00
Christian Mehlmauer
bdeb3547f1 Create rubocop.yml 2019-11-26 23:05:45 +01:00
Christian Mehlmauer
99e04b9669 Create ruby.yml 2019-11-26 23:04:03 +01:00
Christian Mehlmauer
680d2fb7eb add gem push 2019-11-26 23:03:23 +01:00
erwanlr
8814eda018 Adds DFs 2019-11-23 11:45:15 +00:00
erwanlr
7e72ba2885 Adds DFs 2019-11-16 11:18:23 +00:00
Christian Mehlmauer
b4d7a8490b change twitter handle 2019-11-13 08:23:18 +01:00
erwanlr
e9a5bc66df Bumps version 2019-11-11 12:41:29 +00:00
erwanlr
edebc77726 Fixes 1426 2019-11-09 17:55:31 +00:00
erwanlr
271dee824d Adds DFs 2019-11-09 10:59:46 +00:00
erwanlr
1e868d10ca Ignores slow specs when executing rake tasks 2019-11-05 20:11:03 +00:00
670 changed files with 300507 additions and 5315 deletions

View File

@@ -14,3 +14,4 @@ Dockerfile
*.orig
bin/wpscan-*
.wpscan/
.github/

41
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
ruby: [2.5, 2.6, 2.7]
steps:
- name: Checkout code
uses: actions/checkout@v1
- name: Set up Ruby ${{ matrix.ruby }}
uses: actions/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- name: Install GEMs
run: |
gem install bundler
bundle config force_ruby_platform true
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: rubocop
run: |
bundle exec rubocop
- name: rspec
run: |
bundle exec rspec
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

40
.github/workflows/gempush.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Ruby Gem
on:
release:
types: [published]
jobs:
build:
name: Build + Publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Set up Ruby 2.6
uses: actions/setup-ruby@v1
with:
ruby-version: 2.6.x
#- name: Publish to GPR
# run: |
# mkdir -p $HOME/.gem
# touch $HOME/.gem/credentials
# chmod 0600 $HOME/.gem/credentials
# printf -- "---\n:github: Bearer ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
# gem build *.gemspec
# gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
# env:
# GEM_HOST_API_KEY: ${{secrets.GITHUB_TOKEN}}
# OWNER: wpscanteam
- name: Publish to RubyGems
run: |
mkdir -p $HOME/.gem
touch $HOME/.gem/credentials
chmod 0600 $HOME/.gem/credentials
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
gem build *.gemspec
gem push *.gem
env:
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}

5
.rspec
View File

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

View File

@@ -1,9 +1,14 @@
require: rubocop-performance
AllCops:
TargetRubyVersion: 2.4
NewCops: enable
TargetRubyVersion: 2.5
Exclude:
- '*.gemspec'
- 'vendor/**/*'
Layout/LineLength:
Max: 120
Lint/MissingSuper:
Enabled: false
Lint/UriEscapeUnescape:
Enabled: false
Metrics/AbcSize:
@@ -16,13 +21,13 @@ Metrics/ClassLength:
Exclude:
- 'app/controllers/enumeration/cli_options.rb'
Metrics/CyclomaticComplexity:
Max: 8
Metrics/LineLength:
Max: 120
Max: 10
Metrics/MethodLength:
Max: 20
Exclude:
- 'app/controllers/enumeration/cli_options.rb'
Metrics/PerceivedComplexity:
Max: 11
Style/ClassVars:
Enabled: false
Style/Documentation:

View File

@@ -1 +1 @@
2.6.2
2.7.1

View File

@@ -1,4 +1,19 @@
# frozen_string_literal: true
if ENV['GITHUB_ACTION']
require 'simplecov-lcov'
SimpleCov::Formatter::LcovFormatter.config do |c|
c.single_report_path = 'coverage/lcov.info'
c.report_with_single_file = true
end
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
end
SimpleCov.start do
enable_coverage :branch # Only supported for Ruby >= 2.5
add_filter '/spec/'
add_filter 'helper'
end
end

View File

@@ -1,16 +0,0 @@
language: ruby
sudo: false
cache: bundler
rvm:
- 2.4.9
- 2.5.7
- 2.6.5
before_install:
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
- gem update --system
script:
- bundle exec rubocop
- bundle exec rspec
notifications:
email:
- team@wpscan.org

View File

@@ -1,16 +1,16 @@
FROM ruby:2.6.3-alpine AS builder
FROM ruby:2.7.1-alpine AS builder
LABEL maintainer="WPScan Team <team@wpscan.org>"
ARG BUNDLER_ARGS="--jobs=8 --without test development"
RUN echo "gem: --no-ri --no-rdoc" > /etc/gemrc
RUN echo "install: --no-document --no-post-install-message\nupdate: --no-document --no-post-install-message" > /etc/gemrc
COPY . /wpscan
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
bundle config force_ruby_platform true && \
bundle config disable_version_check 'true' && \
bundle config without "test development" && \
bundle config path.system 'true' && \
bundle install --gemfile=/wpscan/Gemfile --jobs=8
WORKDIR /wpscan
RUN rake install --trace
@@ -19,7 +19,7 @@ RUN rake install --trace
RUN chmod -R a+r /usr/local/bundle
FROM ruby:2.6.3-alpine
FROM ruby:2.7.1-alpine
LABEL maintainer="WPScan Team <team@wpscan.org>"
RUN adduser -h /wpscan -g WPScan -D wpscan

View File

@@ -7,15 +7,15 @@
<h3 align="center">WPScan</h3>
<p align="center">
WordPress Vulnerability Scanner
WordPress Security Scanner
<br>
<br>
<a href="https://wpscan.org/" title="homepage" target="_blank">Homepage</a> - <a href="https://wpscan.io/" title="wpscan.io" target="_blank">WPScan.io</a> - <a href="https://wpvulndb.com/" title="vulnerability database" target="_blank">Vulnerability Database</a> - <a href="https://wordpress.org/plugins/wpscan/" title="wordpress plugin" target="_blank">WordPress Plugin</a>
<a href="https://wpscan.org/" title="homepage" target="_blank">Homepage</a> - <a href="https://wpscan.io/" title="wpscan.io" target="_blank">WPScan.io</a> - <a href="https://wpvulndb.com/" title="vulnerability database" target="_blank">Vulnerability Database</a> - <a href="https://wordpress.org/plugins/wpscan/" title="wordpress security plugin" target="_blank">WordPress Security Plugin</a>
</p>
<p align="center">
<a href="https://badge.fury.io/rb/wpscan" target="_blank"><img src="https://badge.fury.io/rb/wpscan.svg"></a>
<a href="https://travis-ci.org/wpscanteam/wpscan" target="_blank"><img src="https://travis-ci.org/wpscanteam/wpscan.svg?branch=master"></a>
<a href="https://github.com/wpscanteam/wpscan/actions?query=workflow%3ABuild" target="_blank"><img src="https://github.com/wpscanteam/wpscan/workflows/Build/badge.svg"></a>
<a href="https://codeclimate.com/github/wpscanteam/wpscan" target="_blank"><img src="https://codeclimate.com/github/wpscanteam/wpscan/badges/gpa.svg"></a>
</p>
@@ -31,7 +31,11 @@
- RubyGems - Recommended: latest
- Nokogiri might require packages to be installed via your package manager depending on your OS, see https://nokogiri.org/tutorials/installing_nokogiri.html
### From RubyGems (Recommended)
### In a Pentesting distribution
When using a pentesting distubution (such as Kali Linux), it is recommended to install/update wpscan via the package manager if available.
### From RubyGems
```shell
gem install wpscan
@@ -39,18 +43,6 @@ gem install wpscan
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/
bundle install && rake install
```
# Updating
You can update the local database by using ```wpscan --update```
@@ -77,6 +69,8 @@ docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u1-1
# Usage
Full user documentation can be found here; https://github.com/wpscanteam/wpscan/wiki/WPScan-User-Documentation
```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.
@@ -130,6 +124,11 @@ cli_options:
api_token: YOUR_API_TOKEN
```
## Load API Token From ENV (since v3.7.10)
The API Token will be automatically loaded from the ENV variable `WPSCAN_API_TOKEN` if present. If the `--api-token` CLI option is also provided, the value from the CLI will be used.
## Enumerating usernames
```shell

View File

@@ -6,14 +6,18 @@ exec = []
begin
require 'rubocop/rake_task'
RuboCop::RakeTask.new
exec << :rubocop
rescue LoadError
end
begin
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
RSpec::Core::RakeTask.new(:spec) { |t| t.rspec_opts = %w{--tag ~slow} }
exec << :spec
rescue LoadError
end

View File

@@ -18,9 +18,7 @@ module WPScan
target.content_dir = ParsedCli.wp_content_dir if ParsedCli.wp_content_dir
target.plugins_dir = ParsedCli.wp_plugins_dir if ParsedCli.wp_plugins_dir
return if target.content_dir
raise Error::WpContentDirNotDetected
raise Error::WpContentDirNotDetected unless target.content_dir
end
end
end

View File

@@ -51,7 +51,7 @@ module WPScan
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.'],
'Use the supplied mode to enumerate Plugins.'],
choices: %w[mixed passive aggressive], normalize: :to_sym, default: :passive
),
OptBoolean.new(
@@ -62,8 +62,7 @@ module WPScan
),
OptChoice.new(
['--plugins-version-detection MODE',
'Use the supplied mode to check plugins versions instead of the --detection-mode ' \
'or --plugins-detection modes.'],
'Use the supplied mode to check plugins\' versions.'],
choices: %w[mixed passive aggressive], normalize: :to_sym, default: :mixed
),
OptInteger.new(

View File

@@ -23,27 +23,32 @@ module WPScan
]
end
def run
return unless ParsedCli.passwords
if user_interaction?
output('@info',
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
end
attack_opts = {
def attack_opts
@attack_opts ||= {
show_progression: user_interaction?,
multicall_max_passwords: ParsedCli.multicall_max_passwords
}
end
def run
return unless ParsedCli.passwords
begin
found = []
attacker.attack(users, passwords(ParsedCli.passwords), attack_opts) do |user|
if user_interaction?
output('@info',
msg: "Performing password attack on #{attacker.titleize} against #{users.size} user/s")
end
attacker.attack(users, ParsedCli.passwords, attack_opts) do |user|
found << user
attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}")
end
rescue Error::NoLoginInterfaceDetected => e
# TODO: Maybe output that in JSON as well.
output('@notice', msg: e.to_s) if user_interaction?
ensure
output('users', users: found)
end
@@ -65,6 +70,8 @@ module WPScan
case ParsedCli.password_attack
when :wp_login
raise Error::NoLoginInterfaceDetected unless target.login_url
Finders::Passwords::WpLogin.new(target)
when :xmlrpc
raise Error::XMLRPCNotDetected unless xmlrpc
@@ -81,8 +88,8 @@ module WPScan
def xmlrpc_get_users_blogs_enabled?
if xmlrpc&.enabled? &&
xmlrpc.available_methods.include?('wp.getUsersBlogs') &&
xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]])
.run.body !~ /XML\-RPC services are disabled/
!xmlrpc.method_call('wp.getUsersBlogs', [SecureRandom.hex[0, 6], SecureRandom.hex[0, 4]])
.run.body.match?(/>\s*405\s*</)
true
else
@@ -100,8 +107,10 @@ module WPScan
else
Finders::Passwords::XMLRPC.new(xmlrpc)
end
else
elsif target.login_url
Finders::Passwords::WpLogin.new(target)
else
raise Error::NoLoginInterfaceDetected
end
end
@@ -113,15 +122,6 @@ module WPScan
acc << Model::User.new(elem.chomp)
end
end
# @param [ String ] wordlist_path
#
# @return [ Array<String> ]
def passwords(wordlist_path)
@passwords ||= File.open(wordlist_path).reduce([]) do |acc, elem|
acc << elem.chomp
end
end
end
end
end

View File

@@ -4,6 +4,8 @@ module WPScan
module Controller
# Controller to handle the API token
class VulnApi < CMSScanner::Controller::Base
ENV_KEY = 'WPSCAN_API_TOKEN'
def cli_options
[
OptString.new(['--api-token TOKEN', 'The WPVulnDB API Token to display vulnerability data'])
@@ -11,9 +13,9 @@ module WPScan
end
def before_scan
return unless ParsedCli.api_token
return unless ParsedCli.api_token || ENV.key?(ENV_KEY)
DB::VulnApi.token = ParsedCli.api_token
DB::VulnApi.token = ParsedCli.api_token || ENV[ENV_KEY]
api_status = DB::VulnApi.status

View File

@@ -4,7 +4,6 @@ module WPScan
module Finders
module DbExports
# DB Exports finder
# See https://github.com/wpscanteam/wpscan-v3/issues/62
class KnownLocations < CMSScanner::Finders::Finder
include CMSScanner::Finders::Finder::Enumerator
@@ -41,7 +40,7 @@ module WPScan
# @return [ Hash ]
def potential_urls(opts = {})
urls = {}
domain_name = target.uri.host[/(^[\w|-]+)/, 1]
domain_name = (PublicSuffix.domain(target.uri.host) || target.uri.host)[/(^[\w|-]+)/, 1]
File.open(opts[:list]).each_with_index do |path, index|
path.gsub!('{domain_name}', domain_name)

View File

@@ -16,8 +16,7 @@ module WPScan
target.url(path),
confidence: 70,
found_by: DIRECT_ACCESS,
interesting_entries: target.directory_listing_entries(path),
references: { url: 'https://github.com/wpscanteam/wpscan/issues/422' }
interesting_entries: target.directory_listing_entries(path)
)
end
end

View File

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

View File

@@ -9,14 +9,9 @@ module WPScan
def aggressive(_opts = {})
path = 'installer-log.txt'
return unless /DUPLICATOR INSTALL-LOG/.match?(target.head_and_get(path).body)
return unless /DUPLICATOR(-|\s)?(PRO|LITE)?:? INSTALL-LOG/i.match?(target.head_and_get(path).body)
Model::DuplicatorInstallerLog.new(
target.url(path),
confidence: 100,
found_by: DIRECT_ACCESS,
references: { url: 'https://www.exploit-db.com/ghdb/3981/' }
)
Model::DuplicatorInstallerLog.new(target.url(path), confidence: 100, found_by: DIRECT_ACCESS)
end
end
end

View File

@@ -15,10 +15,7 @@ module WPScan
Model::EmergencyPwdResetScript.new(
target.url(path),
confidence: /password/i.match?(res.body) ? 100 : 40,
found_by: DIRECT_ACCESS,
references: {
url: 'https://codex.wordpress.org/Resetting_Your_Password#Using_the_Emergency_Password_Reset_Script'
}
found_by: DIRECT_ACCESS
)
end
end

View File

@@ -16,8 +16,7 @@ module WPScan
target.url(path),
confidence: 100,
found_by: DIRECT_ACCESS,
interesting_entries: fpd_entries,
references: { url: 'https://www.owasp.org/index.php/Full_Path_Disclosure' }
interesting_entries: fpd_entries
)
end
end

View File

@@ -7,22 +7,16 @@ module WPScan
class MuPlugins < CMSScanner::Finders::Finder
# @return [ InterestingFinding ]
def passive(_opts = {})
pattern = %r{#{target.content_dir}/mu\-plugins/}i
pattern = %r{#{target.content_dir}/mu-plugins/}i
target.in_scope_uris(target.homepage_res) do |uri|
target.in_scope_uris(target.homepage_res, '(//@href|//@src)[contains(., "mu-plugins")]') do |uri|
next unless uri.path&.match?(pattern)
url = target.url('wp-content/mu-plugins/')
target.mu_plugins = true
return Model::MuPlugins.new(
url,
confidence: 70,
found_by: 'URLs In Homepage (Passive Detection)',
to_s: "This site has 'Must Use Plugins': #{url}",
references: { url: 'http://codex.wordpress.org/Must_Use_Plugins' }
)
return Model::MuPlugins.new(url, confidence: 70, found_by: 'URLs In Homepage (Passive Detection)')
end
nil
end
@@ -37,13 +31,7 @@ module WPScan
target.mu_plugins = true
Model::MuPlugins.new(
url,
confidence: 80,
found_by: DIRECT_ACCESS,
to_s: "This site has 'Must Use Plugins': #{url}",
references: { url: 'http://codex.wordpress.org/Must_Use_Plugins' }
)
Model::MuPlugins.new(url, confidence: 80, found_by: DIRECT_ACCESS)
end
end
end

View File

@@ -12,18 +12,12 @@ module WPScan
location = res.headers_hash['location']
return unless [200, 302].include?(res.code)
return if res.code == 302 && location =~ /wp-login\.php\?action=register/
return unless res.code == 200 || res.code == 302 && location =~ /wp-signup\.php/
return if res.code == 302 && location&.include?('wp-login.php?action=register')
return unless res.code == 200 || res.code == 302 && location&.include?('wp-signup.php')
target.multisite = true
Model::Multisite.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,
to_s: 'This site seems to be a multisite',
references: { url: 'http://codex.wordpress.org/Glossary#Multisite' }
)
Model::Multisite.new(url, confidence: 100, found_by: DIRECT_ACCESS)
end
end
end

View File

@@ -20,12 +20,7 @@ module WPScan
target.registration_enabled = true
Model::Registration.new(
res.effective_url,
confidence: 100,
found_by: DIRECT_ACCESS,
to_s: "Registration is enabled: #{res.effective_url}"
)
Model::Registration.new(res.effective_url, confidence: 100, found_by: DIRECT_ACCESS)
end
end
end

View File

@@ -13,12 +13,7 @@ module WPScan
return unless res.code == 200 && res.headers['Content-Type'] =~ %r{\Aapplication/zip}i
Model::TmmDbMigrate.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,
references: { packetstorm: 131_957 }
)
Model::TmmDbMigrate.new(url, confidence: 100, found_by: DIRECT_ACCESS)
end
end
end

View File

@@ -13,12 +13,7 @@ module WPScan
url = target.url(path)
Model::UploadDirectoryListing.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,
to_s: "Upload directory has listing enabled: #{url}"
)
Model::UploadDirectoryListing.new(url, confidence: 100, found_by: DIRECT_ACCESS)
end
end
end

View File

@@ -14,11 +14,7 @@ module WPScan
return unless SQL_PATTERN.match?(res.body)
Model::UploadSQLDump.new(
target.url(path),
confidence: 100,
found_by: DIRECT_ACCESS
)
Model::UploadSQLDump.new(target.url(path), confidence: 100, found_by: DIRECT_ACCESS)
end
end
end

View File

@@ -11,17 +11,7 @@ module WPScan
return unless res.code == 200
Model::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'
]
}
)
Model::WPCron.new(wp_cron_url, confidence: 60, found_by: DIRECT_ACCESS)
end
def wp_cron_url

View File

@@ -20,8 +20,8 @@ module WPScan
end
def passive_from_css_href(res, opts)
target.in_scope_uris(res, '//style/@src|//link/@href') do |uri|
next unless uri.path =~ %r{/themes/([^\/]+)/style.css\z}i
target.in_scope_uris(res, '//link/@href[contains(., "style.css")]') do |uri|
next unless uri.path =~ %r{/themes/([^/]+)/style.css\z}i
return create_theme(Regexp.last_match[1], uri.to_s, opts)
end
@@ -33,7 +33,7 @@ module WPScan
code = tag.text.to_s
next if code.empty?
next unless code =~ %r{#{item_code_pattern('themes')}\\?/style\.css[^"'\( ]*}i
next unless code =~ %r{#{item_code_pattern('themes')}\\?/style\.css[^"'( ]*}i
return create_theme(Regexp.last_match[1], Regexp.last_match[0].strip, opts)
end

View File

@@ -13,7 +13,7 @@ module WPScan
def passive(opts = {})
found = []
slugs = items_from_links('themes', false) + items_from_codes('themes', false)
slugs = items_from_links('themes', uniq: false) + items_from_codes('themes', uniq: false)
slugs.each_with_object(Hash.new(0)) { |slug, counts| counts[slug] += 1 }.each do |slug, occurences|
found << Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 2 * occurences))

View File

@@ -13,7 +13,7 @@ module WPScan
def valid_credentials?(response)
response.code == 302 &&
[*response.headers['Set-Cookie']]&.any? { |cookie| cookie =~ /wordpress_logged_in_/i }
Array(response.headers['Set-Cookie'])&.any? { |cookie| cookie =~ /wordpress_logged_in_/i }
end
def errored_response?(response)

View File

@@ -12,11 +12,11 @@ module WPScan
end
def valid_credentials?(response)
response.code == 200 && response.body =~ /blogName/
response.code == 200 && response.body.include?('blogName')
end
def errored_response?(response)
response.code != 200 && response.body !~ /login_error/i
response.code != 200 && response.body !~ /Incorrect username or password/i
end
end
end

View File

@@ -22,8 +22,30 @@ module WPScan
target.multi_call(methods, cache_ttl: 0).run
end
# @param [ IO ] file
# @param [ Integer ] passwords_size
# @return [ Array<String> ] The passwords from the last checked position in the file until there are
# passwords_size passwords retrieved
def passwords_from_wordlist(file, passwords_size)
pwds = []
added_pwds = 0
return pwds if passwords_size.zero?
# Make sure that the main code does not call #sysseek or #count etc
# otherwise the file descriptor will be set to somwehere else
file.each_line(chomp: true) do |line|
pwds << line
added_pwds += 1
break if added_pwds == passwords_size
end
pwds
end
# @param [ Array<Model::User> ] users
# @param [ Array<String> ] passwords
# @param [ String ] wordlist_path
# @param [ Hash ] opts
# @option opts [ Boolean ] :show_progression
# @option opts [ Integer ] :multicall_max_passwords
@@ -33,18 +55,22 @@ module WPScan
# TODO: Make rubocop happy about metrics etc
#
# rubocop:disable all
def attack(users, passwords, opts = {})
wordlist_index = 0
def attack(users, wordlist_path, opts = {})
checked_passwords = 0
wordlist = File.open(wordlist_path)
wordlist_size = wordlist.count
max_passwords = opts[:multicall_max_passwords]
current_passwords_size = passwords_size(max_passwords, users.size)
create_progress_bar(total: (passwords.size / current_passwords_size.round(1)).ceil,
create_progress_bar(total: (wordlist_size / current_passwords_size.round(1)).ceil,
show_progression: opts[:show_progression])
wordlist.sysseek(0) # reset the descriptor to the beginning of the file as it changed with #count
loop do
current_users = users.select { |user| user.password.nil? }
current_passwords = passwords[wordlist_index, current_passwords_size]
wordlist_index += current_passwords_size
current_users = users.select { |user| user.password.nil? }
current_passwords = passwords_from_wordlist(wordlist, current_passwords_size)
checked_passwords += current_passwords_size
break if current_users.empty? || current_passwords.nil? || current_passwords.empty?
@@ -76,16 +102,19 @@ module WPScan
break
end
progress_bar.total = progress_bar.progress + ((passwords.size - wordlist_index) / current_passwords_size.round(1)).ceil
begin
progress_bar.total = progress_bar.progress + ((wordlist_size - checked_passwords) / current_passwords_size.round(1)).ceil
rescue ProgressBar::InvalidProgressError
end
end
end
# Maybe a progress_bar.stop ?
end
# rubocop:disable all
# rubocop:enable all
def passwords_size(max_passwords, users_size)
return 1 if max_passwords < users_size
return 0 if users_size == 0
return 0 if users_size.zero?
max_passwords / users_size
end
@@ -94,9 +123,13 @@ module WPScan
def check_and_output_errors(res)
progress_bar.log("Incorrect response: #{res.code} / #{res.return_message}") unless res.code == 200
progress_bar.log('Parsing error, might be caused by a too high --max-passwords value (such as >= 2k)') if res.body =~ /parse error. not well formed/i
if /parse error. not well formed/i.match?(res.body)
progress_bar.log('Parsing error, might be caused by a too high --max-passwords value (such as >= 2k)')
end
progress_bar.log('The requested method is not supported') if res.body =~ /requested method [^ ]+ does not exist/i
return unless /requested method [^ ]+ does not exist/i.match?(res.body)
progress_bar.log('The requested method is not supported')
end
end
end

View File

@@ -48,7 +48,7 @@ module WPScan
#
# @return [ String, nil ] The version number detected from the stable tag
def from_stable_tag(body)
return unless body =~ /\b(?:stable tag|version):\s*(?!trunk)([0-9a-z\.-]+)/i
return unless body =~ /\b(?:stable tag|version):\s*(?!trunk)([0-9a-z.-]+)/i
number = Regexp.last_match[1]
@@ -59,7 +59,7 @@ module WPScan
#
# @return [ String, nil ] The best version number detected from the changelog section
def from_changelog_section(body)
extracted_versions = body.scan(%r{[=]+\s+(?:v(?:ersion)?\s*)?([0-9\.-]+)[ \ta-z0-9\(\)\.\-\/]*[=]+}i)
extracted_versions = body.scan(%r{=+\s+(?:v(?:ersion)?\s*)?([0-9.-]+)[ \ta-z0-9().\-/]*=+}i)
return if extracted_versions.nil? || extracted_versions.empty?
@@ -68,11 +68,9 @@ module WPScan
extracted_versions = extracted_versions.select { |x| x =~ /[0-9]+/ }
sorted = extracted_versions.sort do |x, y|
begin
Gem::Version.new(x) <=> Gem::Version.new(y)
rescue StandardError
0
end
Gem::Version.new(x) <=> Gem::Version.new(y)
rescue StandardError
0
end
sorted.last

View File

@@ -19,8 +19,12 @@ module WPScan
def aggressive(opts = {})
found = []
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |_res, slug|
found << Model::Plugin.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, slug|
finding_opts = opts.merge(found_by: found_by,
confidence: 80,
interesting_entries: ["#{res.effective_url}, status: #{res.code}"])
found << Model::Plugin.new(slug, target, finding_opts)
raise Error::PluginsThresholdReached if opts[:threshold].positive? && found.size >= opts[:threshold]
end

View File

@@ -30,7 +30,7 @@ module WPScan
# @return [ Version ]
def style_version
return unless Browser.get(target.style_url).body =~ /Version:[\t ]*(?!trunk)([0-9a-z\.-]+)/i
return unless Browser.get(target.style_url).body =~ /Version:[\t ]*(?!trunk)([0-9a-z.-]+)/i
Model::Version.new(
Regexp.last_match[1],

View File

@@ -19,8 +19,12 @@ module WPScan
def aggressive(opts = {})
found = []
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |_res, slug|
found << Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, slug|
finding_opts = opts.merge(found_by: found_by,
confidence: 80,
interesting_entries: ["#{res.effective_url}, status: #{res.code}"])
found << Model::Theme.new(slug, target, finding_opts)
raise Error::ThemesThresholdReached if opts[:threshold].positive? && found.size >= opts[:threshold]
end

View File

@@ -6,7 +6,8 @@ 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'
require_relative 'users/author_sitemap'
require_relative 'users/yoast_seo_author_sitemap'
module WPScan
module Finders
@@ -22,6 +23,7 @@ module WPScan
Users::WpJsonApi.new(target) <<
Users::OembedApi.new(target) <<
Users::RSSGenerator.new(target) <<
Users::AuthorSitemap.new(target) <<
Users::YoastSeoAuthorSitemap.new(target) <<
Users::AuthorIdBruteForcing.new(target) <<
Users::LoginErrorMessages.new(target)

View File

@@ -71,11 +71,13 @@ module WPScan
return username, 'Display Name', 50 if username
end
# @param [ String ] url
# @param [ String, Addressable::URI ] uri
#
# @return [ String, nil ]
def username_from_author_url(url)
url[%r{/author/([^/\b]+)/?}i, 1]
def username_from_author_url(uri)
uri = Addressable::URI.parse(uri) unless uri.is_a?(Addressable::URI)
uri.path[%r{/author/([^/\b]+)/?}i, 1]
end
# @param [ Typhoeus::Response ] res
@@ -83,12 +85,12 @@ module WPScan
# @return [ String, nil ] The username found
def username_from_response(res)
# Permalink enabled
target.in_scope_uris(res, '//link/@href|//a/@href') do |uri|
username = username_from_author_url(uri.to_s)
target.in_scope_uris(res, '//@href[contains(., "author/")]') do |uri|
username = username_from_author_url(uri)
return username if username
end
# No permalink
# No permalink, TODO Maybe use xpath to extract the classes ?
res.body[/<body class="archive author author-([^\s]+)[ "]/i, 1]
end

View File

@@ -45,7 +45,7 @@ module WPScan
def potential_usernames(res)
usernames = []
target.in_scope_uris(res, '//a/@href') do |uri, node|
target.in_scope_uris(res, '//a/@href[contains(., "author")]') do |uri, node|
if uri.path =~ %r{/author/([^/\b]+)/?\z}i
usernames << [Regexp.last_match[1], 'Author Pattern', 100]
elsif /author=[0-9]+/.match?(uri.query)

View File

@@ -0,0 +1,36 @@
# frozen_string_literal: true
module WPScan
module Finders
module Users
# Since WP 5.5, /wp-sitemap-users-1.xml is generated and contains
# the usernames of accounts who made a post
class AuthorSitemap < 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 << Model::User.new(username,
found_by: found_by,
confidence: 100,
interesting_entries: [sitemap_url])
end
found
end
# @return [ String ] The URL of the sitemap
def sitemap_url
@sitemap_url ||= target.url('wp-sitemap-users-1.xml')
end
end
end
end
end

View File

@@ -37,7 +37,7 @@ module WPScan
# usernames from the potential Users found
unames = opts[:found].map(&:username)
[*opts[:list]].each { |uname| unames << uname.chomp }
Array(opts[:list]).each { |uname| unames << uname.chomp }
unames.uniq
end

View File

@@ -13,7 +13,7 @@ module WPScan
urls.each do |url|
res = Browser.get_and_follow_location(url)
next unless res.code == 200 && res.body =~ /<dc\:creator>/i
next unless res.code == 200 && res.body =~ /<dc:creator>/i
potential_usernames = []

View File

@@ -21,7 +21,7 @@ module WPScan
loop do
current_page += 1
res = Typhoeus.get(api_url, params: { per_page: MAX_PER_PAGE, page: current_page })
res = Browser.get(api_url, params: { per_page: MAX_PER_PAGE, page: current_page })
total_pages ||= res.headers['X-WP-TotalPages'].to_i

View File

@@ -5,27 +5,7 @@ module WPScan
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 << Model::User.new(username,
found_by: found_by,
confidence: 100,
interesting_entries: [sitemap_url])
end
found
end
class YoastSeoAuthorSitemap < AuthorSitemap
# @return [ String ] The URL of the author-sitemap
def sitemap_url
@sitemap_url ||= target.url('author-sitemap.xml')

View File

@@ -8,11 +8,15 @@ module WPScan
# @param [ String ] type plugins / themes
# @param [ Boolean ] uniq Wether or not to apply the #uniq on the results
#
# @return [Array<String> ] The plugins/themes detected in the href, src attributes of the homepage
def items_from_links(type, uniq = true)
# @return [ Array<String> ] The plugins/themes detected in the href, src attributes of the page
def items_from_links(type, uniq: true)
found = []
xpath = format(
'(//@href|//@src|//@data-src)[contains(., "%s")]',
type == 'plugins' ? target.plugins_dir : target.content_dir
)
target.in_scope_uris(page_res) do |uri|
target.in_scope_uris(page_res, xpath) do |uri|
next unless uri.to_s =~ item_attribute_pattern(type)
slug = Regexp.last_match[1]&.strip
@@ -27,7 +31,7 @@ module WPScan
# @param [ Boolean ] uniq Wether or not to apply the #uniq on the results
#
# @return [Array<String> ] The plugins/themes detected in the javascript/style of the homepage
def items_from_codes(type, uniq = true)
def items_from_codes(type, uniq: true)
found = []
page_res.html.xpath('//script[not(@src)]|//style[not(@src)]').each do |tag|
@@ -51,7 +55,7 @@ module WPScan
#
# @return [ Regexp ]
def item_code_pattern(type)
@item_code_pattern ||= %r{["'\( ]#{item_url_pattern(type)}([^\\\/\)"']+)}i
@item_code_pattern ||= %r{["'( ]#{item_url_pattern(type)}([^\\/)"']+)}i
end
# @param [ String ] type
@@ -62,9 +66,9 @@ module WPScan
item_url = type == 'plugins' ? target.plugins_url : target.content_url
url = /#{item_url.gsub(/\A(?:https?)/i, 'https?').gsub('/', '\\\\\?\/')}/i
item_dir = %r{(?:#{url}|\\?\/#{item_dir.gsub('/', '\\\\\?\/')}\\?/)}i
item_dir = %r{(?:#{url}|\\?/#{item_dir.gsub('/', '\\\\\?\/')}\\?/)}i
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}\\?\/}i
type == 'plugins' ? item_dir : %r{#{item_dir}#{type}\\?/}i
end
end
end

View File

@@ -28,7 +28,7 @@ module WPScan
end
def passive_urls_xpath
'//a[contains(@href, "rdf")]/@href'
'//a[contains(@href, "/rdf")]/@href'
end
def aggressive_urls(_opts = {})

View File

@@ -7,46 +7,130 @@ module WPScan
include References
end
#
# Empty classes for the #type to be correctly displayed (as taken from the self.class from the parent)
#
class BackupDB < InterestingFinding
def to_s
@to_s ||= "A backup directory has been found: #{url}"
end
# @return [ Hash ]
def references
@references ||= { url: ['https://github.com/wpscanteam/wpscan/issues/422'] }
end
end
class DebugLog < InterestingFinding
def to_s
@to_s ||= "Debug Log found: #{url}"
end
# @ return [ Hash ]
def references
@references ||= { url: ['https://codex.wordpress.org/Debugging_in_WordPress'] }
end
end
class DuplicatorInstallerLog < InterestingFinding
# @return [ Hash ]
def references
@references ||= { url: ['https://www.exploit-db.com/ghdb/3981/'] }
end
end
class EmergencyPwdResetScript < InterestingFinding
def references
@references ||= {
url: ['https://codex.wordpress.org/Resetting_Your_Password#Using_the_Emergency_Password_Reset_Script']
}
end
end
class FullPathDisclosure < InterestingFinding
def to_s
@to_s ||= "Full Path Disclosure found: #{url}"
end
# @return [ Hash ]
def references
@references ||= { url: ['https://www.owasp.org/index.php/Full_Path_Disclosure'] }
end
end
class MuPlugins < InterestingFinding
# @return [ String ]
def to_s
@to_s ||= "This site has 'Must Use Plugins': #{url}"
end
# @return [ Hash ]
def references
@references ||= { url: ['http://codex.wordpress.org/Must_Use_Plugins'] }
end
end
class Multisite < InterestingFinding
# @return [ String ]
def to_s
@to_s ||= 'This site seems to be a multisite'
end
# @return [ Hash ]
def references
@references ||= { url: ['http://codex.wordpress.org/Glossary#Multisite'] }
end
end
class Readme < InterestingFinding
def to_s
@to_s ||= "WordPress readme found: #{url}"
end
end
class Registration < InterestingFinding
# @return [ String ]
def to_s
@to_s ||= "Registration is enabled: #{url}"
end
end
class TmmDbMigrate < InterestingFinding
def to_s
@to_s ||= "ThemeMakers migration file found: #{url}"
end
# @return [ Hash ]
def references
@references ||= { packetstorm: [131_957] }
end
end
class UploadDirectoryListing < InterestingFinding
# @return [ String ]
def to_s
@to_s ||= "Upload directory has listing enabled: #{url}"
end
end
class UploadSQLDump < InterestingFinding
def to_s
@to_s ||= "SQL Dump found: #{url}"
end
end
class WPCron < InterestingFinding
# @return [ String ]
def to_s
@to_s ||= "The external WP-Cron seems to be enabled: #{url}"
end
# @return [ Hash ]
def references
@references ||= {
url: [
'https://www.iplocation.net/defend-wordpress-from-ddos',
'https://github.com/wpscanteam/wpscan/issues/1299'
]
}
end
end
end
end

View File

@@ -38,7 +38,7 @@ module WPScan
# @return [ Array<String> ]
def potential_readme_filenames
@potential_readme_filenames ||= [*(DB::DynamicFinders::Plugin.df_data.dig(slug, 'Readme', 'path') || super)]
@potential_readme_filenames ||= Array((DB::DynamicFinders::Plugin.df_data.dig(slug, 'Readme', 'path') || super))
end
end
end

View File

@@ -45,7 +45,7 @@ module WPScan
# @return [ Theme ]
def parent_theme
return unless template
return unless style_body =~ /^@import\surl\(["']?([^"'\)]+)["']?\);\s*$/i
return unless style_body =~ /^@import\surl\(["']?([^"')]+)["']?\);\s*$/i
opts = detection_opts.merge(
style_url: url(Regexp.last_match[1]),
@@ -101,7 +101,7 @@ module WPScan
#
# @return [ String ]
def parse_style_tag(body, tag)
value = body[/#{Regexp.escape(tag)}:[\t ]*([^\r\n\*]+)/i, 1]
value = body[/\b#{Regexp.escape(tag)}:[\t ]*([^\r\n*]+)/, 1]
value && !value.strip.empty? ? value.strip : nil
end

View File

@@ -40,9 +40,9 @@ module WPScan
def rce_132_vuln
Vulnerability.new(
'Timthumb <= 1.32 Remote Code Execution',
{ exploitdb: ['17602'] },
'RCE',
'1.33'
references: { exploitdb: ['17602'] },
type: 'RCE',
fixed_in: '1.33'
)
end
@@ -50,12 +50,12 @@ module WPScan
def rce_webshot_vuln
Vulnerability.new(
'Timthumb <= 2.8.13 WebShot Remote Code Execution',
{
references: {
url: ['http://seclists.org/fulldisclosure/2014/Jun/117', 'https://github.com/wpscanteam/wpscan/issues/519'],
cve: '2014-4663'
},
'RCE',
'2.8.14'
type: 'RCE',
fixed_in: '2.8.14'
)
end

View File

@@ -23,7 +23,7 @@ module WPScan
# @option opts [ Hash ] :version_detection The options to use when looking for the version
# @option opts [ String ] :url The URL of the item
def initialize(slug, blog, opts = {})
@slug = URI.decode(slug)
@slug = Addressable::URI.unencode(slug)
@blog = blog
@uri = Addressable::URI.parse(opts[:url]) if opts[:url]
@@ -39,7 +39,7 @@ module WPScan
@vulnerabilities = []
[*db_data['vulnerabilities']].each do |json_vuln|
Array(db_data['vulnerabilities']).each do |json_vuln|
vulnerability = Vulnerability.load_from_json(json_vuln)
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
end
@@ -83,11 +83,6 @@ module WPScan
end
end
# URI.encode is preferered over Addressable::URI.encode as it will encode
# leading # character:
# URI.encode('#t#') => %23t%23
# Addressable::URI.encode('#t#') => #t%23
#
# @param [ String ] path Optional path to merge with the uri
#
# @return [ String ]
@@ -95,7 +90,7 @@ module WPScan
return unless @uri
return @uri.to_s unless path
@uri.join(URI.encode(path)).to_s
@uri.join(Addressable::URI.encode(path)).to_s
end
# @return [ Boolean ]
@@ -166,7 +161,7 @@ module WPScan
# @return [ Typhoeus::Response ]
def head_and_get(path, codes = [200], params = {})
final_path = +@path_from_blog
final_path << URI.encode(path) unless path.nil?
final_path << path unless path.nil?
blog.head_and_get(final_path, codes, params)
end

View File

@@ -53,7 +53,7 @@ module WPScan
@vulnerabilities = []
[*db_data['vulnerabilities']].each do |json_vuln|
Array(db_data['vulnerabilities']).each do |json_vuln|
@vulnerabilities << Vulnerability.load_from_json(json_vuln)
end

View File

@@ -8,7 +8,7 @@ module WPScan
# @return [ Hash ]
def references
{
@references ||= {
url: ['http://codex.wordpress.org/XML-RPC_Pingback_API'],
metasploit: [
'auxiliary/scanner/http/wordpress_ghost_scanner',

View File

@@ -9,6 +9,6 @@ _______________________________________________________________
WordPress Security Scanner by the WPScan Team
Version <%= WPScan::VERSION %>
<%= ' ' * ((63 - WPScan::DB::Sponsor.text.length)/2) + WPScan::DB::Sponsor.text %>
@_WPScan_, @ethicalhack3r, @erwan_lr, @_FireFart_
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________

View File

@@ -2,7 +2,7 @@
<% if @users.empty? -%>
<%= notice_icon %> No Valid Passwords Found.
<% else -%>
<%= notice_icon %> Valid Combinations Found:
<%= critical_icon %> Valid Combinations Found:
<% @users.each do |user| -%>
| Username: <%= user.username %>, Password: <%= user.password %>
<% end -%>

View File

@@ -9,5 +9,5 @@
<% end -%>
<% else -%>
<%= warning_icon %> No WPVulnDB API Token given, as a result vulnerability data has not been output.
<%= warning_icon %> You can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up.
<%= warning_icon %> You can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up
<% end -%>

View File

@@ -1,4 +1,7 @@
| <%= critical_icon %> Title: <%= @v.title %>
<% if @v.cvss -%>
| CVSS: <%= @v.cvss[:score] %> (<%= @v.cvss[:vector] %>)
<% end -%>
<% if @v.fixed_in -%>
| Fixed in: <%= @v.fixed_in %>
<% end -%>

View File

@@ -5,7 +5,7 @@
"@_WPScan_",
"@ethicalhack3r",
"@erwan_lr",
"@_FireFart_"
"@firefart"
],
"sponsor": <%= WPScan::DB::Sponsor.text.to_json %>
},

View File

@@ -19,6 +19,9 @@
<% vulns.each_with_index do |v, index| -%>
{
"title": <%= v.title.to_json %>,
<% if v.cvss -%>
"cvss": <%= v.cvss.to_json %>,
<% end -%>
"fixed_in": <%= v.fixed_in.to_json %>,
"references": <%= v.references.to_json %>
}<% unless index == last_index -%>,<% end -%>

View File

@@ -8,6 +8,6 @@
"requests_remaining": <%= @status['requests_remaining'].to_json %>
<% end -%>
<% else -%>
"error": "No WPVulnDB API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up."
"error": "No WPVulnDB API Token given, as a result vulnerability data has not been output.\nYou can get a free API token with 50 daily requests by registering at https://wpvulndb.com/users/sign_up"
<% end -%>
},

View File

@@ -31,7 +31,7 @@ module WPScan
finder_configs(
finder_class,
Regexp.last_match[1] == 'aggressive'
aggressive: Regexp.last_match[1] == 'aggressive'
)
end

View File

@@ -16,7 +16,7 @@ module WPScan
# @param [ Symbol ] finder_class
# @param [ Boolean ] aggressive
# @return [ Hash ]
def self.finder_configs(finder_class, aggressive = false)
def self.finder_configs(finder_class, aggressive: false)
configs = {}
return configs unless allowed_classes.include?(finder_class)

View File

@@ -24,7 +24,7 @@ module WPScan
# @param [ Symbol ] finder_class
# @param [ Boolean ] aggressive
# @return [ Hash ]
def self.finder_configs(finder_class, aggressive = false)
def self.finder_configs(finder_class, aggressive: false)
configs = {}
return configs unless allowed_classes.include?(finder_class)

View File

@@ -67,13 +67,13 @@ module WPScan
# @return [ Hash ] The params for Typhoeus::Request
# @note Those params can't be overriden by CLI options
def request_params
@request_params ||= {
@request_params ||= Browser.instance.default_connect_request_params.merge(
timeout: 600,
connecttimeout: 300,
accept_encoding: 'gzip, deflate',
cache_ttl: 0,
headers: { 'User-Agent' => Browser.instance.default_user_agent, 'Referer' => nil }
}
headers: { 'User-Agent' => Browser.instance.default_user_agent }
)
end
# @return [ String ] The raw file URL associated with the given filename
@@ -85,7 +85,7 @@ module WPScan
def remote_file_checksum(filename)
url = "#{remote_file_url(filename)}.sha512"
res = Browser.get(url, request_params)
res = Typhoeus.get(url, request_params)
raise Error::Download, res if res.timed_out? || res.code != 200
res.body.chomp
@@ -126,7 +126,7 @@ module WPScan
file_path = local_file_path(filename)
file_url = remote_file_url(filename)
res = Browser.get(file_url, request_params)
res = Typhoeus.get(file_url, request_params)
raise Error::Download, res if res.timed_out? || res.code != 200
File.open(file_path, 'wb') { |f| f.write(res.body) }
@@ -139,24 +139,22 @@ module WPScan
updated = []
FILES.each do |filename|
begin
db_checksum = remote_file_checksum(filename)
db_checksum = remote_file_checksum(filename)
# Checking if the file needs to be updated
next if File.exist?(local_file_path(filename)) && db_checksum == local_file_checksum(filename)
# Checking if the file needs to be updated
next if File.exist?(local_file_path(filename)) && db_checksum == local_file_checksum(filename)
create_backup(filename)
dl_checksum = download(filename)
create_backup(filename)
dl_checksum = download(filename)
raise "#{filename}: checksums do not match" unless dl_checksum == db_checksum
raise Error::ChecksumsMismatch, filename unless dl_checksum == db_checksum
updated << filename
rescue StandardError => e
restore_backup(filename)
raise e
ensure
delete_backup(filename) if File.exist?(backup_file_path(filename))
end
updated << filename
rescue StandardError => e
restore_backup(filename)
raise e
ensure
delete_backup(filename) if File.exist?(backup_file_path(filename))
end
File.write(last_update_file, Time.now)

View File

@@ -21,8 +21,10 @@ module WPScan
# @return [ Hash ]
def self.get(path, params = {})
return {} unless token
return {} if path.end_with?('/latest') # Remove this when api/v4 is up
res = Browser.get(uri.join(path), params.merge(request_params))
# Typhoeus.get is used rather than Browser.get to avoid merging irrelevant params from the CLI
res = Typhoeus.get(uri.join(path), default_request_params.merge(params))
return {} if res.code == 404 # This is for API inconsistencies when dots in path
return JSON.parse(res.body) if NON_ERROR_CODES.include?(res.code)
@@ -64,15 +66,14 @@ module WPScan
end
# @return [ Hash ]
def self.request_params
{
# @note Those params can not be overriden by CLI options
def self.default_request_params
Browser.instance.default_connect_request_params.merge(
headers: {
'Host' => uri.host, # Reset in case user provided a --vhost for the target
'Referer' => nil, # Removes referer set by the cmsscanner to the target url
'User-Agent' => Browser.instance.default_user_agent,
'Authorization' => "Token token=#{token}"
}
}
)
end
end
end

View File

@@ -8,5 +8,17 @@ module WPScan
'Update required, you can not run a scan if a database file is missing.'
end
end
class ChecksumsMismatch < Standard
attr_reader :db_file
def initialize(db_file)
@db_file = db_file
end
def to_s
"#{db_file}: checksums do not match. Please try again in a few minutes."
end
end
end
end

View File

@@ -29,5 +29,11 @@ module WPScan
' use the --scope option or make sure the --url value given is the correct one'
end
end
class NoLoginInterfaceDetected < Standard
def to_s
'Could not find a login interface to perform the password attack against'
end
end
end
end

View File

@@ -17,7 +17,7 @@ module WPScan
end
# Needed to have inheritance of the @child_class_constants
# If inheritance is not needed, then the #child_class_constant can be used in the classe definition, ie
# If inheritance is not needed, then the #child_class_constant can be used in the class definition, ie
# child_class_constant :FILES, PATTERN: /aaa/i
# @return [ Hash ]
def self.child_class_constants
@@ -56,9 +56,7 @@ module WPScan
homepage_result = find(target.homepage_res, opts)
if homepage_result
return homepage_result unless homepage_result.is_a?(Array) && homepage_result.empty?
end
return homepage_result unless homepage_result.nil? || homepage_result&.is_a?(Array) && homepage_result&.empty?
find(target.error_404_res, opts)
end

View File

@@ -11,7 +11,7 @@ module WPScan
def self.child_class_constants
@child_class_constants ||= super.merge(
PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 70
PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[.\d]+)/, CONFIDENCE: 70
)
end
@@ -21,13 +21,11 @@ module WPScan
parsers = ALLOWED_PARSERS.include?(self.class::PARSER) ? [self.class::PARSER] : ALLOWED_PARSERS
parsers.each do |parser|
begin
parsed = parser.respond_to?(:safe_load) ? parser.safe_load(body) : parser.load(body)
parsed = parser.respond_to?(:safe_load) ? parser.safe_load(body) : parser.load(body)
return parsed if parsed.is_a?(Hash) || parsed.is_a?(Array)
rescue StandardError
next
end
return parsed if parsed.is_a?(Hash) || parsed.is_a?(Array)
rescue StandardError
next
end
nil # Make sure nil is returned in case none of the parsers managed to parse the body correctly

View File

@@ -9,7 +9,7 @@ module WPScan
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)\=(?<v>\d+\.[\.\d]+)/i, CONFIDENCE_PER_OCCURENCE: 10
XPATH: nil, FILES: nil, PATTERN: /(?:v|ver|version)=(?<v>\d+\.[.\d]+)/i, CONFIDENCE_PER_OCCURENCE: 10
)
end

View File

@@ -9,7 +9,7 @@ module WPScan
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(
XPATH: nil, PATTERN: /\A(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 60
XPATH: nil, PATTERN: /\A(?<v>\d+\.[.\d]+)/, CONFIDENCE: 60
)
end

View File

@@ -33,10 +33,12 @@ module WPScan
# @return [ Hash ]
def self.child_class_constants
@child_class_constants ||= super().merge(PATTERN: /ver\=(?<v>\d+\.[\.\d]+)/i)
@child_class_constants ||= super().merge(PATTERN: /ver=(?<v>\d+\.[.\d]+)/i)
end
end
# This one has been disabled from the DF.yml as it was causing FPs when a plugin had numerous
# files matching a known WP version.
class WpItemQueryParameter < QueryParameter
def xpath
@xpath ||=

View File

@@ -13,7 +13,7 @@ end
#
# @return [ Symbol ]
def classify_slug(slug)
classified = slug.to_s.gsub(/[^a-z\d\-]/i, '-').gsub(/\-{1,}/, '_').camelize.to_s
classified = slug.to_s.gsub(/[^a-z\d\-]/i, '-').gsub(/-{1,}/, '_').camelize.to_s
classified = "D_#{classified}" if /\d/.match?(classified[0])
classified.to_sym

View File

@@ -2,7 +2,7 @@
module WPScan
# References module (which should be included along with the CMSScanner::References)
# to allow the use of the wpvulndb reference
# to allow the use of the wpvulndb reference.
module References
extend ActiveSupport::Concern

View File

@@ -19,13 +19,13 @@ module WPScan
# @return [ Boolean ]
def vulnerable?
[@wp_version, @main_theme, @plugins, @themes, @timthumbs].each do |e|
[*e].each { |ae| return true if ae && ae.vulnerable? } # rubocop:disable Style/SafeNavigation
Array(e).each { |ae| return true if ae && ae.vulnerable? } # rubocop:disable Style/SafeNavigation
end
return true unless [*@config_backups].empty?
return true unless [*@db_exports].empty?
return true unless Array(@config_backups).empty?
return true unless Array(@db_exports).empty?
[*@users].each { |u| return true if u.password }
Array(@users).each { |u| return true if u.password }
false
end

View File

@@ -11,9 +11,9 @@ module WPScan
module WordPress
include CMSScanner::Target::Platform::PHP
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu\-)?plugins|uploads))|wp-includes)/}i.freeze
WP_JSON_OEMBED_PATTERN = %r{/wp\-json/oembed/}i.freeze
WP_ADMIN_AJAX_PATTERN = %r{\\?/wp\-admin\\?/admin\-ajax\.php}i.freeze
WORDPRESS_PATTERN = %r{/(?:(?:wp-content/(?:themes|(?:mu-)?plugins|uploads))|wp-includes)/}i.freeze
WP_JSON_OEMBED_PATTERN = %r{/wp-json/oembed/}i.freeze
WP_ADMIN_AJAX_PATTERN = %r{\\?/wp-admin\\?/admin-ajax\.php}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
@@ -32,8 +32,12 @@ module WPScan
if %i[mixed aggressive].include?(detection_mode)
%w[wp-admin/install.php wp-login.php].each do |path|
return true if in_scope_uris(Browser.get_and_follow_location(url(path))).any? do |uri|
WORDPRESS_PATTERN.match?(uri.path)
res = Browser.get_and_follow_location(url(path))
next unless res.code == 200
in_scope_uris(res, '//link/@href|//script/@src') do |uri|
return true if WORDPRESS_PATTERN.match?(uri.path)
end
end
end
@@ -44,7 +48,7 @@ module WPScan
# @param [ Typhoeus::Response ] response
# @return [ Boolean ]
def wordpress_from_meta_comments_or_scripts?(response)
in_scope_uris(response) do |uri|
in_scope_uris(response, '//link/@href|//script/@src') do |uri|
return true if WORDPRESS_PATTERN.match?(uri.path) || WP_JSON_OEMBED_PATTERN.match?(uri.path)
end
@@ -100,8 +104,9 @@ module WPScan
unless content_dir
pattern = %r{https?://s\d\.wp\.com#{WORDPRESS_PATTERN}}i.freeze
xpath = '(//@href|//@src)[contains(., "wp.com")]'
uris_from_page(homepage_res) do |uri|
uris_from_page(homepage_res, xpath) do |uri|
return true if uri.to_s.match?(pattern)
end
end
@@ -134,15 +139,16 @@ module WPScan
# the first time the method is called, and the effective_url is then used
# if suitable, otherwise the default wp-login will be.
#
# @return [ String ] The URL to the login page
# @return [ String, false ] The URL to the login page or false if not detected
def login_url
return @login_url if @login_url
return @login_url unless @login_url.nil?
@login_url = url('wp-login.php')
@login_url = url('wp-login.php') # TODO: url(ParsedCli.login_uri)
res = Browser.get_and_follow_location(@login_url)
@login_url = res.effective_url if res.effective_url =~ /wp\-login\.php\z/i && in_scope?(res.effective_url)
@login_url = res.effective_url if res.effective_url =~ /wp-login\.php\z/i && in_scope?(res.effective_url)
@login_url = false if res.code == 404
@login_url
end

View File

@@ -71,7 +71,7 @@ module WPScan
#
# @return [ String ]
def plugin_url(slug)
plugins_uri.join("#{URI.encode(slug)}/").to_s
plugins_uri.join("#{Addressable::URI.encode(slug)}/").to_s
end
# @return [ String ]
@@ -93,7 +93,7 @@ module WPScan
#
# @return [ String ]
def theme_url(slug)
themes_uri.join("#{URI.encode(slug)}/").to_s
themes_uri.join("#{Addressable::URI.encode(slug)}/").to_s
end
# @return [ String, False ] String of the sub_dir found, false otherwise
@@ -104,10 +104,11 @@ module WPScan
return @sub_dir unless @sub_dir.nil?
# url_pattern is from CMSScanner::Target
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp\-includes/)}i
pattern = %r{#{url_pattern}(.+?)/(?:xmlrpc\.php|wp-includes/)}i
xpath = '(//@src|//@href|//@data-src)[contains(., "xmlrpc.php") or contains(., "wp-includes/")]'
[homepage_res, error_404_res].each do |page_res|
in_scope_uris(page_res) do |uri|
in_scope_uris(page_res, xpath) do |uri|
return @sub_dir = Regexp.last_match[1] if uri.to_s.match(pattern)
end
end
@@ -123,9 +124,9 @@ module WPScan
def url(path = nil)
return @uri.to_s unless path
if %r{wp\-content/plugins}i.match?(path)
if %r{wp-content/plugins}i.match?(path)
path = +path.gsub('wp-content/plugins', plugins_dir)
elsif /wp\-content/i.match?(path)
elsif /wp-content/i.match?(path)
path = +path.gsub('wp-content', content_dir)
elsif path[0] != '/' && sub_dir
path = "#{sub_dir}/#{path}"

View File

@@ -2,5 +2,5 @@
# Version
module WPScan
VERSION = '3.7.4'
VERSION = '3.8.7'
end

View File

@@ -18,9 +18,10 @@ module WPScan
new(
json_data['title'],
references,
json_data['vuln_type'],
json_data['fixed_in']
references: references,
type: json_data['vuln_type'],
fixed_in: json_data['fixed_in'],
cvss: json_data['cvss']&.symbolize_keys
)
end
end

View File

@@ -52,9 +52,10 @@ describe WPScan::Controller::Core do
%i[apache iis nginx].each do |server|
context "when #{server}" do
let(:cli_args) { "#{super()} --server #{server}" }
let(:servers) { [:Apache, nil, :IIS, :Nginx] }
it "loads the #{server.capitalize} module and returns :#{server}" do
@stubbed_server = [:Apache, nil, :IIS, :Nginx].sample
@stubbed_server = servers.sample
@expected = server == :iis ? :IIS : server.to_s.camelize.to_sym
end
end
@@ -70,7 +71,7 @@ describe WPScan::Controller::Core do
let(:cli_args) { "#{super()} --no-update" }
it 'raises an error' do
expect { core.update_db_required? }. to raise_error(WPScan::Error::MissingDatabaseFile)
expect { core.update_db_required? }.to raise_error(WPScan::Error::MissingDatabaseFile)
end
end

View File

@@ -1,5 +1,24 @@
# frozen_string_literal: true
XMLRPC_FAILED_BODY = '
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>405</int></value>
</member>
<member>
<name>faultString</name>
<value><string>%s</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>'
describe WPScan::Controller::PasswordAttack do
subject(:controller) { described_class.new }
let(:target_url) { 'http://ex.lo/' }
@@ -21,7 +40,7 @@ describe WPScan::Controller::PasswordAttack do
describe '#users' do
context 'when no --usernames' do
it 'calles target.users' do
it 'calls target.users' do
expect(controller.target).to receive(:users)
controller.users
end
@@ -40,10 +59,6 @@ describe WPScan::Controller::PasswordAttack do
end
end
describe '#passwords' do
xit
end
describe '#run' do
context 'when no --passwords is supplied' do
it 'does not run the attacker' do
@@ -85,20 +100,34 @@ describe WPScan::Controller::PasswordAttack do
end
context 'when wp.getUsersBlogs method listed' do
before { expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2]) }
before do
expect(xmlrpc).to receive(:available_methods).and_return(%w[wp.getUsersBlogs m2])
stub_request(:post, xmlrpc.url).to_return(body: body)
end
context 'when wp.getUsersBlogs method disabled' do
it 'returns false' do
stub_request(:post, xmlrpc.url).to_return(body: 'XML-RPC services are disabled on this site.')
context 'when blog is in EN' do
let(:body) { format(XMLRPC_FAILED_BODY, 'XML-RPC services are disabled on this site.') }
expect(controller.xmlrpc_get_users_blogs_enabled?).to be false
it 'returns false' do
expect(controller.xmlrpc_get_users_blogs_enabled?).to be false
end
end
context 'when blog is in FR' do
let(:body) { format(XMLRPC_FAILED_BODY, 'Les services XML-RPC sont désactivés sur ce site.') }
it 'returns false' do
expect(controller.xmlrpc_get_users_blogs_enabled?).to be false
end
end
end
context 'when wp.getUsersBlogs method enabled' do
it 'returns true' do
stub_request(:post, xmlrpc.url).to_return(body: 'Incorrect username or password.')
let(:body) { 'Incorrect username or password.' }
it 'returns true' do
expect(controller.xmlrpc_get_users_blogs_enabled?).to be true
end
end
@@ -107,15 +136,34 @@ describe WPScan::Controller::PasswordAttack do
end
describe '#attacker' do
before do
allow(controller.target).to receive(:sub_dir)
controller.target.instance_variable_set(:@login_url, nil)
end
context 'when --password-attack provided' do
let(:cli_args) { "#{super()} --password-attack #{attack}" }
context 'when wp-login' do
before { stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status) }
let(:attack) { 'wp-login' }
it 'returns the correct object' do
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
expect(controller.attacker.target).to be_a WPScan::Target
context 'when available' do
let(:status) { 200 }
it 'returns the correct object' do
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
expect(controller.attacker.target).to be_a WPScan::Target
end
end
context 'when not available (404)' do
let(:status) { 404 }
it 'raises an error' do
expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)
end
end
end
@@ -172,11 +220,26 @@ describe WPScan::Controller::PasswordAttack do
context 'when automatic detection' do
context 'when xmlrpc_get_users_blogs_enabled? is false' do
it 'returns the WpLogin' do
before do
expect(controller).to receive(:xmlrpc_get_users_blogs_enabled?).and_return(false)
stub_request(:get, controller.target.url('wp-login.php')).to_return(status: status)
end
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
expect(controller.attacker.target).to be_a WPScan::Target
context 'when wp-login available' do
let(:status) { 200 }
it 'returns the WpLogin' do
expect(controller.attacker).to be_a WPScan::Finders::Passwords::WpLogin
expect(controller.attacker.target).to be_a WPScan::Target
end
end
context 'when wp-login.php not available' do
let(:status) { 404 }
it 'raises an error' do
expect { controller.attacker }.to raise_error(WPScan::Error::NoLoginInterfaceDetected)
end
end
end

View File

@@ -74,20 +74,40 @@ describe WPScan::Controller::VulnApi do
context 'when limited requests' do
let(:requests) { 100 }
it 'does not raise an error' do
it 'sets the token and does not raise an error' do
expect { controller.before_scan }.to_not raise_error
expect(WPScan::DB::VulnApi.token).to eql 'token'
end
context 'when unlimited requests' do
let(:requests) { 'Unlimited' }
it 'does not raise an error' do
it 'sets the token and does not raise an error' do
expect { controller.before_scan }.to_not raise_error
expect(WPScan::DB::VulnApi.token).to eql 'token'
end
end
end
end
end
end
context 'when token in ENV' do
before do
ENV[described_class::ENV_KEY] = 'token-from-env'
expect(WPScan::DB::VulnApi)
.to receive(:status)
.and_return('success' => true, 'plan' => 'free', 'requests_remaining' => 'Unlimited')
end
it 'sets the token and does not raise an error' do
expect { controller.before_scan }.to_not raise_error
expect(WPScan::DB::VulnApi.token).to eql 'token-from-env'
end
end
end
end

View File

@@ -9,10 +9,10 @@ describe WPScan::Finders::DbExports::KnownLocations do
describe '#potential_urls' do
before do
expect(target).to receive(:sub_dir).at_least(1).and_return(false)
allow(target).to receive(:sub_dir).and_return(false)
end
it 'replace {domain_name} by its value' do
it 'replaces {domain_name} by its value' do
expect(finder.potential_urls(opts).keys).to eql %w[
http://ex.lo/aa/ex.sql
http://ex.lo/aa/wordpress.sql
@@ -22,11 +22,53 @@ describe WPScan::Finders::DbExports::KnownLocations do
http://ex.lo/aa/backups/db_backup.sql
]
end
%w[dev poc www].each do |sub_domain|
context "when #{sub_domain} sub-domain" do
let(:url) { "https://#{sub_domain}.domain.tld" }
it 'replaces {domain_name} by its correct value' do
expect(finder.potential_urls(opts).keys).to include "#{url}/domain.sql"
end
end
end
context 'when multi-level tlds' do
let(:url) { 'https://something.com.tr' }
it 'replaces {domain_name} by its correct value' do
expect(finder.potential_urls(opts).keys).to include 'https://something.com.tr/something.sql'
end
end
context 'when multi-level tlds and sub-domain' do
let(:url) { 'https://dev.something.com.tr' }
it 'replaces {domain_name} by its correct value' do
expect(finder.potential_urls(opts).keys).to include 'https://dev.something.com.tr/something.sql'
end
end
context 'when some weird stuff' do
let(:url) { 'https://098f6bcd4621d373cade4e832627b4f6.aa-bb-ccc-dd.domain-test.com' }
it 'replaces {domain_name} by its correct value' do
expect(finder.potential_urls(opts).keys).to include "#{url}/domain-test.sql"
end
end
context 'when a non standard URL' do
let(:url) { 'http://dc-2' }
it 'replaces {domain_name} by its correct value' do
expect(finder.potential_urls(opts).keys).to include "#{url}/dc-2.sql"
end
end
end
describe '#aggressive' do
before do
expect(target).to receive(:sub_dir).at_least(1).and_return(false)
allow(target).to receive(:sub_dir).and_return(false)
expect(target).to receive(:head_or_get_params).and_return(method: :head)
finder.potential_urls(opts).each_key do |url|

View File

@@ -35,15 +35,47 @@ describe WPScan::Finders::InterestingFindings::DuplicatorInstallerLog do
end
context 'when the body matches' do
let(:body) { File.read(fixtures.join(filename)) }
it 'returns the InterestingFinding' do
after do
expect(finder.aggressive).to eql WPScan::Model::DuplicatorInstallerLog.new(
log_url,
confidence: 100,
found_by: described_class::DIRECT_ACCESS
)
end
context 'when old versions of the file' do
let(:body) { File.read(fixtures.join('old.txt')) }
it 'returns the InterestingFinding' do
# handled in after loop above
end
end
context 'when newest versions of the file' do
context 'when PRO format 1' do
let(:body) { File.read(fixtures.join('pro.txt')) }
it 'returns the InterestingFinding' do
# handled in after loop above
end
end
context 'when PRO format 2' do
let(:body) { File.read(fixtures.join('pro2.txt')) }
it 'returns the InterestingFinding' do
# handled in after loop above
end
end
context 'when LITE' do
let(:body) { File.read(fixtures.join('lite.txt')) }
it 'returns the InterestingFinding' do
# handled in after loop above
end
end
end
end
end
end

View File

@@ -4,7 +4,7 @@ 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(:file_url) { url + 'emergency.php' }
let(:file_url) { "#{url}emergency.php" }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'emergency_pwd_reset_script') }
before do

View File

@@ -6,8 +6,55 @@ describe WPScan::Finders::InterestingFindings::MuPlugins do
let(:url) { 'http://ex.lo/' }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'mu_plugins') }
before do
expect(target).to receive(:content_dir).at_least(1).and_return('wp-content')
end
describe '#passive' do
xit
before { stub_request(:get, url).to_return(body: body) }
context 'when no uris' do
let(:body) { '' }
its(:passive) { should be nil }
end
context 'when a large amount of unrelated uris' do
let(:body) do
Array.new(250) { |i| "<a href='#{url}#{i}.html'>Some Link</a><img src='#{url}img-#{i}.png'/>" }.join("\n")
end
it 'should not take a while to process the page' do
time_start = Time.now
result = finder.passive
time_end = Time.now
expect(result).to be nil
expect(time_end - time_start).to be < 1
end
end
context 'when uris' do
let(:body) { File.read(fixtures.join(fixture)) }
context 'when none matching' do
let(:fixture) { 'no_match.html' }
its(:passive) { should be nil }
end
context 'when matching via href' do
let(:fixture) { 'match_href.html' }
its(:passive) { should be_a WPScan::Model::MuPlugins }
end
context 'when matching from src' do
let(:fixture) { 'match_src.html' }
its(:passive) { should be_a WPScan::Model::MuPlugins }
end
end
end
describe '#aggressive' do

View File

@@ -4,7 +4,7 @@ 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(:dump_url) { url + 'wp-content/uploads/dump.sql' }
let(:dump_url) { "#{url}wp-content/uploads/dump.sql" }
let(:fixtures) { FINDERS_FIXTURES.join('interesting_findings', 'upload_sql_dump') }
let(:wp_content) { 'wp-content' }

View File

@@ -13,8 +13,8 @@ describe WPScan::Finders::Medias::AttachmentBruteForcing do
describe '#target_urls' do
it 'returns the expected urls' do
expect(finder.target_urls(range: (1..2))).to eql(
url + '?attachment_id=1' => 1,
url + '?attachment_id=2' => 2
"#{url}?attachment_id=1" => 1,
"#{url}?attachment_id=2" => 2
)
end
end

View File

@@ -0,0 +1,51 @@
# frozen_string_literal: true
describe WPScan::Finders::Passwords::XMLRPC do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Model::XMLRPC.new(url) }
let(:url) { 'http://ex.lo/xmlrpc.php' }
RESPONSE_403_BODY = '<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>403</int></value>
</member>
<member>
<name>faultString</name>
<value><string>Incorrect username or password.</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>'
describe '#attack' do
let(:wordlist_path) { FINDERS_FIXTURES.join('passwords.txt').to_s }
context 'when no valid credentials' do
before do
stub_request(:post, url).to_return(status: status, body: RESPONSE_403_BODY)
finder.attack(users, wordlist_path)
end
let(:users) { %w[admin].map { |username| WPScan::Model::User.new(username) } }
context 'when status = 200' do
let(:status) { 200 }
its('progress_bar.log') { should be_empty }
end
context 'when status = 403' do
let(:status) { 403 }
its('progress_bar.log') { should be_empty }
end
end
end
end

View File

@@ -109,7 +109,7 @@ describe WPScan::Finders::PluginVersion::Readme do
'a-lead-capture-contact-form-and-tab-button-by-awebvoicecom' => '3.1',
'backup-scheduler' => '1.5.9',
'release_date_slash' => '1.0.4'
}. each do |file, version_number|
}.each do |file, version_number|
context "whith #{file}.txt" do
it 'returns the expected version' do
@file = "#{file}.txt"

View File

@@ -13,13 +13,13 @@ describe WPScan::Finders::Users::AuthorIdBruteForcing do
describe '#target_urls' do
it 'returns the correct URLs' do
expect(finder.target_urls(range: (1..2))).to eql(
url + '?author=1' => 1,
url + '?author=2' => 2
"#{url}?author=1" => 1,
"#{url}?author=2" => 2
)
end
end
describe '#potential_username' do
describe '#username_from_response' do
[
'4.1.1', '4.1.1-permalink',
'3.0', '3.0-permalink',
@@ -32,6 +32,19 @@ describe WPScan::Finders::Users::AuthorIdBruteForcing do
expect(finder.username_from_response(res)).to eql 'admin'
end
end
context 'when a lot of unrelated links' do
it 'should not take a while to process the page' do
body = Array.new(300) { |i| "<a href='#{url}#{i}.html'>Some Link</a>" }.join("\n")
body << '<a href="https://wp.lab/author/test/">Link</a>'
time_start = Time.now
expect(finder.username_from_response(Typhoeus::Response.new(body: body))).to eql 'test'
time_end = Time.now
expect(time_end - time_start).to be < 1
end
end
end
describe '#display_name_from_body' do

View File

@@ -16,12 +16,31 @@ describe WPScan::Finders::Users::AuthorPosts do
results = finder.potential_usernames(res)
expect(results).to eql([
['admin', 'Author Pattern', 100],
['admin display_name', 'Display Name', 30],
['editor', 'Author Pattern', 100],
['editor', 'Display Name', 30]
])
expect(results).to eql [
['admin', 'Author Pattern', 100],
['admin display_name', 'Display Name', 30],
['editor', 'Author Pattern', 100],
['editor', 'Display Name', 30]
]
end
context 'when a lot of unrelated uris' do
it 'should not take a while to process the page' do
body = Array.new(300) { |i| "<a href='#{url}#{i}.html'>Some Link</a>" }.join("\n")
body << "<a href='#{url}author/admin/'>Other Link</a>"
body << "<a href='#{url}?author=2'>user display name</a>"
time_start = Time.now
results = finder.potential_usernames(Typhoeus::Response.new(body: body))
time_end = Time.now
expect(results).to eql [
['admin', 'Author Pattern', 100],
['user display name', 'Display Name', 30]
]
expect(time_end - time_start).to be < 1
end
end
end
end

View File

@@ -0,0 +1,48 @@
# frozen_string_literal: true
describe WPScan::Finders::Users::AuthorSitemap do
subject(:finder) { described_class.new(target) }
let(:target) { WPScan::Target.new(url) }
let(:url) { 'http://wp.lab/' }
let(:fixtures) { FINDERS_FIXTURES.join('users', 'author_sitemap') }
describe '#aggressive' do
before do
allow(target).to receive(:sub_dir).and_return(false)
stub_request(:get, finder.sitemap_url).to_return(body: body)
end
context 'when not an XML response' do
let(:body) { '' }
its(:aggressive) { should eql([]) }
end
context 'when an XML response' do
context 'when no usernames disclosed' do
let(:body) { File.read(fixtures.join('no_usernames.xml')) }
its(:aggressive) { should eql([]) }
end
context 'when usernames disclosed' do
let(:body) { File.read(fixtures.join('usernames.xml')) }
it 'returns the expected array of users' do
users = finder.aggressive
expect(users.size).to eql 2
expect(users.first.username).to eql 'admin'
expect(users.first.confidence).to eql 100
expect(users.first.interesting_entries).to eql ['http://wp.lab/wp-sitemap-users-1.xml']
expect(users.last.username).to eql 'author'
expect(users.last.confidence).to eql 100
expect(users.last.interesting_entries).to eql ['http://wp.lab/wp-sitemap-users-1.xml']
end
end
end
end
end

View File

@@ -8,7 +8,7 @@ describe WPScan::Finders::Users::Base do
describe '#finders' do
it 'contains the expected finders' do
expect(user.finders.map { |f| f.class.to_s.demodulize })
.to eq %w[AuthorPosts WpJsonApi OembedApi RSSGenerator YoastSeoAuthorSitemap
.to eq %w[AuthorPosts WpJsonApi OembedApi RSSGenerator AuthorSitemap YoastSeoAuthorSitemap
AuthorIdBruteForcing LoginErrorMessages]
end
end

View File

@@ -5,7 +5,7 @@ describe WPScan::Finders::WpVersion::Readme do
let(:target) { WPScan::Target.new(url).extend(CMSScanner::Target::Server::Apache) }
let(:url) { 'http://ex.lo/' }
let(:fixtures) { FINDERS_FIXTURES.join('wp_version', 'readme') }
let(:readme_url) { url + 'readme.html' }
let(:readme_url) { "#{url}readme.html" }
describe '#aggressive' do
before { stub_request(:get, readme_url).to_return(body: File.read(fixtures.join(file))) }

View File

@@ -202,11 +202,11 @@ describe WPScan::Model::Plugin do
[
WPScan::Vulnerability.new(
'First Vuln <= 6.3.10 - LFI',
{ wpvulndb: '1' },
'LFI',
'6.3.10'
references: { wpvulndb: '1' },
type: 'LFI',
fixed_in: '6.3.10'
),
WPScan::Vulnerability.new('No Fixed In', wpvulndb: '2')
WPScan::Vulnerability.new('No Fixed In', references: { wpvulndb: '2' })
]
end

View File

@@ -41,6 +41,12 @@ describe WPScan::Model::Theme do
its(:style_uri) { should eql 'http://www.elegantthemes.com/gallery/divi/' }
its(:license_uri) { should eql 'http://www.gnu.org/licenses/gpl-2.0.html' }
end
context 'when no tags' do
let(:fixture) { fixtures.join('no_tags.css') }
its(:author) { should eql nil }
end
end
describe '#version' do
@@ -224,11 +230,11 @@ describe WPScan::Model::Theme do
[
WPScan::Vulnerability.new(
'First Vuln',
{ wpvulndb: '1' },
'LFI',
'6.3.10'
references: { wpvulndb: '1' },
type: 'LFI',
fixed_in: '6.3.10'
),
WPScan::Vulnerability.new('No Fixed In', wpvulndb: '2')
WPScan::Vulnerability.new('No Fixed In', references: { wpvulndb: '2' })
]
end

View File

@@ -46,7 +46,7 @@ describe WPScan::Model::WpItem do
end
it 'encodes the path' do
expect(wp_item.url('#t#')).to eql "#{item_url}%23t%23"
expect(wp_item.url('#t#')).to eql "#{item_url}#t%23"
expect(wp_item.url('t .txt')).to eql "#{item_url}t%20.txt"
end
end

View File

@@ -55,31 +55,15 @@ describe WPScan::Model::WpVersion do
expect(version).to be_vulnerable
end
let(:all_vulns) do
[
WPScan::Vulnerability.new(
'WP 3.8.1 - Vuln 1',
{ wpvulndb: '1' },
'SQLI'
),
WPScan::Vulnerability.new(
'WP 3.8.1 - Vuln 2',
{ url: %w[url-2 url-3], osvdb: %w[10], cve: %w[2014-0166], wpvulndb: '2' },
nil,
'3.8.2'
)
]
end
context 'when a signle vuln' do
let(:number) { '3.8.1' }
let(:number) { '3.8' }
let(:db_data) { vuln_api_data_for('wordpresses/38') }
it 'returns the expected result' do
@expected = [WPScan::Vulnerability.new(
'WP 3.8 - Vuln 1',
{ url: %w[url-4], wpvulndb: '3' },
'AUTHBYPASS'
references: { url: %w[url-4], wpvulndb: '3' },
type: 'AUTHBYPASS'
)]
end
end
@@ -92,14 +76,14 @@ describe WPScan::Model::WpVersion do
@expected = [
WPScan::Vulnerability.new(
'WP 3.8.1 - Vuln 1',
{ wpvulndb: '1' },
'SQLI'
references: { wpvulndb: '1' },
type: 'SQLI',
cvss: { score: '5.4', vector: 'VECTOR' }
),
WPScan::Vulnerability.new(
'WP 3.8.1 - Vuln 2',
{ url: %w[url-2 url-3], cve: %w[2014-0166], wpvulndb: '2' },
nil,
'3.8.2'
references: { url: %w[url-2 url-3], cve: %w[2014-0166], wpvulndb: '2' },
fixed_in: '3.8.2'
)
]
end

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