Compare commits

..

217 Commits

Author SHA1 Message Date
erwanlr
30f329fe43 Bumps version 2019-07-23 18:27:09 +01:00
erwanlr
4ce39951a9 Additional specs for #1374 2019-07-23 16:33:09 +01:00
ethicalhack3r
0e9eb34626 Remove Patreon link 2019-07-23 12:09:04 +02:00
erwanlr
0ff299c425 Updates UA used when updating the DB 2019-07-22 12:13:01 +01:00
erwanlr
6366258ce9 Merge branch 'df' 2019-07-20 19:11:06 +01:00
erwanlr
bca69a026e Adds DFs 2019-07-20 19:10:47 +01:00
Christian Mehlmauer
adc26ea42a ruby 2.6.3 2019-07-19 09:16:56 +02:00
Erwan
b16e8d84d7 Merge pull request #1369 from wpscanteam/dependabot/bundler/rubocop-tw-0.73.0
Update rubocop requirement from ~> 0.72.0 to ~> 0.73.0
2019-07-17 11:38:45 +02:00
dependabot-preview[bot]
5ee405d5a0 Update rubocop requirement from ~> 0.72.0 to ~> 0.73.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.72.0...v0.73.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-07-17 05:28:25 +00:00
erwanlr
a5b9470636 Adds DFs 2019-07-13 11:57:18 +01:00
erwanlr
16a3d54cb6 Bumps version 2019-07-11 17:50:46 +01:00
erwanlr
9677dcd978 Makes sure the sub_dir is only checked once 2019-07-10 18:35:46 +01:00
erwanlr
17ea42f918 Updates cms_scanner dep 2019-07-10 09:17:49 +01:00
erwanlr
bd8915918d Switcing back to master 2019-07-09 15:07:42 +01:00
erwanlr
91db6773a0 Reduces --themes-threshold 2019-07-09 14:37:30 +01:00
erwanlr
f50680b61f Adds a --plugins-threshold and --themes-threshold options 2019-07-08 19:47:46 +01:00
erwanlr
3fb5d33333 Switching to dev 2019-07-08 17:34:13 +01:00
erwanlr
f70bbb2660 Switching to master 2019-07-08 14:02:38 +01:00
erwanlr
589c1ac9bb Only create Versions DF when needed 2019-07-08 13:02:29 +01:00
erwanlr
d458fa1b89 Switching to dev 2019-07-08 10:23:42 +01:00
erwanlr
dc2c99434f Switching to master 2019-07-07 12:19:05 +01:00
erwanlr
bbf36562d0 Fixes specs 2019-07-07 09:57:54 +01:00
erwanlr
c458edf3e4 Adds a note about the Readme DF 2019-07-07 08:51:29 +01:00
erwanlr
99c2aaef7a Changes some DF method names to avoid confusion with db ones 2019-07-07 08:35:41 +01:00
erwanlr
921096ca10 Adds DFs 2019-07-07 08:09:22 +01:00
erwanlr
b0fbd6fa36 Removes empty expected DF 2019-07-06 15:58:23 +01:00
erwanlr
21bd67c44f Switching to minor release 2019-07-06 15:53:00 +01:00
erwanlr
4f142985a2 Fixes #1364 2019-07-06 15:32:42 +01:00
erwanlr
bfa89b44bc Switching to dev 2019-07-06 13:04:38 +01:00
erwanlr
eba876e72b Adds DFs 2019-07-06 11:05:22 +01:00
erwanlr
f1a7413e20 Adds Theme DFs 2019-07-05 09:34:13 +01:00
erwanlr
4d32749489 Updates deps and bump version 2019-07-04 17:06:28 +01:00
erwanlr
d911a16684 Removes useless WPScan namespace 2019-07-04 16:46:33 +01:00
erwanlr
d7193bc755 Ref #1364 2019-07-04 16:33:14 +01:00
erwanlr
aee9ffdb9c Fixes #1365 2019-07-04 15:45:01 +01:00
erwanlr
1f627d5e49 Adds DFs 2019-06-29 10:51:03 +01:00
ethicalhack3r
bb67626d09 Change thimthub enum icon back to info 2019-06-28 15:04:30 +02:00
ethicalhack3r
4e0153e94a Change some enum icons to critical 2019-06-28 15:00:05 +02:00
erwanlr
065142ff19 Updates Rubocop dep 2019-06-27 08:15:54 +01:00
erwanlr
8bb6fae52f Adds DFs 2019-06-22 11:31:28 +01:00
Erwan
8cb7b81903 Merge pull request #1359 from wpscanteam/dependabot/bundler/rubocop-performance-tw-1.4.0
Update rubocop-performance requirement from ~> 1.3.0 to ~> 1.4.0
2019-06-21 13:02:00 +02:00
dependabot-preview[bot]
cb214ccda9 Update rubocop-performance requirement from ~> 1.3.0 to ~> 1.4.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.3.0...v1.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-06-21 05:41:30 +00:00
erwanlr
3fa7b96f27 Bumps version 2019-06-17 18:11:37 +01:00
erwanlr
7c8e259072 Adds DFs 2019-06-15 16:28:14 +01:00
erwanlr
743d067042 Adds DFs 2019-06-08 12:11:24 +01:00
Erwan
50ea410718 Merge pull request #1353 from wpscanteam/dependabot/bundler/webmock-tw-3.6.0
Update webmock requirement from ~> 3.5.1 to ~> 3.6.0
2019-06-05 12:50:44 +02:00
dependabot-preview[bot]
e71182aed2 Update webmock requirement from ~> 3.5.1 to ~> 3.6.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.5.1...v3.6.0)
2019-06-05 07:53:59 +00:00
erwanlr
97f7963e0b Adds DFs 2019-06-02 14:05:44 +01:00
Erwan
6cea6a10bd Merge pull request #1352 from wpscanteam/dependabot/bundler/rubocop-tw-0.71.0
Update rubocop requirement from ~> 0.70.0 to ~> 0.71.0
2019-05-31 14:24:27 +02:00
dependabot-preview[bot]
344d41e365 Update rubocop requirement from ~> 0.70.0 to ~> 0.71.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.70.0...v0.71.0)
2019-05-31 08:04:55 +00:00
erwanlr
597a8adfed Removes FP DF 2019-05-24 11:16:52 +01:00
erwanlr
5682e5483a Adds DFs 2019-05-24 11:06:50 +01:00
Erwan
18779edd7d Merge pull request #1349 from wpscanteam/dependabot/bundler/rubocop-tw-0.70.0
Update rubocop requirement from ~> 0.69.0 to ~> 0.70.0
2019-05-22 14:23:49 +02:00
dependabot[bot]
63aeaea77a Update rubocop requirement from ~> 0.69.0 to ~> 0.70.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.69.0...v0.70.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-22 06:25:23 +00:00
Erwan
f51e48cb40 Merge pull request #1347 from wpscanteam/dependabot/bundler/rubocop-tw-0.69.0
Update rubocop requirement from ~> 0.68.0 to ~> 0.69.0
2019-05-14 17:50:34 +02:00
Erwan
193372c79c Adds a note about Nokogiri in the Readme 2019-05-14 14:12:28 +02:00
dependabot[bot]
34d0afe7e5 Update rubocop requirement from ~> 0.68.0 to ~> 0.69.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.68.0...v0.69.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-14 11:26:28 +00:00
Erwan
d33a9dd56d Merge pull request #1346 from wpscanteam/dependabot/bundler/rubocop-performance-tw-1.3.0
Update rubocop-performance requirement from ~> 1.2.0 to ~> 1.3.0
2019-05-14 13:19:26 +02:00
dependabot[bot]
af2be90176 Update rubocop-performance requirement from ~> 1.2.0 to ~> 1.3.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.2.0...v1.3.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-14 06:21:57 +00:00
erwanlr
701fb21544 Adds DFs 2019-05-11 11:34:47 +01:00
erwanlr
c8f010d9a6 Ref #1344 2019-05-08 18:17:25 +01:00
Erwan
c1ca7580e2 Merge pull request #1343 from wpscanteam/dependabot/bundler/rubocop-performance-tw-1.2.0
Update rubocop-performance requirement from ~> 1.1.0 to ~> 1.2.0
2019-05-08 18:56:36 +02:00
dependabot[bot]
11d3c2cbf1 Update rubocop-performance requirement from ~> 1.1.0 to ~> 1.2.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.1.0...v1.2.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-05-06 06:12:47 +00:00
erwanlr
412f576aee Adds DFs 2019-05-03 11:54:25 +01:00
erwanlr
ff98a7b23b Fixes #1341 2019-05-01 19:50:43 +01:00
erwanlr
507bac8542 Merge branch 'master' of github.com:wpscanteam/wpscan 2019-04-29 15:48:07 +01:00
erwanlr
3bd6cf4805 Adds Ruby 2.6.3 to Travis 2019-04-29 15:47:55 +01:00
erwanlr
5712b31869 Updates Rubocop dep 2019-04-29 15:47:33 +01:00
Erwan
b0f9a0b18f Update issue templates 2019-04-29 15:24:22 +02:00
Erwan
f7665b460e Update issue templates 2019-04-29 15:20:44 +02:00
Erwan
100029b640 Delete old issue template 2019-04-29 15:18:10 +02:00
Erwan
2b89bddf0f Update issue templates 2019-04-29 15:17:39 +02:00
erwanlr
ca46bad8ec Bumps version 2019-04-26 11:53:00 +01:00
erwanlr
1ecd2600a3 Adds DFs 2019-04-26 10:56:26 +01:00
erwanlr
28306b126b Adds DFs 2019-04-24 17:32:25 +01:00
erwanlr
5c842e192b Updates deps 2019-04-24 12:42:18 +01:00
erwanlr
f9f307118d Adds DFs 2019-04-19 10:18:52 +01:00
erwanlr
2266fa4f4b Removes useless comment 2019-04-18 14:43:21 +01:00
erwanlr
6df2564d1a Improves Target#wordpress_hosted? 2019-04-18 14:17:00 +01:00
erwanlr
b2a62ebd26 Fixes #1335 2019-04-18 12:26:47 +01:00
erwanlr
2fca30752a Improves wp-content detection 2019-04-18 12:13:56 +01:00
erwanlr
210eced369 Typo in comments 2019-04-17 17:39:20 +01:00
erwanlr
08c574aff8 Improves detection of wp-content folder 2019-04-17 15:52:06 +01:00
Christian Mehlmauer
f4db2d65f1 fix #1309 2019-04-16 06:42:26 +02:00
erwanlr
23b02ade96 Adds DFs 2019-04-13 14:29:24 +01:00
erwanlr
71d35b16ac Adds DFs 2019-04-13 14:22:42 +01:00
erwanlr
200058c52a Adds DFs 2019-04-13 10:37:39 +01:00
erwanlr
edb5fb202a Removes check for 301 when enumerating plugins and themes 2019-04-13 08:13:38 +01:00
erwanlr
d114c25cdb Typo 2019-04-12 14:52:39 +01:00
erwanlr
64e469568b Adds message help for --wp-content-dir and --wp-plugins-dir options 2019-04-12 14:07:28 +01:00
erwanlr
c63d777372 Fixes version wrongly bumped 2019-04-12 14:00:39 +01:00
erwanlr
ae343b8cb0 Checks for wp-content directly (depends on detection-mode) when not identified passively 2019-04-12 13:55:40 +01:00
Erwan
86eb5d2d57 Merge pull request #1332 from wpscanteam/dependabot/bundler/rspec-its-tw-1.3.0
Update rspec-its requirement from ~> 1.2.0 to ~> 1.3.0
2019-04-10 10:33:27 +02:00
dependabot[bot]
b562d241db Update rspec-its requirement from ~> 1.2.0 to ~> 1.3.0
Updates the requirements on [rspec-its](https://github.com/rspec/rspec-its) to permit the latest version.
- [Release notes](https://github.com/rspec/rspec-its/releases)
- [Changelog](https://github.com/rspec/rspec-its/blob/master/Changelog.md)
- [Commits](https://github.com/rspec/rspec-its/compare/v1.2.0...v1.3.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-10 06:07:39 +00:00
erwanlr
49b1829b78 Bumps version 2019-04-08 16:58:26 +01:00
erwanlr
1a5bf4035c Update deps 2019-04-08 09:39:07 +01:00
erwanlr
f3810a1504 Bumps version 2019-04-07 17:45:29 +01:00
erwanlr
4831760c11 Merge branch '3.5.1' 2019-04-07 17:42:51 +01:00
erwanlr
f375d8991e Update deps 2019-04-07 17:35:18 +01:00
erwanlr
8145a4a3a6 Fixes #1330 2019-04-07 17:06:19 +01:00
erwanlr
12c9b49d4c Adds DFs 2019-04-06 11:34:23 +01:00
erwanlr
c8eb81161e Uses https rather than git protocols for CMSScanner dep 2019-04-05 19:53:29 +01:00
erwanlr
8ab246a66c Uses CMSScanner git dep 2019-04-05 19:48:22 +01:00
erwanlr
8dfc4797fa Handles default user_agent_list via CLI option (in CMSScanner) 2019-04-05 19:30:53 +01:00
erwanlr
7888fe1176 Uses ParsedCli 2019-04-05 16:47:14 +01:00
Erwan
8a6f3056a3 Merge pull request #1329 from wpscanteam/dependabot/bundler/rubocop-tw-0.67.1
Update rubocop requirement from ~> 0.66.0 to ~> 0.67.1
2019-04-05 11:37:00 +02:00
dependabot[bot]
5fbdf9e013 Update rubocop requirement from ~> 0.66.0 to ~> 0.67.1
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.66.0...v0.67.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-04-05 06:16:13 +00:00
erwanlr
1da2f5e823 Sets the Target#mu_plugind to true when detected passively 2019-04-04 17:25:58 +01:00
erwanlr
888779f81b Support of Ruby 2.3 removed as its life ended 2019-04-04 15:40:21 +01:00
erwanlr
352286e497 Adds a #maybe_add_cookies to handle website requiring a specific cookie 2019-04-03 19:08:52 +01:00
erwanlr
025ce37c05 Bumps version 2019-04-03 12:32:07 +01:00
erwanlr
d6c2c63679 Updates deps 2019-04-03 10:14:28 +01:00
erwanlr
49efbf25ea Adds detection of Plugin/Theme via errors 500 and custom 401/403 - Fixes #1090 2019-04-03 08:22:31 +01:00
erwanlr
02cdee2776 Retains db_data for Plugin, Theme and WpVersion models 2019-04-02 17:10:07 +01:00
erwanlr
7c9d4d5b05 Updates deps 2019-04-02 11:56:59 +01:00
erwanlr
609b7551f8 Forces GC to start only after Plugin/Theme DF generation 2019-04-02 09:47:16 +01:00
erwanlr
e8f215ae00 Forces the Garbage Collector to run after creating the DFs 2019-04-01 19:39:40 +01:00
erwanlr
2e00aea16e Mem tests 2019-04-01 12:19:40 +01:00
erwanlr
dd274d77f5 Updates deps 2019-04-01 11:15:02 +01:00
Erwan
58171a7b8c Fixes CodeClimate URL 2019-03-30 16:00:32 +01:00
erwanlr
8b05179401 Adds DFs 2019-03-30 14:17:09 +00:00
erwanlr
51d61a7e88 Adds DFs 2019-03-30 12:15:08 +00:00
erwanlr
d653ce4e0e Adds DFs 2019-03-30 11:11:27 +00:00
erwanlr
07b3826806 Adds DFs 2019-03-30 07:22:14 +00:00
erwanlr
1baa3e23b2 Fixes #1326 2019-03-29 08:27:18 +00:00
erwanlr
0aa1f20d47 Removes Changelog detection 2019-03-28 13:40:58 +00:00
erwanlr
1cf330b389 Merge branch 'master' into 3.5.0 2019-03-28 06:45:48 +00:00
erwanlr
1771c4b346 Updates Ruby version 2019-03-27 17:44:47 +00:00
erwanlr
4c053b4873 Updates dockerignore to ignore profiling executables as well 2019-03-27 14:25:58 +00:00
erwanlr
743ba0541b Updates finders to use new methods 2019-03-26 21:10:14 +00:00
erwanlr
cfab2a9cd7 Uses the new CMSScanner Enumerator module 2019-03-26 17:05:19 +00:00
erwanlr
32270efd65 Updates plugin version detection via Readme 2019-03-26 09:02:23 +00:00
erwanlr
7ea1acb7c1 Fixes non detection of plugin/theme readme and changelog files due to changes in CMSSCanner 2019-03-25 21:25:00 +00:00
erwanlr
bf91f60242 Uses the new Browser#forge_request method 2019-03-25 20:42:43 +00:00
Ryan Dewhurst
660885c0b1 Try to resolve weird char after readme conversion 2019-03-25 09:49:03 +01:00
erwanlr
15fd3b969f Uses head_and_get to check for Readme and Changelog locations 2019-03-24 22:01:19 +00:00
erwanlr
f1d15ca7f2 Updates spec for latest changes 2019-03-24 20:24:14 +00:00
erwanlr
6f4f4a5924 Typo 2019-03-24 20:15:43 +00:00
erwanlr
9af0520701 Delegates #head_and_get to #blog in WpItem models 2019-03-24 20:06:03 +00:00
erwanlr
2edeab558e Adds ruby frozen_string_literal comment to profiling bins 2019-03-24 19:57:59 +00:00
erwanlr
87bf59f50b Merge branch 'master' of github.com:wpscanteam/wpscan 2019-03-24 14:38:25 +00:00
erwanlr
eeb69e63f7 Adds DFs 2019-03-24 14:38:01 +00:00
erwanlr
f9435906e7 Merges with Master (and solves conflicts) 2019-03-24 13:01:29 +00:00
Ryan Dewhurst
6c8adbe50e Remove strange char when converted to html 2019-03-23 10:37:05 +01:00
Ryan Dewhurst
23bdb6c579 Open readme links in new tab 2019-03-23 10:14:51 +01:00
Ryan Dewhurst
264411bfb9 Update README.md 2019-03-23 10:00:50 +01:00
Ryan Dewhurst
2104237584 Update README.md 2019-03-23 09:57:50 +01:00
Ryan Dewhurst
0ae2525737 Update README.md 2019-03-23 09:57:33 +01:00
Ryan Dewhurst
b12973a837 Add projects links to the top of Readme 2019-03-23 09:41:14 +01:00
erwanlr
fa0582ce0b Uses head or get method to enumerate config backups 2019-03-22 20:35:22 +00:00
erwanlr
231f5157bf Fixes #1322 2019-03-22 20:20:07 +00:00
erwanlr
8b18204a69 Updates memory_profiler dep, revert changes to memory allocated commit (increased retained memory too much) 2019-03-22 06:56:10 +00:00
erwanlr
95eb6a732c Memprofiling - Increases the top to be displayed to 15 2019-03-21 20:50:57 +00:00
erwanlr
047a188b34 Uses the frozen_string_literal magic comment (will be the default in Ruby 3) 2019-03-21 17:41:29 +00:00
erwanlr
d407815c30 Adds comment about scale_bytes in memory_profiler 2019-03-21 16:54:06 +00:00
erwanlr
1f0f87633b Reduces memory allocation with creating DFs 2019-03-21 13:52:34 +00:00
erwanlr
c15ff4e32e Adds memprof binary - Ref #1321 2019-03-21 12:45:44 +00:00
erwanlr
72bddca314 Adds profiling binary for dev [WIP] - Ref #1321 2019-03-20 21:12:53 +00:00
erwanlr
496fc4ebee Typo 2019-03-20 20:12:18 +00:00
erwanlr
f414e6eeb7 Better code for WpVersion#all 2019-03-20 20:10:30 +00:00
erwanlr
f09606cfa3 Fixes #1319 2019-03-20 15:42:05 +00:00
erwanlr
6304fe4c19 Fixes #1318 2019-03-20 08:41:39 +00:00
erwanlr
5f2b8f8a2e Fixes #1317 2019-03-20 07:47:28 +00:00
erwanlr
898e8d4546 Moves Models into their own namespace - Ref #1315 2019-03-19 21:07:53 +00:00
erwanlr
f1657164d5 Errors moved into their own namespace - Ref #1315 2019-03-19 19:09:16 +00:00
erwanlr
357e13be2b Updates cms_scanner dep 2019-03-19 18:52:18 +00:00
erwanlr
9685568c75 Updates deps 2019-03-19 10:55:50 +00:00
erwanlr
b316940790 Merge branch 'enum-head' 2019-03-18 20:40:36 +00:00
erwanlr
2ced489e1e Updates deps 2019-03-18 20:37:24 +00:00
erwanlr
5969fe08d8 Revert changes related to the unexpected return - Ref #1314 2019-03-18 19:24:02 +00:00
erwanlr
4a427f1ff6 Adds a custom temporary Enumerator for Plugins,Themes and Timthumbs 2019-03-18 19:15:43 +00:00
erwanlr
9a3db275f3 Merge branch 'master' of github.com:wpscanteam/wpscan 2019-03-17 07:25:09 +00:00
erwanlr
475dd4d1ff Ref #1314 2019-03-17 07:24:49 +00:00
erwanlr
57c99c4a34 Fixes #1313 2019-03-17 06:59:44 +00:00
Christian Mehlmauer
966f5691a2 update image 2019-03-16 19:48:47 +01:00
erwanlr
5088ece8a1 Updates deps 2019-03-16 12:35:19 +00:00
erwanlr
943d87fe17 Updates deps 2019-03-16 09:31:01 +00:00
erwanlr
b5363b2689 Adds DFs 2019-03-16 08:38:07 +00:00
erwanlr
c15cb16ca8 Update deps 2019-03-15 14:09:31 +00:00
erwanlr
18b7f088fc Adds ruby versions to Travis 2019-03-15 12:47:06 +00:00
erwanlr
4f9822743c Improves Password Attack against wp-login.php to avoid FP 2019-03-14 19:21:39 +00:00
erwanlr
e7925de5bc Check the wp-login.php for potential redirection before using it 2019-03-14 18:06:32 +00:00
erwanlr
27fc6a7279 Updates cms_scanner dep 2019-03-14 11:55:20 +00:00
erwanlr
ab5f46e955 Adds detection of wp-content from raw JS 2019-03-14 09:14:55 +00:00
erwanlr
d30d212cc5 Updates WP DF (also check non minified file paths) - Ref #1311 2019-03-12 07:55:32 +00:00
erwanlr
adff971d62 Bumps version 2019-03-10 09:47:41 +00:00
erwanlr
23b22f71b8 Reduces confidence of wp-cron detection 2019-03-10 08:02:51 +00:00
erwanlr
fee3671e32 Adds wp-cron.php detection - Fixes #1299 2019-03-10 07:53:12 +00:00
erwanlr
26c6be7268 Fixes #1307 2019-03-10 07:11:48 +00:00
erwanlr
01c5bcf2be Adds DFs 2019-03-09 16:19:25 +00:00
erwanlr
1ab8a5ab98 Updates deps 2019-03-07 19:37:01 +00:00
erwanlr
b54aaca28a Adds missing lines 2019-03-04 07:40:45 +00:00
erwanlr
86a29ae000 Adds DF 2019-03-04 07:35:21 +00:00
erwanlr
a5dbee93ff Adds DFs 2019-03-02 10:43:45 +00:00
Christian Mehlmauer
e0465e6e10 remove line 2019-02-28 08:41:19 +01:00
Christian Mehlmauer
7da48b9dd1 readme linting 2019-02-28 08:18:01 +01:00
Christian Mehlmauer
a64895c3a6 remove UTF characters from license 2019-02-28 08:13:42 +01:00
erwanlr
21f1a5d4c4 Adds DFs 2019-02-23 08:27:27 +00:00
erwanlr
d60f79ca33 Adds DFs 2019-02-16 13:20:51 +00:00
Erwan
2d5cea5033 Adds missing #to_s calls again 2019-02-11 21:14:40 +01:00
erwanlr
b0615215fe Adds missing #to_s calls 2019-02-11 20:03:05 +00:00
erwanlr
7a0f98b2cb Uses Pathname#join rather than File#join when possible 2019-02-11 19:56:07 +00:00
erwanlr
cdc1dab4a6 Bumps version 2019-02-11 11:48:49 +00:00
erwanlr
431739ab19 Updates Rubocop dep 2019-02-11 10:44:29 +00:00
erwanlr
1780399050 Fixes #1277 2019-02-10 15:32:30 +00:00
erwanlr
eb75d38716 Fixes #1284 2019-02-10 13:47:19 +00:00
erwanlr
06f82d78f4 Ref #1285 - Adds comment about the pagination 2019-02-10 10:49:03 +00:00
erwanlr
dee4da1c0e Fixes #1285 2019-02-10 10:45:54 +00:00
erwanlr
e341ec7c60 Adds DFs 2019-02-10 09:44:17 +00:00
Erwan
9146609e4a Update Readme, Fixes #1286 2019-02-03 20:46:03 +01:00
erwanlr
f90615ca41 Adds DF 2019-02-03 07:08:05 +00:00
erwanlr
8a2a6a05ff Adds DFs 2019-01-27 10:54:13 +00:00
Erwan
5a787f8ed5 Adds a note about bug in Ruby 2.5.x, Ref #1283 2019-01-25 20:14:14 +00:00
erwanlr
a904053002 Adds DFs 2019-01-20 17:04:32 +00:00
Erwan
70ecd30dcc Merge pull request #1276 from wpscanteam/dependabot/bundler/rubocop-tw-0.63.0
Update rubocop requirement from ~> 0.62.0 to ~> 0.63.0
2019-01-17 09:32:24 +00:00
dependabot[bot]
b0976d7e47 Update rubocop requirement from ~> 0.62.0 to ~> 0.63.0
Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version.
- [Release notes](https://github.com/rubocop-hq/rubocop/releases)
- [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop-hq/rubocop/commits/v0.63.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-01-17 05:54:18 +00:00
erwanlr
bb5e55016c Adds DFs 2019-01-13 16:56:13 +00:00
1274 changed files with 259450 additions and 1443 deletions

View File

@@ -12,5 +12,5 @@ spec/
Dockerfile
**/*.orig
*.orig
bin/wpscan-docker*
bin/wpscan-*
.wpscan/

View File

@@ -1,3 +1,14 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
Before submitting an issue, please make sure you fully read any potential error messages output and did some research on your own.
### Subject of the issue
Describe your issue here.
@@ -24,4 +35,4 @@ Things you have tried (where relevant):
* Update Ruby to the latest version [ ]
* Ensure you can reach the target site using cURL [ ]
* Proxied WPScan through a HTTP proxy to view the raw traffic [ ]
* Ensure you are using a supported Operating System (Linux and macOS) [ ]
* Ensure you are using a supported Operating System (Linux and macOS) [ ]

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

10
.github/ISSUE_TEMPLATE/other-issue.md vendored Normal file
View File

@@ -0,0 +1,10 @@
---
name: Other Issue
about: Create a report which is not a related to a Bug or Feature
title: ''
labels: ''
assignees: ''
---
Before submitting an issue, please make sure you fully read any potential error messages output and did some research on your own.

3
.gitignore vendored
View File

@@ -21,3 +21,6 @@ doc/
# Old files from v2
cache/
data/
# Profiling reports
bin/memprof*.report

View File

@@ -1,5 +1,6 @@
require: rubocop-performance
AllCops:
TargetRubyVersion: 2.3
TargetRubyVersion: 2.4
Exclude:
- '*.gemspec'
- 'vendor/**/*'
@@ -9,6 +10,8 @@ LineLength:
Max: 120
MethodLength:
Max: 20
Exclude:
- 'app/controllers/enumeration/cli_options.rb'
Lint/UriEscapeUnescape:
Enabled: false
Metrics/AbcSize:
@@ -18,11 +21,11 @@ Metrics/BlockLength:
- 'spec/**/*'
Metrics/ClassLength:
Max: 150
Exclude:
- 'app/controllers/enumeration/cli_options.rb'
Metrics/CyclomaticComplexity:
Max: 8
Style/Documentation:
Enabled: false
Style/FrozenStringLiteralComment:
Enabled: false
Style/FormatStringToken:
Enabled: false

View File

@@ -1 +1 @@
2.6.0
2.6.2

View File

@@ -2,25 +2,22 @@ language: ruby
sudo: false
cache: bundler
rvm:
- 2.3.0
- 2.3.1
- 2.3.2
- 2.3.3
- 2.3.4
- 2.3.5
- 2.3.6
- 2.3.7
- 2.3.8
- 2.4.1
- 2.4.2
- 2.4.3
- 2.4.4
- 2.4.5
- 2.4.6
- 2.5.0
- 2.5.1
- 2.5.2
- 2.5.3
- 2.5.4
- 2.5.5
- 2.6.0
- 2.6.1
- 2.6.2
- 2.6.3
- ruby-head
before_install:
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"

View File

@@ -1,4 +1,4 @@
FROM ruby:2.5.1-alpine AS builder
FROM ruby:2.6.3-alpine AS builder
LABEL maintainer="WPScan Team <team@wpscan.org>"
ARG BUNDLER_ARGS="--jobs=8 --without test development"
@@ -19,19 +19,22 @@ RUN rake install --trace
RUN chmod -R a+r /usr/local/bundle
FROM ruby:2.5-alpine
FROM ruby:2.6.3-alpine
LABEL maintainer="WPScan Team <team@wpscan.org>"
RUN adduser -h /wpscan -g WPScan -D wpscan
COPY --from=builder /usr/local/bundle /usr/local/bundle
RUN chown -R wpscan:wpscan /wpscan
# runtime dependencies
RUN apk add --no-cache libcurl procps sqlite-libs
WORKDIR /wpscan
USER wpscan
RUN /usr/local/bundle/bin/wpscan --update --verbose
ENTRYPOINT ["/usr/local/bundle/bin/wpscan"]

View File

@@ -1,2 +1,6 @@
# frozen_string_literal: true
source 'https://rubygems.org'
gemspec
# gem 'cms_scanner', branch: 'xxx', git: 'https://github.com/wpscanteam/CMSScanner.git'

10
LICENSE
View File

@@ -6,9 +6,9 @@ Cases that include commercialization of WPScan require a commercial, non-free li
1. Definitions
1.1 License means this document.
1.2 Contributor means each individual or legal entity that creates, contributes to the creation of, or owns WPScan.
1.3 WPScan Team means WPScans core developers.
1.1 "License" means this document.
1.2 "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns WPScan.
1.3 "WPScan Team" means WPScans core developers.
2. Commercialization
@@ -29,6 +29,8 @@ Example cases which do not require a commercial license, and thus fall under the
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
We may grant commercial licenses at no monetary cost at our own discretion if the commercial usage is deemed by the WPScan Team to significantly benefit WPScan.
Free-use Terms and Conditions;
3. Redistribution
@@ -57,7 +59,7 @@ WPScan is provided under an AS-IS basis and without any support, updates or main
8. Disclaimer of Warranty
WPScan is provided under this License on an as is basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the WPScan is free of defects, merchantable, fit for a particular purpose or non-infringing.
WPScan is provided under this License on an "as is" basis, without warranty of any kind, either expressed, implied, or statutory, including, without limitation, warranties that the WPScan is free of defects, merchantable, fit for a particular purpose or non-infringing.
9. Limitation of Liability

106
README.md
View File

@@ -1,29 +1,49 @@
![alt text](https://raw.githubusercontent.com/wpscanteam/wpscan/gh-pages/images/wpscan_logo.png "WPScan - WordPress Security Scanner")
<p align="center">
<a href="https://wpscan.org/">
<img src="https://raw.githubusercontent.com/wpscanteam/wpscan/gh-pages/images/wpscan_logo.png" alt="WPScan logo">
</a>
</p>
[![Gem Version](https://badge.fury.io/rb/wpscan.svg)](https://badge.fury.io/rb/wpscan)
[![Build Status](https://travis-ci.org/wpscanteam/wpscan.svg?branch=master)](https://travis-ci.org/wpscanteam/wpscan)
[![Code Climate](https://codeclimate.com/github/wpscanteam/wpscan/badges/gpa.svg)](https://codeclimate.com/github/wpscanteam/wpscan)
[![Patreon Donate](https://img.shields.io/badge/patreon-donate-green.svg)](https://www.patreon.com/wpscan)
<h3 align="center">WPScan</h3>
<p align="center">
WordPress Vulnerability 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>
</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://codeclimate.com/github/wpscanteam/wpscan" target="_blank"><img src="https://codeclimate.com/github/wpscanteam/wpscan/badges/gpa.svg"></a>
</p>
# INSTALL
## Prerequisites:
## Prerequisites
- (Optional but highly recommended: [RVM](https://rvm.io/rvm/install))
- Ruby >= 2.3 - Recommended: latest
- Curl >= 7.21 - Recommended: latest - FYI the 7.29 has a segfault
- Ruby 2.5.0 to 2.5.3 can cause an 'undefined symbol: rmpd_util_str_to_d' error in some systems, see [#1283](https://github.com/wpscanteam/wpscan/issues/1283)
- Curl >= 7.21 - Recommended: latest
- The 7.29 has a segfault
- RubyGems - Recommended: latest
- 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:
### From RubyGems (Recommended)
```
```shell
gem install wpscan
```
### From sources:
On MacOSX, if a ```Gem::FilePermissionError``` is raised due to the Apple's System Integrity Protection (SIP), either install RVM and install wpscan again, or run ```sudo gem install -n /usr/local/bin wpscan``` (see [#1286](https://github.com/wpscanteam/wpscan/issues/1286))
### From sources (NOT Recommended)
Prerequisites: Git
```
```shell
git clone https://github.com/wpscanteam/wpscan
cd wpscan/
@@ -31,19 +51,28 @@ cd wpscan/
bundle install && rake install
```
# Updating
You can update the local database by using ```wpscan --update```
Updating WPScan itself is either done via ```gem update wpscan``` or the packages manager (this is quite important for distributions such as in Kali Linux: ```apt-get update && apt-get upgrade```) depending how WPScan was (pre)installed
# Docker
Pull the repo with ```docker pull wpscanteam/wpscan```
Enumerating usernames
```
```shell
docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u
```
Enumerating a range of usernames
```
```shell
docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u1-100
```
** replace u1-100 with a range of your choice.
# Usage
@@ -57,50 +86,45 @@ The DB is located at ~/.wpscan/db
WPScan can load all options (including the --url) from configuration files, the following locations are checked (order: first to last):
* ~/.wpscan/cli_options.json
* ~/.wpscan/cli_options.yml
* pwd/.wpscan/cli_options.json
* pwd/.wpscan/cli_options.yml
- ~/.wpscan/cli_options.json
- ~/.wpscan/cli_options.yml
- pwd/.wpscan/cli_options.json
- pwd/.wpscan/cli_options.yml
If those files exist, options from them will be loaded and overridden if found twice.
e.g:
~/.wpscan/cli_options.yml:
```
```yml
proxy: 'http://127.0.0.1:8080'
verbose: true
```
pwd/.wpscan/cli_options.yml:
```
```yml
proxy: 'socks5://127.0.0.1:9090'
url: 'http://target.tld'
```
Running ```wpscan``` in the current directory (pwd), is the same as ```wpscan -v --proxy socks5://127.0.0.1:9090 --url http://target.tld```
Enumerating usernames
```
```shell
wpscan --url https://target.tld/ --enumerate u
```
Enumerating a range of usernames
```
```shell
wpscan --url https://target.tld/ --enumerate u1-100
```
** replace u1-100 with a range of your choice.
# PROJECT HOME
[https://wpscan.org](https://wpscan.org)
# VULNERABILITY DATABASE
[https://wpvulndb.com](https://wpvulndb.com)
# LICENSE
## WPScan Public Source License
@@ -123,16 +147,16 @@ A commercial use is one intended for commercial advantage or monetary compensati
Example cases of commercialization are:
- Using WPScan to provide commercial managed/Software-as-a-Service services.
- Distributing WPScan as a commercial product or as part of one.
- Using WPScan as a value added service/product.
- Using WPScan to provide commercial managed/Software-as-a-Service services.
- Distributing WPScan as a commercial product or as part of one.
- Using WPScan as a value added service/product.
Example cases which do not require a commercial license, and thus fall under the terms set out below, include (but are not limited to):
- Penetration testers (or penetration testing organizations) using WPScan as part of their assessment toolkit.
- Penetration Testing Linux Distributions including but not limited to Kali Linux, SamuraiWTF, BackBox Linux.
- Using WPScan to test your own systems.
- Any non-commercial use of WPScan.
- Penetration testers (or penetration testing organizations) using WPScan as part of their assessment toolkit.
- Penetration Testing Linux Distributions including but not limited to Kali Linux, SamuraiWTF, BackBox Linux.
- Using WPScan to test your own systems.
- Any non-commercial use of WPScan.
If you need to purchase a commercial license or are unsure whether you need to purchase a commercial license contact us - team@wpscan.org.
@@ -142,9 +166,9 @@ Free-use Terms and Conditions;
Redistribution is permitted under the following conditions:
- Unmodified License is provided with WPScan.
- Unmodified Copyright notices are provided with WPScan.
- Does not conflict with the commercialization clause.
- Unmodified License is provided with WPScan.
- Unmodified Copyright notices are provided with WPScan.
- Does not conflict with the commercialization clause.
### 4. Copying

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'models'
require_relative 'finders'
require_relative 'controllers'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'controllers/core'
require_relative 'controllers/custom_directories'
require_relative 'controllers/wp_version'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Controller
# Controller to add the aliases in the CLI

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Controller
# Specific Core controller to include WordPress checks
@@ -25,53 +27,56 @@ module WPScan
# @return [ Boolean ]
def update_db_required?
if local_db.missing_files?
raise MissingDatabaseFile if parsed_options[:update] == false
raise Error::MissingDatabaseFile if ParsedCli.update == false
return true
end
return parsed_options[:update] unless parsed_options[:update].nil?
return ParsedCli.update unless ParsedCli.update.nil?
return false unless user_interaction? && local_db.outdated?
output('@notice', msg: 'It seems like you have not updated the database for some time.')
print '[?] Do you want to update now? [Y]es [N]o, default: [N]'
Readline.readline =~ /^y/i ? true : false
/^y/i.match?(Readline.readline) ? true : false
end
def update_db
output('db_update_started')
output('db_update_finished', updated: local_db.update, verbose: parsed_options[:verbose])
output('db_update_finished', updated: local_db.update, verbose: ParsedCli.verbose)
exit(0) unless parsed_options[:url]
exit(0) unless ParsedCli.url
end
def before_scan
@last_update = local_db.last_update
maybe_output_banner_help_and_version # From CMS Scanner
maybe_output_banner_help_and_version # From CMSScanner
update_db if update_db_required?
setup_cache
check_target_availability
load_server_module
check_wordpress_state
rescue Error::NotWordPress => e
target.maybe_add_cookies
raise e unless target.wordpress?(ParsedCli.detection_mode)
end
# Raises errors if the target is hosted on wordpress.com or is not running WordPress
# Also check if the homepage_url is still the install url
def check_wordpress_state
raise WordPressHostedError if target.wordpress_hosted?
raise Error::WordPressHosted if target.wordpress_hosted?
if Addressable::URI.parse(target.homepage_url).path =~ %r{/wp-admin/install.php$}i
if %r{/wp-admin/install.php$}i.match?(Addressable::URI.parse(target.homepage_url).path)
output('not_fully_configured', url: target.homepage_url)
exit(WPScan::ExitCode::VULNERABLE)
end
raise NotWordPressError unless target.wordpress? || parsed_options[:force]
raise Error::NotWordPress unless target.wordpress?(ParsedCli.detection_mode) || ParsedCli.force
end
# Loads the related server module in the target
@@ -83,7 +88,7 @@ module WPScan
server = target.server || :Apache # Tries to auto detect the server
# Force a specific server module to be loaded if supplied
case parsed_options[:server]
case ParsedCli.server
when :apache
server = :Apache
when :iis
@@ -95,7 +100,7 @@ module WPScan
mod = CMSScanner::Target::Server.const_get(server)
target.extend mod
WPScan::WpItem.include mod
Model::WpItem.include mod
server
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Controller
# Controller to ensure that the wp-content and wp-plugins
@@ -5,18 +7,20 @@ module WPScan
class CustomDirectories < CMSScanner::Controller::Base
def cli_options
[
OptString.new(['--wp-content-dir DIR']),
OptString.new(['--wp-plugins-dir DIR'])
OptString.new(['--wp-content-dir DIR',
'The wp-content directory if custom or not detected, such as "wp-content"']),
OptString.new(['--wp-plugins-dir DIR',
'The plugins directory if custom or not detected, such as "wp-content/plugins"'])
]
end
def before_scan
target.content_dir = parsed_options[:wp_content_dir] if parsed_options[:wp_content_dir]
target.plugins_dir = parsed_options[:wp_plugins_dir] if parsed_options[:wp_plugins_dir]
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
return if target.content_dir(ParsedCli.detection_mode)
raise 'Unable to identify the wp-content dir, please supply it with --wp-content-dir'
raise Error::WpContentDirNotDetected
end
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'enumeration/cli_options'
require_relative 'enumeration/enum_methods'
@@ -5,13 +7,8 @@ module WPScan
module Controller
# Enumeration Controller
class Enumeration < CMSScanner::Controller::Base
def before_scan
DB::DynamicFinders::Plugin.create_versions_finders
DB::DynamicFinders::Theme.create_versions_finders
end
def run
enum = parsed_options[:enumerate] || {}
enum = ParsedCli.enumerate || {}
enum_plugins if enum_plugins?(enum)
enum_themes if enum_themes?(enum)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Controller
# Enumeration CLI Options
@@ -9,7 +11,6 @@ module WPScan
end
# @return [ Array<OptParseValidator::OptBase> ]
# rubocop:disable Metrics/MethodLength
def cli_enum_choices
[
OptMultiChoices.new(
@@ -43,7 +44,6 @@ module WPScan
)
]
end
# rubocop:enable Metrics/MethodLength
# @return [ Array<OptParseValidator::OptBase> ]
def cli_plugins_opts
@@ -65,6 +65,11 @@ module WPScan
'Use the supplied mode to check plugins versions instead of the --detection-mode ' \
'or --plugins-detection modes.'],
choices: %w[mixed passive aggressive], normalize: :to_sym, default: :mixed
),
OptInteger.new(
['--plugins-threshold THRESHOLD',
'Raise an error when the number of detected plugins via known locations reaches the threshold. ' \
'Set to 0 to ignore the threshold.'], default: 100
)
]
end
@@ -89,6 +94,11 @@ module WPScan
'Use the supplied mode to check themes versions instead of the --detection-mode ' \
'or --themes-detection modes.'],
choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
),
OptInteger.new(
['--themes-threshold THRESHOLD',
'Raise an error when the number of detected themes via known locations reaches the threshold. ' \
'Set to 0 to ignore the threshold.'], default: 20
)
]
end
@@ -98,7 +108,7 @@ module WPScan
[
OptFilePath.new(
['--timthumbs-list FILE-PATH', 'List of timthumbs\' location to use'],
exists: true, default: File.join(DB_DIR, 'timthumbs-v3.txt'), advanced: true
exists: true, default: DB_DIR.join('timthumbs-v3.txt').to_s, advanced: true
),
OptChoice.new(
['--timthumbs-detection MODE',
@@ -113,7 +123,7 @@ module WPScan
[
OptFilePath.new(
['--config-backups-list FILE-PATH', 'List of config backups\' filenames to use'],
exists: true, default: File.join(DB_DIR, 'config_backups.txt'), advanced: true
exists: true, default: DB_DIR.join('config_backups.txt').to_s, advanced: true
),
OptChoice.new(
['--config-backups-detection MODE',
@@ -128,7 +138,7 @@ module WPScan
[
OptFilePath.new(
['--db-exports-list FILE-PATH', 'List of DB exports\' paths to use'],
exists: true, default: File.join(DB_DIR, 'db_exports.txt'), advanced: true
exists: true, default: DB_DIR.join('db_exports.txt').to_s, advanced: true
),
OptChoice.new(
['--db-exports-detection MODE',

View File

@@ -1,37 +1,53 @@
# frozen_string_literal: true
module WPScan
module Controller
# Enumeration Methods
class Enumeration < CMSScanner::Controller::Base
# @param [ String ] type (plugins or themes)
# @param [ Symbol ] detection_mode
#
# @return [ String ] The related enumration message depending on the parsed_options and type supplied
def enum_message(type)
# @return [ String ] The related enumration message depending on the ParsedCli and type supplied
def enum_message(type, detection_mode)
return unless %w[plugins themes].include?(type)
details = if parsed_options[:enumerate][:"vulnerable_#{type}"]
details = if ParsedCli.enumerate[:"vulnerable_#{type}"]
'Vulnerable'
elsif parsed_options[:enumerate][:"all_#{type}"]
elsif ParsedCli.enumerate[:"all_#{type}"]
'All'
else
'Most Popular'
end
"Enumerating #{details} #{type.capitalize}"
"Enumerating #{details} #{type.capitalize} #{enum_detection_message(detection_mode)}"
end
# @param [ Symbol ] detection_mode
#
# @return [ String ]
def enum_detection_message(detection_mode)
detection_method = if detection_mode == :mixed
'Passive and Aggressive'
else
detection_mode.to_s.capitalize
end
"(via #{detection_method} Methods)"
end
# @param [ String ] type (plugins, themes etc)
#
# @return [ Hash ]
def default_opts(type)
mode = parsed_options[:"#{type}_detection"] || parsed_options[:detection_mode]
mode = ParsedCli.options[:"#{type}_detection"] || ParsedCli.detection_mode
{
mode: mode,
exclude_content: parsed_options[:exclude_content_based],
exclude_content: ParsedCli.exclude_content_based,
show_progression: user_interaction?,
version_detection: {
mode: parsed_options[:"#{type}_version_detection"] || mode,
confidence_threshold: parsed_options[:"#{type}_version_all"] ? 0 : 100
mode: ParsedCli.options[:"#{type}_version_detection"] || mode,
confidence_threshold: ParsedCli.options[:"#{type}_version_all"] ? 0 : 100
}
}
end
@@ -45,20 +61,24 @@ module WPScan
def enum_plugins
opts = default_opts('plugins').merge(
list: plugins_list_from_opts(parsed_options),
list: plugins_list_from_opts(ParsedCli.options),
threshold: ParsedCli.plugins_threshold,
sort: true
)
output('@info', msg: enum_message('plugins')) if user_interaction?
output('@info', msg: enum_message('plugins', opts[:mode])) if user_interaction?
# Enumerate the plugins & find their versions to avoid doing that when #version
# is called in the view
plugins = target.plugins(opts)
output('@info', msg: 'Checking Plugin Versions') if user_interaction? && !plugins.empty?
if user_interaction? && !plugins.empty?
output('@info',
msg: "Checking Plugin Versions #{enum_detection_message(opts[:version_detection][:mode])}")
end
plugins.each(&:version)
plugins.select!(&:vulnerable?) if parsed_options[:enumerate][:vulnerable_plugins]
plugins.select!(&:vulnerable?) if ParsedCli.enumerate[:vulnerable_plugins]
output('plugins', plugins: plugins)
end
@@ -88,20 +108,24 @@ module WPScan
def enum_themes
opts = default_opts('themes').merge(
list: themes_list_from_opts(parsed_options),
list: themes_list_from_opts(ParsedCli.options),
threshold: ParsedCli.themes_threshold,
sort: true
)
output('@info', msg: enum_message('themes')) if user_interaction?
output('@info', msg: enum_message('themes', opts[:mode])) if user_interaction?
# Enumerate the themes & find their versions to avoid doing that when #version
# is called in the view
themes = target.themes(opts)
output('@info', msg: 'Checking Theme Versions') if user_interaction? && !themes.empty?
if user_interaction? && !themes.empty?
output('@info',
msg: "Checking Theme Versions #{enum_detection_message(opts[:version_detection][:mode])}")
end
themes.each(&:version)
themes.select!(&:vulnerable?) if parsed_options[:enumerate][:vulnerable_themes]
themes.select!(&:vulnerable?) if ParsedCli.enumerate[:vulnerable_themes]
output('themes', themes: themes)
end
@@ -123,31 +147,33 @@ module WPScan
end
def enum_timthumbs
opts = default_opts('timthumbs').merge(list: parsed_options[:timthumbs_list])
opts = default_opts('timthumbs').merge(list: ParsedCli.timthumbs_list)
output('@info', msg: 'Enumerating Timthumbs') if user_interaction?
output('@info', msg: "Enumerating Timthumbs #{enum_detection_message(opts[:mode])}") if user_interaction?
output('timthumbs', timthumbs: target.timthumbs(opts))
end
def enum_config_backups
opts = default_opts('config_backups').merge(list: parsed_options[:config_backups_list])
opts = default_opts('config_backups').merge(list: ParsedCli.config_backups_list)
output('@info', msg: 'Enumerating Config Backups') if user_interaction?
output('@info', msg: "Enumerating Config Backups #{enum_detection_message(opts[:mode])}") if user_interaction?
output('config_backups', config_backups: target.config_backups(opts))
end
def enum_db_exports
opts = default_opts('db_exports').merge(list: parsed_options[:db_exports_list])
opts = default_opts('db_exports').merge(list: ParsedCli.db_exports_list)
output('@info', msg: 'Enumerating DB Exports') if user_interaction?
output('@info', msg: "Enumerating DB Exports #{enum_detection_message(opts[:mode])}") if user_interaction?
output('db_exports', db_exports: target.db_exports(opts))
end
def enum_medias
opts = default_opts('medias').merge(range: parsed_options[:enumerate][:medias])
opts = default_opts('medias').merge(range: ParsedCli.enumerate[:medias])
if user_interaction?
output('@info', msg: 'Enumerating Medias (Permalink setting must be set to "Plain" for those to be detected)')
output('@info',
msg: "Enumerating Medias #{enum_detection_message(opts[:mode])} "\
'(Permalink setting must be set to "Plain" for those to be detected)')
end
output('medias', medias: target.medias(opts))
@@ -157,16 +183,16 @@ module WPScan
#
# @return [ Boolean ] Wether or not to enumerate the users
def enum_users?(opts)
opts[:users] || (parsed_options[:passwords] && !parsed_options[:username] && !parsed_options[:usernames])
opts[:users] || (ParsedCli.passwords && !ParsedCli.username && !ParsedCli.usernames)
end
def enum_users
opts = default_opts('users').merge(
range: enum_users_range,
list: parsed_options[:users_list]
list: ParsedCli.users_list
)
output('@info', msg: 'Enumerating Users') if user_interaction?
output('@info', msg: "Enumerating Users #{enum_detection_message(opts[:mode])}") if user_interaction?
output('users', users: target.users(opts))
end
@@ -174,7 +200,7 @@ module WPScan
# If the --enumerate is used, the default value is handled by the Option
# However, when using --passwords alone, the default has to be set by the code below
def enum_users_range
parsed_options[:enumerate][:users] || cli_enum_choices[0].choices[:u].validate(nil)
ParsedCli.enumerate[:users] || cli_enum_choices[0].choices[:u].validate(nil)
end
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Controller
# Main Theme Controller
@@ -16,9 +18,9 @@ module WPScan
output(
'theme',
theme: target.main_theme(
mode: parsed_options[:main_theme_detection] || parsed_options[:detection_mode]
mode: ParsedCli.main_theme_detection || ParsedCli.detection_mode
),
verbose: parsed_options[:verbose]
verbose: ParsedCli.verbose
)
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Controller
# Password Attack Controller
@@ -22,7 +24,7 @@ module WPScan
end
def run
return unless parsed_options[:passwords]
return unless ParsedCli.passwords
if user_interaction?
output('@info',
@@ -31,13 +33,13 @@ module WPScan
attack_opts = {
show_progression: user_interaction?,
multicall_max_passwords: parsed_options[:multicall_max_passwords]
multicall_max_passwords: ParsedCli.multicall_max_passwords
}
begin
found = []
attacker.attack(users, passwords(parsed_options[:passwords]), attack_opts) do |user|
attacker.attack(users, passwords(ParsedCli.passwords), attack_opts) do |user|
found << user
attacker.progress_bar.log("[SUCCESS] - #{user.username} / #{user.password}")
@@ -52,46 +54,63 @@ module WPScan
@attacker ||= attacker_from_cli_options || attacker_from_automatic_detection
end
# @return [ WPScan::XMLRPC ]
# @return [ Model::XMLRPC ]
def xmlrpc
@xmlrpc ||= target.xmlrpc
end
# @return [ CMSScanner::Finders::Finder ]
def attacker_from_cli_options
return unless parsed_options[:password_attack]
return unless ParsedCli.password_attack
case parsed_options[:password_attack]
case ParsedCli.password_attack
when :wp_login
WPScan::Finders::Passwords::WpLogin.new(target)
Finders::Passwords::WpLogin.new(target)
when :xmlrpc
WPScan::Finders::Passwords::XMLRPC.new(xmlrpc)
raise Error::XMLRPCNotDetected unless xmlrpc
Finders::Passwords::XMLRPC.new(xmlrpc)
when :xmlrpc_multicall
WPScan::Finders::Passwords::XMLRPCMulticall.new(xmlrpc)
raise Error::XMLRPCNotDetected unless xmlrpc
Finders::Passwords::XMLRPCMulticall.new(xmlrpc)
end
end
# @return [ Boolean ]
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/
true
else
false
end
end
# @return [ CMSScanner::Finders::Finder ]
def attacker_from_automatic_detection
if xmlrpc&.enabled? && xmlrpc.available_methods.include?('wp.getUsersBlogs')
if xmlrpc_get_users_blogs_enabled?
wp_version = target.wp_version
if wp_version && wp_version < '4.4'
WPScan::Finders::Passwords::XMLRPCMulticall.new(xmlrpc)
Finders::Passwords::XMLRPCMulticall.new(xmlrpc)
else
WPScan::Finders::Passwords::XMLRPC.new(xmlrpc)
Finders::Passwords::XMLRPC.new(xmlrpc)
end
else
WPScan::Finders::Passwords::WpLogin.new(target)
Finders::Passwords::WpLogin.new(target)
end
end
# @return [ Array<Users> ] The users to brute force
def users
return target.users unless parsed_options[:usernames]
return target.users unless ParsedCli.usernames
parsed_options[:usernames].reduce([]) do |acc, elem|
acc << CMSScanner::User.new(elem.chomp)
ParsedCli.usernames.reduce([]) do |acc, elem|
acc << Model::User.new(elem.chomp)
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Controller
# Wp Version Controller
@@ -15,15 +17,15 @@ module WPScan
end
def before_scan
WPScan::DB::DynamicFinders::Wordpress.create_versions_finders
DB::DynamicFinders::Wordpress.create_versions_finders
end
def run
output(
'version',
version: target.wp_version(
mode: parsed_options[:wp_version_detection] || parsed_options[:detection_mode],
confidence_threshold: parsed_options[:wp_version_all] ? 0 : 100,
mode: ParsedCli.wp_version_detection || ParsedCli.detection_mode,
confidence_threshold: ParsedCli.wp_version_all ? 0 : 100,
show_progression: user_interaction?
)
)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'finders/interesting_findings'
require_relative 'finders/wp_items'
require_relative 'finders/wp_version'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'config_backups/known_filenames'
module WPScan

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module ConfigBackups
@@ -13,11 +15,10 @@ module WPScan
def aggressive(opts = {})
found = []
enumerate(potential_urls(opts), opts) do |res|
# Might need to improve that
enumerate(potential_urls(opts), opts.merge(check_full_response: 200)) do |res|
next unless res.body =~ /define/i && res.body !~ /<\s?html/i
found << WPScan::ConfigBackup.new(res.request.url, found_by: DIRECT_ACCESS, confidence: 100)
found << Model::ConfigBackup.new(res.request.url, found_by: DIRECT_ACCESS, confidence: 100)
end
found

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'db_exports/known_locations'
module WPScan

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module DbExports
@@ -6,6 +8,8 @@ module WPScan
class KnownLocations < CMSScanner::Finders::Finder
include CMSScanner::Finders::Finder::Enumerator
SQL_PATTERN = /(?:DROP|(?:UN)?LOCK|CREATE) TABLE|INSERT INTO/.freeze
# @param [ Hash ] opts
# @option opts [ String ] :list
# @option opts [ Boolean ] :show_progression
@@ -14,15 +18,23 @@ module WPScan
def aggressive(opts = {})
found = []
enumerate(potential_urls(opts), opts) do |res|
next unless res.code == 200 && res.body =~ /INSERT INTO/
enumerate(potential_urls(opts), opts.merge(check_full_response: 200)) do |res|
if res.effective_url.end_with?('.zip')
next unless res.headers['Content-Type'] =~ %r{\Aapplication/zip}i
else
next unless res.body =~ SQL_PATTERN
end
found << WPScan::DbExport.new(res.request.url, found_by: DIRECT_ACCESS, confidence: 100)
found << Model::DbExport.new(res.request.url, found_by: DIRECT_ACCESS, confidence: 100)
end
found
end
def full_request_params
@full_request_params ||= { headers: { 'Range' => 'bytes=0-3000' } }
end
# @param [ Hash ] opts
# @option opts [ String ] :list Mandatory
#

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -6,13 +8,12 @@ module WPScan
# @return [ InterestingFinding ]
def aggressive(_opts = {})
path = 'wp-content/backup-db/'
url = target.url(path)
res = Browser.get(url)
res = target.head_and_get(path, [200, 403])
return unless [200, 403].include?(res.code) && !target.homepage_or_404?(res)
WPScan::BackupDB.new(
url,
Model::BackupDB.new(
target.url(path),
confidence: 70,
found_by: DIRECT_ACCESS,
interesting_entries: target.directory_listing_entries(path),

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -9,7 +11,7 @@ module WPScan
return unless target.debug_log?(path)
WPScan::DebugLog.new(
Model::DebugLog.new(
target.url(path),
confidence: 100, found_by: DIRECT_ACCESS,
references: { url: 'https://codex.wordpress.org/Debugging_in_WordPress' }

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -5,13 +7,12 @@ module WPScan
class DuplicatorInstallerLog < CMSScanner::Finders::Finder
# @return [ InterestingFinding ]
def aggressive(_opts = {})
url = target.url('installer-log.txt')
res = Browser.get(url)
path = 'installer-log.txt'
return unless res.body =~ /DUPLICATOR INSTALL-LOG/
return unless target.head_and_get(path).body =~ /DUPLICATOR INSTALL-LOG/
WPScan::DuplicatorInstallerLog.new(
url,
Model::DuplicatorInstallerLog.new(
target.url(path),
confidence: 100,
found_by: DIRECT_ACCESS,
references: { url: 'https://www.exploit-db.com/ghdb/3981/' }

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -5,14 +7,14 @@ module WPScan
class EmergencyPwdResetScript < CMSScanner::Finders::Finder
# @return [ InterestingFinding ]
def aggressive(_opts = {})
url = target.url('/emergency.php')
res = Browser.get(url)
path = 'emergency.php'
res = target.head_and_get(path)
return unless res.code == 200 && !target.homepage_or_404?(res)
WPScan::EmergencyPwdResetScript.new(
url,
confidence: res.body =~ /password/i ? 100 : 40,
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'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -10,7 +12,7 @@ module WPScan
return if fpd_entries.empty?
WPScan::FullPathDisclosure.new(
Model::FullPathDisclosure.new(
target.url(path),
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -7,12 +9,14 @@ module WPScan
def passive(_opts = {})
pattern = %r{#{target.content_dir}/mu\-plugins/}i
target.in_scope_urls(target.homepage_res) do |url|
next unless Addressable::URI.parse(url).path =~ pattern
target.in_scope_uris(target.homepage_res) do |uri|
next unless uri.path =~ pattern
url = target.url('wp-content/mu-plugins/')
return WPScan::MuPlugins.new(
target.mu_plugins = true
return Model::MuPlugins.new(
url,
confidence: 70,
found_by: 'URLs In Homepage (Passive Detection)',
@@ -31,11 +35,9 @@ module WPScan
return unless [200, 401, 403].include?(res.code)
return if target.homepage_or_404?(res)
# TODO: add the check for --exclude-content once implemented ?
target.mu_plugins = true
WPScan::MuPlugins.new(
Model::MuPlugins.new(
url,
confidence: 80,
found_by: DIRECT_ACCESS,

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -15,7 +17,7 @@ module WPScan
target.multisite = true
WPScan::Multisite.new(
Model::Multisite.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -5,14 +7,14 @@ module WPScan
class Readme < CMSScanner::Finders::Finder
# @return [ InterestingFinding ]
def aggressive(_opts = {})
potential_files.each do |file|
url = target.url(file)
res = Browser.get(url)
potential_files.each do |path|
res = target.head_and_get(path)
if res.code == 200 && res.body =~ /wordpress/i
return WPScan::Readme.new(url, confidence: 100, found_by: DIRECT_ACCESS)
end
next unless res.code == 200 && res.body =~ /wordpress/i
return Model::Readme.new(target.url(path), confidence: 100, found_by: DIRECT_ACCESS)
end
nil
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -18,7 +20,7 @@ module WPScan
target.registration_enabled = true
WPScan::Registration.new(
Model::Registration.new(
res.effective_url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -7,11 +9,11 @@ module WPScan
def aggressive(_opts = {})
path = 'wp-content/uploads/tmm_db_migrate/tmm_db_migrate.zip'
url = target.url(path)
res = Browser.get(url)
res = browser.forge_request(url, target.head_or_get_request_params).run
return unless res.code == 200 && res.headers['Content-Type'] =~ %r{\Aapplication/zip}i
WPScan::TmmDbMigrate.new(
Model::TmmDbMigrate.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
@@ -11,7 +13,7 @@ module WPScan
url = target.url(path)
WPScan::UploadDirectoryListing.new(
Model::UploadDirectoryListing.new(
url,
confidence: 100,
found_by: DIRECT_ACCESS,

View File

@@ -1,27 +1,25 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
# UploadSQLDump finder
class UploadSQLDump < CMSScanner::Finders::Finder
SQL_PATTERN = /(?:(?:(?:DROP|CREATE) TABLE)|INSERT INTO)/.freeze
SQL_PATTERN = /(?:DROP|CREATE|(?:UN)?LOCK) TABLE|INSERT INTO/.freeze
# @return [ InterestingFinding ]
def aggressive(_opts = {})
url = dump_url
res = Browser.get(url)
path = 'wp-content/uploads/dump.sql'
res = target.head_and_get(path, [200], get: { headers: { 'Range' => 'bytes=0-3000' } })
return unless res.code == 200 && res.body =~ SQL_PATTERN
return unless res.body =~ SQL_PATTERN
WPScan::UploadSQLDump.new(
url,
Model::UploadSQLDump.new(
target.url(path),
confidence: 100,
found_by: DIRECT_ACCESS
)
end
def dump_url
target.url('wp-content/uploads/dump.sql')
end
end
end
end

View File

@@ -0,0 +1,33 @@
# frozen_string_literal: true
module WPScan
module Finders
module InterestingFindings
# wp-cron.php finder
class WPCron < CMSScanner::Finders::Finder
# @return [ InterestingFinding ]
def aggressive(_opts = {})
res = Browser.get(wp_cron_url)
return unless res.code == 200
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'
]
}
)
end
def wp_cron_url
@wp_cron_url ||= target.url('wp-cron.php')
end
end
end
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'main_theme/css_style'
require_relative 'main_theme/woo_framework_meta_generator'
require_relative 'main_theme/urls_in_homepage'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module MainTheme
@@ -6,7 +8,7 @@ module WPScan
include Finders::WpItems::URLsInHomepage
def create_theme(slug, style_url, opts)
WPScan::Theme.new(
Model::Theme.new(
slug,
target,
opts.merge(found_by: found_by, confidence: 70, style_url: style_url)
@@ -18,10 +20,10 @@ module WPScan
end
def passive_from_css_href(res, opts)
target.in_scope_urls(res, '//style/@src|//link/@href') do |url|
next unless Addressable::URI.parse(url).path =~ %r{/themes/([^\/]+)/style.css\z}i
target.in_scope_uris(res, '//style/@src|//link/@href') do |uri|
next unless uri.path =~ %r{/themes/([^\/]+)/style.css\z}i
return create_theme(Regexp.last_match[1], url, opts)
return create_theme(Regexp.last_match[1], uri.to_s, opts)
end
nil
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module MainTheme
@@ -14,7 +16,7 @@ module WPScan
slugs = items_from_links('themes', false) + items_from_codes('themes', false)
slugs.each_with_object(Hash.new(0)) { |slug, counts| counts[slug] += 1 }.each do |slug, occurences|
found << WPScan::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 2 * occurences))
found << Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 2 * occurences))
end
found

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module MainTheme
@@ -10,7 +12,7 @@ module WPScan
def passive(opts = {})
return unless target.homepage_res.body =~ PATTERN
WPScan::Theme.new(
Model::Theme.new(
Regexp.last_match[1],
target,
opts.merge(found_by: found_by, confidence: 80)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'medias/attachment_brute_forcing'
module WPScan

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Medias
@@ -15,7 +17,7 @@ module WPScan
enumerate(target_urls(opts), opts) do |res|
next unless res.code == 200
found << WPScan::Media.new(res.effective_url, opts.merge(found_by: found_by, confidence: 100))
found << Model::Media.new(res.effective_url, opts.merge(found_by: found_by, confidence: 100))
end
found

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'passwords/wp_login'
require_relative 'passwords/xml_rpc'
require_relative 'passwords/xml_rpc_multicall'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Passwords
@@ -10,7 +12,8 @@ module WPScan
end
def valid_credentials?(response)
response.code == 302
response.code == 302 &&
response.headers['Set-Cookie']&.any? { |cookie| cookie =~ /wordpress_logged_in_/i }
end
def errored_response?(response)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Passwords

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Passwords
@@ -20,13 +22,13 @@ module WPScan
target.multi_call(methods).run
end
# @param [ Array<CMSScanner::User> ] users
# @param [ Array<Model::User> ] users
# @param [ Array<String> ] passwords
# @param [ Hash ] opts
# @option opts [ Boolean ] :show_progression
# @option opts [ Integer ] :multicall_max_passwords
#
# @yield [ CMSScanner::User ] When a valid combination is found
# @yield [ Model::User ] When a valid combination is found
#
# TODO: Make rubocop happy about metrics etc
#

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'plugin_version/readme'
module WPScan
@@ -7,29 +9,19 @@ module WPScan
class Base
include CMSScanner::Finders::UniqueFinder
# @param [ WPScan::Plugin ] plugin
# @param [ Model::Plugin ] plugin
def initialize(plugin)
finders << PluginVersion::Readme.new(plugin)
load_specific_finders(plugin)
create_and_load_dynamic_versions_finders(plugin)
end
# Load the finders associated with the plugin
# Create the dynamic version finders related to the plugin and register them
#
# @param [ WPScan::Plugin ] plugin
def load_specific_finders(plugin)
module_name = plugin.classify
return unless Finders::PluginVersion.constants.include?(module_name)
mod = Finders::PluginVersion.const_get(module_name)
mod.constants.each do |constant|
c = mod.const_get(constant)
next unless c.is_a?(Class)
finders << c.new(plugin)
# @param [ Model::Plugin ] plugin
def create_and_load_dynamic_versions_finders(plugin)
DB::DynamicFinders::Plugin.create_versions_finders(plugin.slug).each do |finder|
finders << finder.new(plugin)
end
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module PluginVersion
@@ -7,21 +9,23 @@ module WPScan
def aggressive(_opts = {})
found_by_msg = 'Readme - %s (Aggressive Detection)'
WPScan::WpItem::READMES.each do |file|
url = target.url(file)
res = Browser.get(url)
# The target(plugin)#readme_url can't be used directly here
# as if the --detection-mode is passive, it will always return nil
target.potential_readme_filenames.each do |file|
res = target.head_and_get(file)
next unless res.code == 200 && !(numbers = version_numbers(res.body)).empty?
return numbers.reduce([]) do |a, e|
a << WPScan::Version.new(
a << Model::Version.new(
e[0],
found_by: format(found_by_msg, e[1]),
confidence: e[2],
interesting_entries: [url]
interesting_entries: [res.effective_url]
)
end
end
nil
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'plugins/urls_in_homepage'
require_relative 'plugins/known_locations'
# From the DynamicFinders

View File

@@ -1,8 +1,10 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
# Plugins finder from Dynamic Finder 'BodyPattern'
class BodyPattern < WPScan::Finders::DynamicFinder::WpItems::Finder
class BodyPattern < Finders::DynamicFinder::WpItems::Finder
DEFAULT_CONFIDENCE = 30
# @param [ Hash ] opts The options from the #passive, #aggressive methods
@@ -15,7 +17,7 @@ module WPScan
def process_response(opts, response, slug, klass, config)
return unless response.body =~ config['pattern']
Plugin.new(
Model::Plugin.new(
slug,
target,
opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)

View File

@@ -1,8 +1,10 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
# Plugins finder from the Dynamic Finder 'Comment'
class Comment < WPScan::Finders::DynamicFinder::WpItems::Finder
class Comment < Finders::DynamicFinder::WpItems::Finder
DEFAULT_CONFIDENCE = 30
# @param [ Hash ] opts The options from the #passive, #aggressive methods
@@ -18,7 +20,7 @@ module WPScan
next unless comment =~ config['pattern']
return Plugin.new(
return Model::Plugin.new(
slug,
target,
opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)

View File

@@ -1,8 +1,10 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
# Plugins finder from Dynamic Finder 'ConfigParser'
class ConfigParser < WPScan::Finders::DynamicFinder::WpItems::Finder
class ConfigParser < Finders::DynamicFinder::WpItems::Finder
DEFAULT_CONFIDENCE = 40
# @param [ Hash ] opts The options from the #passive, #aggressive methods
@@ -19,7 +21,7 @@ module WPScan
# when checking for plugins
#
Plugin.new(
Model::Plugin.new(
slug,
target,
opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)

View File

@@ -1,8 +1,10 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
# Plugins finder from Dynamic Finder 'HeaderPattern'
class HeaderPattern < WPScan::Finders::DynamicFinder::WpItems::Finder
class HeaderPattern < Finders::DynamicFinder::WpItems::Finder
DEFAULT_CONFIDENCE = 30
# @param [ Hash ] opts
@@ -18,7 +20,7 @@ module WPScan
configs.each do |klass, config|
next unless headers[config['header']] && headers[config['header']].to_s =~ config['pattern']
found << Plugin.new(
found << Model::Plugin.new(
slug,
target,
opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)

View File

@@ -1,8 +1,10 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
# Plugins finder from the Dynamic Finder 'JavascriptVar'
class JavascriptVar < WPScan::Finders::DynamicFinder::WpItems::Finder
class JavascriptVar < Finders::DynamicFinder::WpItems::Finder
DEFAULT_CONFIDENCE = 60
# @param [ Hash ] opts The options from the #passive, #aggressive methods
@@ -16,7 +18,7 @@ module WPScan
response.html.xpath(config['xpath'] || '//script[not(@src)]').each do |node|
next if config['pattern'] && !node.text.match(config['pattern'])
return Plugin.new(
return Model::Plugin.new(
slug,
target,
opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
@@ -5,6 +7,11 @@ module WPScan
class KnownLocations < CMSScanner::Finders::Finder
include CMSScanner::Finders::Finder::Enumerator
# @return [ Array<Integer> ]
def valid_response_codes
@valid_response_codes ||= [200, 401, 403, 500].freeze
end
# @param [ Hash ] opts
# @option opts [ String ] :list
#
@@ -12,12 +19,10 @@ module WPScan
def aggressive(opts = {})
found = []
enumerate(target_urls(opts), opts) do |res, slug|
# TODO: follow the location (from enumerate()) and remove the 301 here ?
# As a result, it might remove false positive due to redirection to the homepage
next unless [200, 401, 403, 301].include?(res.code)
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))
found << WPScan::Plugin.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
raise Error::PluginsThresholdReached if opts[:threshold].positive? && found.size >= opts[:threshold]
end
found
@@ -30,10 +35,9 @@ module WPScan
def target_urls(opts = {})
slugs = opts[:list] || DB::Plugins.vulnerable_slugs
urls = {}
plugins_url = target.plugins_url
slugs.each do |slug|
urls["#{plugins_url}#{URI.encode(slug)}/"] = slug
urls[target.plugin_url(slug)] = slug
end
urls

View File

@@ -1,8 +1,10 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
# Plugins finder from Dynamic Finder 'QueryParameter'
class QueryParameter < WPScan::Finders::DynamicFinder::WpItems::Finder
class QueryParameter < Finders::DynamicFinder::WpItems::Finder
DEFAULT_CONFIDENCE = 10
def passive(_opts = {})

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
@@ -14,7 +16,7 @@ module WPScan
found = []
(items_from_links('plugins') + items_from_codes('plugins')).uniq.sort.each do |slug|
found << Plugin.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
found << Model::Plugin.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
end
found

View File

@@ -1,8 +1,10 @@
# frozen_string_literal: true
module WPScan
module Finders
module Plugins
# Plugins finder from the Dynamic Finder 'Xpath'
class Xpath < WPScan::Finders::DynamicFinder::WpItems::Finder
class Xpath < Finders::DynamicFinder::WpItems::Finder
DEFAULT_CONFIDENCE = 40
# @param [ Hash ] opts The options from the #passive, #aggressive methods
@@ -16,7 +18,7 @@ module WPScan
response.html.xpath(config['xpath']).each do |node|
next if config['pattern'] && !node.text.match(config['pattern'])
return Plugin.new(
return Model::Plugin.new(
slug,
target,
opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'theme_version/style'
require_relative 'theme_version/woo_framework_meta_generator'
@@ -8,31 +10,21 @@ module WPScan
class Base
include CMSScanner::Finders::UniqueFinder
# @param [ WPScan::Theme ] theme
# @param [ Model::Theme ] theme
def initialize(theme)
finders <<
ThemeVersion::Style.new(theme) <<
ThemeVersion::WooFrameworkMetaGenerator.new(theme)
load_specific_finders(theme)
create_and_load_dynamic_versions_finders(theme)
end
# Load the finders associated with the theme
# Create the dynamic version finders related to the theme and register them
#
# @param [ WPScan::Theme ] theme
def load_specific_finders(theme)
module_name = theme.classify
return unless Finders::ThemeVersion.constants.include?(module_name)
mod = Finders::ThemeVersion.const_get(module_name)
mod.constants.each do |constant|
c = mod.const_get(constant)
next unless c.is_a?(Class)
finders << c.new(theme)
# @param [ Model::Theme ] theme
def create_and_load_dynamic_versions_finders(theme)
DB::DynamicFinders::Theme.create_versions_finders(theme.slug).each do |finder|
finders << finder.new(theme)
end
end
end

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module ThemeVersion
@@ -11,7 +13,7 @@ module WPScan
return unless Regexp.last_match[1] == target.slug
WPScan::Version.new(Regexp.last_match[2], found_by: found_by, confidence: 80)
Model::Version.new(Regexp.last_match[2], found_by: found_by, confidence: 80)
end
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'themes/urls_in_homepage'
require_relative 'themes/known_locations'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Themes
@@ -5,6 +7,11 @@ module WPScan
class KnownLocations < CMSScanner::Finders::Finder
include CMSScanner::Finders::Finder::Enumerator
# @return [ Array<Integer> ]
def valid_response_codes
@valid_response_codes ||= [200, 401, 403, 500].freeze
end
# @param [ Hash ] opts
# @option opts [ String ] :list
#
@@ -12,12 +19,10 @@ module WPScan
def aggressive(opts = {})
found = []
enumerate(target_urls(opts), opts) do |res, slug|
# TODO: follow the location (from enumerate()) and remove the 301 here ?
# As a result, it might remove false positive due to redirection to the homepage
next unless [200, 401, 403, 301].include?(res.code)
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))
found << WPScan::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
raise Error::ThemesThresholdReached if opts[:threshold].positive? && found.size >= opts[:threshold]
end
found
@@ -28,12 +33,11 @@ module WPScan
#
# @return [ Hash ]
def target_urls(opts = {})
slugs = opts[:list] || DB::Themes.vulnerable_slugs
urls = {}
themes_url = target.url('wp-content/themes/')
slugs = opts[:list] || DB::Themes.vulnerable_slugs
urls = {}
slugs.each do |slug|
urls["#{themes_url}#{URI.encode(slug)}/"] = slug
urls[target.theme_url(slug)] = slug
end
urls

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Themes
@@ -12,7 +14,7 @@ module WPScan
found = []
(items_from_links('themes') + items_from_codes('themes')).uniq.sort.each do |slug|
found << WPScan::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
found << Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
end
found

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'timthumb_version/bad_request'
module WPScan
@@ -7,7 +9,7 @@ module WPScan
class Base
include CMSScanner::Finders::UniqueFinder
# @param [ WPScan::Timthumb ] target
# @param [ Model::Timthumb ] target
def initialize(target)
finders << TimthumbVersion::BadRequest.new(target)
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module TimthumbVersion
@@ -8,7 +10,7 @@ module WPScan
def aggressive(_opts = {})
return unless Browser.get(target.url).body =~ /(TimThumb version\s*: ([^<]+))/
WPScan::Version.new(
Model::Version.new(
Regexp.last_match[2],
found_by: 'Bad Request (Aggressive Detection)',
confidence: 90,

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'timthumbs/known_locations'
module WPScan

View File

@@ -1,10 +1,19 @@
# frozen_string_literal: true
module WPScan
module Finders
module Timthumbs
# Known Locations Timthumbs Finder
# Note: A vulnerable version, 2.8.13 can be found here:
# https://github.com/GabrielGil/TimThumb/blob/980c3d6a823477761570475e8b83d3e9fcd2d7ae/timthumb.php
class KnownLocations < CMSScanner::Finders::Finder
include CMSScanner::Finders::Finder::Enumerator
# @return [ Array<Integer> ]
def valid_response_codes
@valid_response_codes ||= [400]
end
# @param [ Hash ] opts
# @option opts [ String ] :list Mandatory
#
@@ -12,10 +21,10 @@ module WPScan
def aggressive(opts = {})
found = []
enumerate(target_urls(opts), opts) do |res|
next unless res.code == 400 && res.body =~ /no image specified/i
enumerate(target_urls(opts), opts.merge(check_full_response: 400)) do |res|
next unless res.body =~ /no image specified/i
found << WPScan::Timthumb.new(res.request.url, opts.merge(found_by: found_by, confidence: 100))
found << Model::Timthumb.new(res.request.url, opts.merge(found_by: found_by, confidence: 100))
end
found

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'users/author_posts'
require_relative 'users/wp_json_api'
require_relative 'users/oembed_api'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Users
@@ -5,6 +7,11 @@ module WPScan
class AuthorIdBruteForcing < CMSScanner::Finders::Finder
include CMSScanner::Finders::Finder::Enumerator
# @return [ Array<Integer> ]
def valid_response_codes
@valid_response_codes ||= [200, 301, 302]
end
# @param [ Hash ] opts
# @option opts [ Range ] :range Mandatory
#
@@ -13,12 +20,12 @@ module WPScan
found = []
found_by_msg = 'Author Id Brute Forcing - %s (Aggressive Detection)'
enumerate(target_urls(opts), opts) do |res, id|
enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, id|
username, found_by, confidence = potential_username(res)
next unless username
found << CMSScanner::User.new(
found << Model::User.new(
username,
id: id,
found_by: format(found_by_msg, found_by),
@@ -47,7 +54,7 @@ module WPScan
super(opts.merge(title: ' Brute Forcing Author IDs -'))
end
def request_params
def full_request_params
{ followlocation: true }
end
@@ -76,8 +83,8 @@ module WPScan
# @return [ String, nil ] The username found
def username_from_response(res)
# Permalink enabled
target.in_scope_urls(res, '//link/@href|//a/@href') do |url|
username = username_from_author_url(url)
target.in_scope_uris(res, '//link/@href|//a/@href') do |uri|
username = username_from_author_url(uri.to_s)
return username if username
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Users
@@ -10,7 +12,7 @@ module WPScan
found_by_msg = 'Author Posts - %s (Passive Detection)'
usernames(opts).reduce([]) do |a, e|
a << CMSScanner::User.new(
a << Model::User.new(
e[0],
found_by: format(found_by_msg, e[1]),
confidence: e[2]
@@ -43,12 +45,10 @@ module WPScan
def potential_usernames(res)
usernames = []
target.in_scope_urls(res, '//a/@href') do |url, node|
uri = Addressable::URI.parse(url)
target.in_scope_uris(res, '//a/@href') do |uri, node|
if uri.path =~ %r{/author/([^/\b]+)/?\z}i
usernames << [Regexp.last_match[1], 'Author Pattern', 100]
elsif uri.query =~ /author=[0-9]+/
elsif /author=[0-9]+/.match?(uri.query)
usernames << [node.text.to_s.strip, 'Display Name', 30]
end
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Users
@@ -24,7 +26,7 @@ module WPScan
next unless error =~ /The password you entered for the username|Incorrect Password/i
found << CMSScanner::User.new(username, found_by: found_by, confidence: 100)
found << Model::User.new(username, found_by: found_by, confidence: 100)
end
found

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Users
@@ -21,10 +23,10 @@ module WPScan
return [] unless details
[CMSScanner::User.new(details[0],
found_by: format(found_by_msg, details[1]),
confidence: details[2],
interesting_entries: [api_url])]
[Model::User.new(details[0],
found_by: format(found_by_msg, details[1]),
confidence: details[2],
interesting_entries: [api_url])]
rescue JSON::ParserError
[]
end

View File

@@ -1,10 +1,12 @@
# frozen_string_literal: true
module WPScan
module Finders
module Users
# Users disclosed from the dc:creator field in the RSS
# The names disclosed are display names, however depending on the configuration of the blog,
# they can be the same than usernames
class RSSGenerator < WPScan::Finders::WpVersion::RSSGenerator
class RSSGenerator < Finders::WpVersion::RSSGenerator
def process_urls(urls, _opts = {})
found = []
@@ -17,20 +19,20 @@ module WPScan
begin
res.xml.xpath('//item/dc:creator').each do |node|
potential_username = node.text.to_s
username = node.text.to_s
# Ignoring potential username longer than 60 characters and containing accents
# as they are considered invalid. See https://github.com/wpscanteam/wpscan/issues/1215
next if potential_username.length > 60 || potential_username =~ /[^\x00-\x7F]/
next if username.strip.empty? || username.length > 60 || username =~ /[^\x00-\x7F]/
potential_usernames << potential_username
potential_usernames << username
end
rescue Nokogiri::XML::XPath::SyntaxError
next
end
potential_usernames.uniq.each do |potential_username|
found << CMSScanner::User.new(potential_username, found_by: found_by, confidence: 50)
potential_usernames.uniq.each do |username|
found << Model::User.new(username, found_by: found_by, confidence: 50)
end
break

View File

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

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module Users
@@ -15,10 +17,10 @@ module WPScan
next unless username && !username.strip.empty?
found << CMSScanner::User.new(username,
found_by: found_by,
confidence: 100,
interesting_entries: [sitemap_url])
found << Model::User.new(username,
found_by: found_by,
confidence: 100,
interesting_entries: [sitemap_url])
end
found

View File

@@ -1 +1,3 @@
# frozen_string_literal: true
require_relative 'wp_items/urls_in_homepage'

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module WpItems
@@ -10,8 +12,8 @@ module WPScan
def items_from_links(type, uniq = true)
found = []
target.in_scope_urls(target.homepage_res) do |url|
next unless url =~ item_attribute_pattern(type)
target.in_scope_uris(target.homepage_res) do |uri|
next unless uri.to_s =~ item_attribute_pattern(type)
found << Regexp.last_match[1]
end

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
require_relative 'wp_version/rss_generator'
require_relative 'wp_version/atom_generator'
require_relative 'wp_version/rdf_generator'
@@ -26,7 +28,7 @@ module WPScan
# @param [ WPScan::Target ] target
def initialize(target)
(%w[RSSGenerator AtomGenerator RDFGenerator] +
WPScan::DB::DynamicFinders::Wordpress.versions_finders_configs.keys +
DB::DynamicFinders::Wordpress.versions_finders_configs.keys +
%w[Readme UniqueFingerprinting]
).each do |finder_name|
finders << WpVersion.const_get(finder_name.to_sym).new(target)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module WpVersion

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module WpVersion

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module WpVersion
@@ -13,9 +15,9 @@ module WPScan
number = Regexp.last_match(1)
return unless WPScan::WpVersion.valid?(number)
return unless Model::WpVersion.valid?(number)
WPScan::WpVersion.new(
Model::WpVersion.new(
number,
found_by: 'Readme (Aggressive Detection)',
# Since WP 4.7, the Readme only contains the major version (ie 4.7, 4.8 etc)

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module WpVersion

View File

@@ -1,3 +1,5 @@
# frozen_string_literal: true
module WPScan
module Finders
module WpVersion
@@ -11,7 +13,7 @@ module WPScan
hydra.abort
progress_bar.finish
return WPScan::WpVersion.new(
return Model::WpVersion.new(
version_number,
found_by: 'Unique Fingerprinting (Aggressive Detection)',
confidence: 100,

View File

@@ -1,3 +1,11 @@
# frozen_string_literal: true
module WPScan
module Model
include CMSScanner::Model
end
end
require_relative 'models/interesting_finding'
require_relative 'models/wp_version'
require_relative 'models/xml_rpc'

View File

@@ -1,5 +1,9 @@
# frozen_string_literal: true
module WPScan
# Config Backup
class ConfigBackup < InterestingFinding
module Model
# Config Backup
class ConfigBackup < InterestingFinding
end
end
end

View File

@@ -1,5 +1,9 @@
# frozen_string_literal: true
module WPScan
# DB Export
class DbExport < InterestingFinding
module Model
# DB Export
class DbExport < InterestingFinding
end
end
end

View File

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

View File

@@ -1,5 +1,9 @@
# frozen_string_literal: true
module WPScan
# Media
class Media < InterestingFinding
module Model
# Media
class Media < InterestingFinding
end
end
end

View File

@@ -1,25 +1,38 @@
# frozen_string_literal: true
module WPScan
# WordPress Plugin
class Plugin < WpItem
# See WpItem
def initialize(slug, blog, opts = {})
super(slug, blog, opts)
module Model
# WordPress Plugin
class Plugin < WpItem
# See WpItem
def initialize(slug, blog, opts = {})
super(slug, blog, opts)
@uri = Addressable::URI.parse(blog.url("wp-content/plugins/#{slug}/"))
end
# To be used by #head_and_get
# If custom wp-content, it will be replaced by blog#url
@path_from_blog = "wp-content/plugins/#{slug}/"
# @return [ JSON ]
def db_data
DB::Plugin.db_data(slug)
end
@uri = Addressable::URI.parse(blog.url(path_from_blog))
end
# @param [ Hash ] opts
#
# @return [ WPScan::Version, false ]
def version(opts = {})
@version = Finders::PluginVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
# @return [ JSON ]
def db_data
@db_data ||= DB::Plugin.db_data(slug)
end
@version
# @param [ Hash ] opts
#
# @return [ Model::Version, false ]
def version(opts = {})
@version = Finders::PluginVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
@version
end
# @return [ Array<String> ]
def potential_readme_filenames
@potential_readme_filenames ||= [*(DB::DynamicFinders::Plugin.df_data.dig(slug, 'Readme', 'path') || super)]
end
end
end
end

View File

@@ -1,99 +1,107 @@
# frozen_string_literal: true
module WPScan
# WordPress Theme
class Theme < WpItem
attr_reader :style_url, :style_name, :style_uri, :author, :author_uri, :template, :description,
:license, :license_uri, :tags, :text_domain
module Model
# WordPress Theme
class Theme < WpItem
attr_reader :style_url, :style_name, :style_uri, :author, :author_uri, :template, :description,
:license, :license_uri, :tags, :text_domain
# See WpItem
def initialize(slug, blog, opts = {})
super(slug, blog, opts)
# See WpItem
def initialize(slug, blog, opts = {})
super(slug, blog, opts)
@uri = Addressable::URI.parse(blog.url("wp-content/themes/#{slug}/"))
@style_url = opts[:style_url] || url('style.css')
# To be used by #head_and_get
# If custom wp-content, it will be replaced by blog#url
@path_from_blog = "wp-content/themes/#{slug}/"
parse_style
end
@uri = Addressable::URI.parse(blog.url(path_from_blog))
@style_url = opts[:style_url] || url('style.css')
# @return [ JSON ]
def db_data
DB::Theme.db_data(slug)
end
# @param [ Hash ] opts
#
# @return [ WPScan::Version, false ]
def version(opts = {})
@version = Finders::ThemeVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
@version
end
# @return [ Theme ]
def parent_theme
return unless template
return unless style_body =~ /^@import\surl\(["']?([^"'\)]+)["']?\);\s*$/i
opts = detection_opts.merge(
style_url: url(Regexp.last_match[1]),
found_by: 'Parent Themes (Passive Detection)',
confidence: 100
).merge(version_detection: version_detection_opts)
self.class.new(template, blog, opts)
end
# @param [ Integer ] depth
#
# @retun [ Array<Theme> ]
def parent_themes(depth = 3)
theme = self
found = []
(1..depth).each do |_|
parent = theme.parent_theme
break unless parent
found << parent
theme = parent
parse_style
end
found
end
def style_body
@style_body ||= Browser.get(style_url).body
end
def parse_style
{
style_name: 'Theme Name',
style_uri: 'Theme URI',
author: 'Author',
author_uri: 'Author URI',
template: 'Template',
description: 'Description',
license: 'License',
license_uri: 'License URI',
tags: 'Tags',
text_domain: 'Text Domain'
}.each do |attribute, tag|
instance_variable_set(:"@#{attribute}", parse_style_tag(style_body, tag))
# @return [ JSON ]
def db_data
@db_data ||= DB::Theme.db_data(slug)
end
end
# @param [ String ] bofy
# @param [ String ] tag
#
# @return [ String ]
def parse_style_tag(body, tag)
value = body[/^\s*#{Regexp.escape(tag)}:[\t ]*([^\r\n]+)/i, 1]
# @param [ Hash ] opts
#
# @return [ Model::Version, false ]
def version(opts = {})
@version = Finders::ThemeVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
value && !value.strip.empty? ? value.strip : nil
end
@version
end
def ==(other)
super(other) && style_url == other.style_url
# @return [ Theme ]
def parent_theme
return unless template
return unless style_body =~ /^@import\surl\(["']?([^"'\)]+)["']?\);\s*$/i
opts = detection_opts.merge(
style_url: url(Regexp.last_match[1]),
found_by: 'Parent Themes (Passive Detection)',
confidence: 100
).merge(version_detection: version_detection_opts)
self.class.new(template, blog, opts)
end
# @param [ Integer ] depth
#
# @retun [ Array<Theme> ]
def parent_themes(depth = 3)
theme = self
found = []
(1..depth).each do |_|
parent = theme.parent_theme
break unless parent
found << parent
theme = parent
end
found
end
def style_body
@style_body ||= Browser.get(style_url).body
end
def parse_style
{
style_name: 'Theme Name',
style_uri: 'Theme URI',
author: 'Author',
author_uri: 'Author URI',
template: 'Template',
description: 'Description',
license: 'License',
license_uri: 'License URI',
tags: 'Tags',
text_domain: 'Text Domain'
}.each do |attribute, tag|
instance_variable_set(:"@#{attribute}", parse_style_tag(style_body, tag))
end
end
# @param [ String ] bofy
# @param [ String ] tag
#
# @return [ String ]
def parse_style_tag(body, tag)
value = body[/^\s*#{Regexp.escape(tag)}:[\t ]*([^\r\n]+)/i, 1]
value && !value.strip.empty? ? value.strip : nil
end
def ==(other)
super(other) && style_url == other.style_url
end
end
end
end

View File

@@ -1,71 +1,75 @@
# frozen_string_literal: true
module WPScan
# Timthumb
class Timthumb < InterestingFinding
include Vulnerable
module Model
# Timthumb
class Timthumb < InterestingFinding
include Vulnerable
attr_reader :version_detection_opts
attr_reader :version_detection_opts
# @param [ String ] url
# @param [ Hash ] opts
# @option opts [ Symbol ] :mode The mode to use to detect the version
def initialize(url, opts = {})
super(url, opts)
# @param [ String ] url
# @param [ Hash ] opts
# @option opts [ Symbol ] :mode The mode to use to detect the version
def initialize(url, opts = {})
super(url, opts)
@version_detection_opts = opts[:version_detection] || {}
end
@version_detection_opts = opts[:version_detection] || {}
end
# @param [ Hash ] opts
#
# @return [ WPScan::Version, false ]
def version(opts = {})
@version = Finders::TimthumbVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
# @param [ Hash ] opts
#
# @return [ Model::Version, false ]
def version(opts = {})
@version = Finders::TimthumbVersion::Base.find(self, version_detection_opts.merge(opts)) if @version.nil?
@version
end
@version
end
# @return [ Array<Vulnerability> ]
def vulnerabilities
vulns = []
# @return [ Array<Vulnerability> ]
def vulnerabilities
vulns = []
vulns << rce_webshot_vuln if version == false || version > '1.35' && version < '2.8.14' && webshot_enabled?
vulns << rce_132_vuln if version == false || version < '1.33'
vulns << rce_webshot_vuln if version == false || version > '1.35' && version < '2.8.14' && webshot_enabled?
vulns << rce_132_vuln if version == false || version < '1.33'
vulns
end
vulns
end
# @return [ Vulnerability ] The RCE in the <= 1.32
def rce_132_vuln
Vulnerability.new(
'Timthumb <= 1.32 Remote Code Execution',
{ exploitdb: ['17602'] },
'RCE',
'1.33'
)
end
# @return [ Vulnerability ] The RCE in the <= 1.32
def rce_132_vuln
Vulnerability.new(
'Timthumb <= 1.32 Remote Code Execution',
{ exploitdb: ['17602'] },
'RCE',
'1.33'
)
end
# @return [ Vulnerability ] The RCE due to the WebShot in the > 1.35 (or >= 2.0) and <= 2.8.13
def rce_webshot_vuln
Vulnerability.new(
'Timthumb <= 2.8.13 WebShot Remote Code Execution',
{
url: ['http://seclists.org/fulldisclosure/2014/Jun/117', 'https://github.com/wpscanteam/wpscan/issues/519'],
cve: '2014-4663'
},
'RCE',
'2.8.14'
)
end
# @return [ Vulnerability ] The RCE due to the WebShot in the > 1.35 (or >= 2.0) and <= 2.8.13
def rce_webshot_vuln
Vulnerability.new(
'Timthumb <= 2.8.13 WebShot Remote Code Execution',
{
url: ['http://seclists.org/fulldisclosure/2014/Jun/117', 'https://github.com/wpscanteam/wpscan/issues/519'],
cve: '2014-4663'
},
'RCE',
'2.8.14'
)
end
# @return [ Boolean ]
def webshot_enabled?
res = Browser.get(url, params: { webshot: 1, src: "http://#{default_allowed_domains.sample}" })
# @return [ Boolean ]
def webshot_enabled?
res = Browser.get(url, params: { webshot: 1, src: "http://#{default_allowed_domains.sample}" })
res.body =~ /WEBSHOT_ENABLED == true/ ? false : true
end
/WEBSHOT_ENABLED == true/.match?(res.body) ? false : true
end
# @return [ Array<String> ] The default allowed domains (between the 2.0 and 2.8.13)
def default_allowed_domains
%w[flickr.com picasa.com img.youtube.com upload.wikimedia.org]
# @return [ Array<String> ] The default allowed domains (between the 2.0 and 2.8.13)
def default_allowed_domains
%w[flickr.com picasa.com img.youtube.com upload.wikimedia.org]
end
end
end
end

View File

@@ -1,158 +1,175 @@
# frozen_string_literal: true
module WPScan
# WpItem (superclass of Plugin & Theme)
class WpItem
include Vulnerable
include Finders::Finding
include CMSScanner::Target::Platform::PHP
include CMSScanner::Target::Server::Generic
module Model
# WpItem (superclass of Plugin & Theme)
class WpItem
include Vulnerable
include Finders::Finding
include CMSScanner::Target::Platform::PHP
include CMSScanner::Target::Server::Generic
READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
CHANGELOGS = %w[changelog.txt CHANGELOG.md changelog.md].freeze
# Most common readme filenames, based on checking all public plugins and themes.
READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
attr_reader :uri, :slug, :detection_opts, :version_detection_opts, :blog, :db_data
attr_reader :uri, :slug, :detection_opts, :version_detection_opts, :blog, :path_from_blog, :db_data
delegate :homepage_res, :xpath_pattern_from_page, :in_scope_urls, to: :blog
delegate :homepage_res, :xpath_pattern_from_page, :in_scope_uris, :head_or_get_params, to: :blog
# @param [ String ] slug The plugin/theme slug
# @param [ Target ] blog The targeted blog
# @param [ Hash ] opts
# @option opts [ Symbol ] :mode The detection mode to use
# @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)
@blog = blog
@uri = Addressable::URI.parse(opts[:url]) if opts[:url]
# @param [ String ] slug The plugin/theme slug
# @param [ Target ] blog The targeted blog
# @param [ Hash ] opts
# @option opts [ Symbol ] :mode The detection mode to use
# @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)
@blog = blog
@uri = Addressable::URI.parse(opts[:url]) if opts[:url]
@detection_opts = { mode: opts[:mode] }
@version_detection_opts = opts[:version_detection] || {}
@detection_opts = { mode: opts[:mode] }
@version_detection_opts = opts[:version_detection] || {}
parse_finding_options(opts)
end
# @return [ Array<Vulnerabily> ]
def vulnerabilities
return @vulnerabilities if @vulnerabilities
@vulnerabilities = []
[*db_data['vulnerabilities']].each do |json_vuln|
vulnerability = Vulnerability.load_from_json(json_vuln)
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
parse_finding_options(opts)
end
@vulnerabilities
end
# @return [ Array<Vulnerabily> ]
def vulnerabilities
return @vulnerabilities if @vulnerabilities
# Checks if the wp_item is vulnerable to a specific vulnerability
#
# @param [ Vulnerability ] vuln Vulnerability to check the item against
#
# @return [ Boolean ]
def vulnerable_to?(vuln)
return true unless version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
@vulnerabilities = []
version < vuln.fixed_in
end
# @return [ String ]
def latest_version
@latest_version ||= db_data['latest_version'] ? WPScan::Version.new(db_data['latest_version']) : nil
end
# Not used anywhere ATM
# @return [ Boolean ]
def popular?
@popular ||= db_data['popular']
end
# @return [ String ]
def last_updated
@last_updated ||= db_data['last_updated']
end
# @return [ Boolean ]
def outdated?
@outdated ||= if version && latest_version
version < latest_version
else
false
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 ]
def url(path = nil)
return unless @uri
return @uri.to_s unless path
@uri.join(URI.encode(path)).to_s
end
# @return [ Boolean ]
def ==(other)
self.class == other.class && slug == other.slug
end
def to_s
slug
end
# @return [ Symbol ] The Class symbol associated to the item
def classify
@classify ||= classify_slug(slug)
end
# @return [ String ] The readme url if found
def readme_url
return if detection_opts[:mode] == :passive
if @readme_url.nil?
READMES.each do |path|
return @readme_url = url(path) if Browser.get(url(path)).code == 200
[*db_data['vulnerabilities']].each do |json_vuln|
vulnerability = Vulnerability.load_from_json(json_vuln)
@vulnerabilities << vulnerability if vulnerable_to?(vulnerability)
end
@vulnerabilities
end
@readme_url
end
# Checks if the wp_item is vulnerable to a specific vulnerability
#
# @param [ Vulnerability ] vuln Vulnerability to check the item against
#
# @return [ Boolean ]
def vulnerable_to?(vuln)
return true unless version && vuln && vuln.fixed_in && !vuln.fixed_in.empty?
# @return [ String, false ] The changelog urr if found
def changelog_url
return if detection_opts[:mode] == :passive
version < vuln.fixed_in
end
if @changelog_url.nil?
CHANGELOGS.each do |path|
return @changelog_url = url(path) if Browser.get(url(path)).code == 200
# @return [ String ]
def latest_version
@latest_version ||= db_data['latest_version'] ? Model::Version.new(db_data['latest_version']) : nil
end
# Not used anywhere ATM
# @return [ Boolean ]
def popular?
@popular ||= db_data['popular']
end
# @return [ String ]
def last_updated
@last_updated ||= db_data['last_updated']
end
# @return [ Boolean ]
def outdated?
@outdated ||= if version && latest_version
version < latest_version
else
false
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 ]
def url(path = nil)
return unless @uri
return @uri.to_s unless path
@uri.join(URI.encode(path)).to_s
end
# @return [ Boolean ]
def ==(other)
self.class == other.class && slug == other.slug
end
def to_s
slug
end
# @return [ Symbol ] The Class symbol associated to the item
def classify
@classify ||= classify_slug(slug)
end
# @return [ String, False ] The readme url if found, false otherwise
def readme_url
return if detection_opts[:mode] == :passive
return @readme_url unless @readme_url.nil?
potential_readme_filenames.each do |path|
t_url = url(path)
return @readme_url = t_url if Browser.forge_request(t_url, blog.head_or_get_params).run.code == 200
end
@readme_url = false
end
@changelog_url
end
def potential_readme_filenames
@potential_readme_filenames ||= READMES
end
# @param [ String ] path
# @param [ Hash ] params The request params
#
# @return [ Boolean ]
def directory_listing?(path = nil, params = {})
return if detection_opts[:mode] == :passive
# @param [ String ] path
# @param [ Hash ] params The request params
#
# @return [ Boolean ]
def directory_listing?(path = nil, params = {})
return if detection_opts[:mode] == :passive
super(path, params)
end
super(path, params)
end
# @param [ String ] path
# @param [ Hash ] params The request params
#
# @return [ Boolean ]
def error_log?(path = 'error_log', params = {})
return if detection_opts[:mode] == :passive
# @param [ String ] path
# @param [ Hash ] params The request params
#
# @return [ Boolean ]
def error_log?(path = 'error_log', params = {})
return if detection_opts[:mode] == :passive
super(path, params)
super(path, params)
end
# See CMSScanner::Target#head_and_get
#
# This is used by the error_log? above in the super()
# to have the correct path (ie readme.txt checked from the plugin/theme location
# and not from the blog root). Could also be used in finders
#
# @param [ String ] path
# @param [ Array<String> ] codes
# @param [ Hash ] params The requests params
# @option params [ Hash ] :head Request params for the HEAD
# @option params [ hash ] :get Request params for the GET
#
# @return [ Typhoeus::Response ]
def head_and_get(path, codes = [200], params = {})
final_path = +@path_from_blog
final_path << URI.encode(path) unless path.nil?
blog.head_and_get(final_path, codes, params)
end
end
end
end

View File

@@ -1,64 +1,67 @@
# frozen_string_literal: true
module WPScan
# WP Version
class WpVersion < CMSScanner::Version
include Vulnerable
module Model
# WP Version
class WpVersion < CMSScanner::Model::Version
include Vulnerable
def initialize(number, opts = {})
raise InvalidWordPressVersion unless WpVersion.valid?(number.to_s)
def initialize(number, opts = {})
raise Error::InvalidWordPressVersion unless WpVersion.valid?(number.to_s)
super(number, opts)
end
super(number, opts)
end
# @param [ String ] number
#
# @return [ Boolean ] true if the number is a valid WP version, false otherwise
def self.valid?(number)
all.include?(number)
end
# @param [ String ] number
#
# @return [ Boolean ] true if the number is a valid WP version, false otherwise
def self.valid?(number)
all.include?(number)
end
# @return [ Array<String> ] All the version numbers
def self.all
return @all_numbers if @all_numbers
# @return [ Array<String> ] All the version numbers
def self.all
return @all_numbers if @all_numbers
@all_numbers = []
@all_numbers = []
DB::Fingerprints.wp_fingerprints.each_value do |fp|
fp.each_value do |versions|
versions.each do |version|
@all_numbers << version unless @all_numbers.include?(version)
end
DB::Fingerprints.wp_fingerprints.each_value do |fp|
@all_numbers << fp.values
end
# @all_numbers.flatten.uniq.sort! {} doesn't produce the same result here.
@all_numbers.flatten!
@all_numbers.uniq!
@all_numbers.sort! { |a, b| Gem::Version.new(b) <=> Gem::Version.new(a) }
end
@all_numbers.sort! { |a, b| Gem::Version.new(b) <=> Gem::Version.new(a) }
end
# @return [ JSON ]
def db_data
DB::Version.db_data(number)
end
# @return [ Array<Vulnerability> ]
def vulnerabilities
return @vulnerabilities if @vulnerabilities
@vulnerabilities = []
[*db_data['vulnerabilities']].each do |json_vuln|
@vulnerabilities << Vulnerability.load_from_json(json_vuln)
# @return [ JSON ]
def db_data
@db_data ||= DB::Version.db_data(number)
end
@vulnerabilities
end
# @return [ Array<Vulnerability> ]
def vulnerabilities
return @vulnerabilities if @vulnerabilities
# @return [ String ]
def release_date
@release_date ||= db_data['release_date'] || 'Unknown'
end
@vulnerabilities = []
# @return [ String ]
def status
@status ||= db_data['status'] || 'Unknown'
[*db_data['vulnerabilities']].each do |json_vuln|
@vulnerabilities << Vulnerability.load_from_json(json_vuln)
end
@vulnerabilities
end
# @return [ String ]
def release_date
@release_date ||= db_data['release_date'] || 'Unknown'
end
# @return [ String ]
def status
@status ||= db_data['status'] || 'Unknown'
end
end
end
end

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