wpscan 3.8.28 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +104 -30
  3. data/app/app.rb +26 -0
  4. data/app/controllers/aliases.rb +2 -2
  5. data/app/controllers/authenticated_inventory.rb +43 -0
  6. data/app/controllers/core/cli_options.rb +151 -0
  7. data/app/controllers/core.rb +200 -25
  8. data/app/controllers/custom_directories.rb +1 -1
  9. data/app/controllers/enumeration/cli_options.rb +21 -31
  10. data/app/controllers/enumeration/enum_methods.rb +145 -38
  11. data/app/controllers/enumeration.rb +26 -3
  12. data/app/controllers/interesting_findings.rb +25 -0
  13. data/app/controllers/main_theme.rb +1 -1
  14. data/app/controllers/password_attack.rb +14 -6
  15. data/app/controllers/vuln_api.rb +9 -3
  16. data/app/controllers/wp_version.rb +1 -1
  17. data/app/controllers.rb +1 -0
  18. data/app/finders/backup_folders/known_locations.rb +66 -0
  19. data/app/finders/backup_folders.rb +19 -0
  20. data/app/finders/config_backups/known_filenames.rb +6 -4
  21. data/app/finders/config_backups.rb +1 -1
  22. data/app/finders/db_exports/known_locations.rb +16 -14
  23. data/app/finders/db_exports.rb +1 -1
  24. data/app/finders/interesting_findings/backup_db.rb +1 -1
  25. data/app/finders/interesting_findings/debug_log.rb +1 -1
  26. data/app/finders/interesting_findings/duplicator_installer_log.rb +1 -1
  27. data/app/finders/interesting_findings/emergency_pwd_reset_script.rb +1 -1
  28. data/app/finders/interesting_findings/fantastico_fileslist.rb +21 -0
  29. data/app/finders/interesting_findings/full_path_disclosure.rb +1 -1
  30. data/app/finders/interesting_findings/headers.rb +17 -0
  31. data/app/finders/interesting_findings/mu_plugins.rb +1 -1
  32. data/app/finders/interesting_findings/multisite.rb +1 -1
  33. data/app/finders/interesting_findings/php_disabled.rb +2 -2
  34. data/app/finders/interesting_findings/readme.rb +1 -1
  35. data/app/finders/interesting_findings/registration.rb +1 -1
  36. data/app/finders/interesting_findings/robots_txt.rb +20 -0
  37. data/app/finders/interesting_findings/search_replace_db_2.rb +19 -0
  38. data/app/finders/interesting_findings/tmm_db_migrate.rb +1 -1
  39. data/app/finders/interesting_findings/upload_directory_listing.rb +1 -1
  40. data/app/finders/interesting_findings/upload_sql_dump.rb +2 -2
  41. data/app/finders/interesting_findings/wp_cron.rb +1 -1
  42. data/app/finders/interesting_findings/xml_rpc.rb +61 -0
  43. data/app/finders/interesting_findings.rb +13 -4
  44. data/app/finders/main_theme/css_style_in_homepage.rb +1 -1
  45. data/app/finders/main_theme/urls_in_homepage.rb +3 -7
  46. data/app/finders/main_theme/woo_framework_meta_generator.rb +4 -4
  47. data/app/finders/main_theme.rb +1 -1
  48. data/app/finders/medias/attachment_brute_forcing.rb +2 -2
  49. data/app/finders/medias.rb +1 -1
  50. data/app/finders/passwords/wp_login.rb +2 -2
  51. data/app/finders/passwords/xml_rpc.rb +2 -2
  52. data/app/finders/passwords/xml_rpc_multicall.rb +1 -1
  53. data/app/finders/plugin_version/readme.rb +1 -1
  54. data/app/finders/plugin_version.rb +1 -1
  55. data/app/finders/plugins/known_locations.rb +17 -7
  56. data/app/finders/plugins/urls_in_homepage.rb +3 -7
  57. data/app/finders/plugins/wp_json_api.rb +85 -0
  58. data/app/finders/plugins.rb +2 -1
  59. data/app/finders/theme_version/style.rb +1 -1
  60. data/app/finders/theme_version/woo_framework_meta_generator.rb +1 -1
  61. data/app/finders/theme_version.rb +1 -1
  62. data/app/finders/themes/known_locations.rb +12 -6
  63. data/app/finders/themes/urls_in_homepage.rb +3 -7
  64. data/app/finders/themes/wp_json_api.rb +74 -0
  65. data/app/finders/themes.rb +2 -1
  66. data/app/finders/timthumb_version/bad_request.rb +1 -1
  67. data/app/finders/timthumb_version.rb +1 -1
  68. data/app/finders/timthumbs/known_locations.rb +6 -4
  69. data/app/finders/timthumbs.rb +1 -1
  70. data/app/finders/users/author_id_brute_forcing.rb +11 -7
  71. data/app/finders/users/author_posts.rb +1 -1
  72. data/app/finders/users/author_sitemap.rb +1 -1
  73. data/app/finders/users/login_error_messages.rb +1 -1
  74. data/app/finders/users/oembed_api.rb +3 -1
  75. data/app/finders/users/wp_json_api.rb +11 -7
  76. data/app/finders/users.rb +1 -1
  77. data/app/finders/wp_version/atom_generator.rb +1 -1
  78. data/app/finders/wp_version/rdf_generator.rb +1 -1
  79. data/app/finders/wp_version/readme.rb +1 -1
  80. data/app/finders/wp_version/rss_generator.rb +1 -1
  81. data/app/finders/wp_version/unique_fingerprinting.rb +2 -2
  82. data/app/finders/wp_version.rb +1 -1
  83. data/app/finders.rb +1 -0
  84. data/app/formatters/cli.rb +79 -0
  85. data/app/formatters/cli_no_color.rb +9 -0
  86. data/app/formatters/cli_no_colour.rb +17 -0
  87. data/app/formatters/json.rb +14 -0
  88. data/app/formatters/jsonl.rb +29 -0
  89. data/app/formatters/sarif.rb +311 -0
  90. data/app/models/backup_folder.rb +39 -0
  91. data/app/models/fantastico_fileslist.rb +34 -0
  92. data/app/models/headers.rb +44 -0
  93. data/app/models/interesting_finding.rb +41 -2
  94. data/app/models/plugin.rb +8 -2
  95. data/app/models/robots_txt.rb +31 -0
  96. data/app/models/search_replace_db_2.rb +17 -0
  97. data/app/models/theme.rb +9 -2
  98. data/app/models/timthumb.rb +2 -2
  99. data/app/models/user.rb +35 -0
  100. data/app/models/version.rb +49 -0
  101. data/app/models/wp_item/wordpress_org_data.rb +55 -0
  102. data/app/models/wp_item.rb +109 -9
  103. data/app/models/wp_version.rb +2 -2
  104. data/app/models/xml_rpc.rb +73 -3
  105. data/app/models.rb +2 -1
  106. data/app/user_agents.txt +46 -0
  107. data/app/views/cli/core/banner.erb +3 -3
  108. data/app/views/cli/core/finished.erb +15 -0
  109. data/app/views/cli/core/help.erb +4 -0
  110. data/app/views/cli/core/started.erb +11 -0
  111. data/app/views/cli/enumeration/backup_folders.erb +11 -0
  112. data/app/views/cli/enumeration/plugin.erb +13 -0
  113. data/app/views/cli/enumeration/plugins.erb +1 -12
  114. data/app/views/cli/enumeration/theme.erb +4 -0
  115. data/app/views/cli/enumeration/themes.erb +1 -3
  116. data/app/views/cli/enumeration/user.erb +4 -0
  117. data/app/views/cli/enumeration/users.erb +1 -3
  118. data/app/views/cli/finding.erb +1 -1
  119. data/app/views/cli/interesting_findings/_array.erb +10 -0
  120. data/app/views/cli/interesting_findings/findings.erb +23 -0
  121. data/app/views/cli/scan_aborted.erb +5 -0
  122. data/app/views/cli/update_aborted.erb +5 -0
  123. data/app/views/cli/vuln_api/status.erb +2 -0
  124. data/app/views/cli/vulnerability.erb +6 -0
  125. data/app/views/cli/wp_item.erb +4 -1
  126. data/app/views/json/core/banner.erb +2 -8
  127. data/app/views/json/core/finished.erb +13 -0
  128. data/app/views/json/core/help.erb +4 -0
  129. data/app/views/json/core/started.erb +10 -0
  130. data/app/views/json/enumeration/backup_folders.erb +11 -0
  131. data/app/views/json/enumeration/plugin.erb +15 -0
  132. data/app/views/json/enumeration/theme.erb +5 -0
  133. data/app/views/json/enumeration/user.erb +6 -0
  134. data/app/views/json/finding.erb +8 -2
  135. data/app/views/json/interesting_findings/findings.erb +24 -0
  136. data/app/views/json/notice.erb +1 -0
  137. data/app/views/json/scan_aborted.erb +5 -0
  138. data/app/views/json/update_aborted.erb +5 -0
  139. data/app/views/json/vuln_api/status.erb +2 -0
  140. data/app/views/json/wp_item.erb +4 -1
  141. data/bin/wpscan +1 -0
  142. data/lib/opt_parse_validator/config_files_loader_merger/base.rb +26 -0
  143. data/lib/opt_parse_validator/config_files_loader_merger/json.rb +17 -0
  144. data/lib/opt_parse_validator/config_files_loader_merger/yml.rb +17 -0
  145. data/lib/opt_parse_validator/config_files_loader_merger.rb +62 -0
  146. data/lib/opt_parse_validator/errors.rb +9 -0
  147. data/lib/opt_parse_validator/hacks.rb +19 -0
  148. data/lib/opt_parse_validator/opts/alias.rb +28 -0
  149. data/lib/opt_parse_validator/opts/array.rb +34 -0
  150. data/lib/opt_parse_validator/opts/base.rb +142 -0
  151. data/lib/opt_parse_validator/opts/boolean.rb +19 -0
  152. data/lib/opt_parse_validator/opts/choice.rb +43 -0
  153. data/lib/opt_parse_validator/opts/credentials.rb +15 -0
  154. data/lib/opt_parse_validator/opts/directory_path.rb +17 -0
  155. data/lib/opt_parse_validator/opts/file_path.rb +34 -0
  156. data/lib/opt_parse_validator/opts/headers.rb +33 -0
  157. data/lib/opt_parse_validator/opts/integer.rb +15 -0
  158. data/lib/opt_parse_validator/opts/integer_range.rb +37 -0
  159. data/lib/opt_parse_validator/opts/multi_choices.rb +135 -0
  160. data/lib/opt_parse_validator/opts/path.rb +78 -0
  161. data/lib/opt_parse_validator/opts/positive_integer.rb +16 -0
  162. data/lib/opt_parse_validator/opts/proxy.rb +7 -0
  163. data/lib/opt_parse_validator/opts/regexp.rb +14 -0
  164. data/lib/opt_parse_validator/opts/smart_list.rb +30 -0
  165. data/lib/opt_parse_validator/opts/string.rb +8 -0
  166. data/lib/opt_parse_validator/opts/uri.rb +41 -0
  167. data/lib/opt_parse_validator/opts/url.rb +11 -0
  168. data/lib/opt_parse_validator/opts.rb +9 -0
  169. data/lib/opt_parse_validator/version.rb +6 -0
  170. data/lib/opt_parse_validator.rb +161 -0
  171. data/lib/wpscan/browser/actions.rb +48 -0
  172. data/lib/wpscan/browser/options.rb +92 -0
  173. data/lib/wpscan/browser.rb +87 -2
  174. data/lib/wpscan/browser_authenticator.rb +64 -0
  175. data/lib/wpscan/cache/file_store.rb +77 -0
  176. data/lib/wpscan/cache/typhoeus.rb +25 -0
  177. data/lib/wpscan/controller.rb +100 -4
  178. data/lib/wpscan/controllers.rb +78 -3
  179. data/lib/wpscan/db/dynamic_finders/base.rb +3 -7
  180. data/lib/wpscan/db/dynamic_finders/plugin.rb +2 -2
  181. data/lib/wpscan/db/dynamic_finders/wordpress.rb +1 -1
  182. data/lib/wpscan/db/fingerprints.rb +2 -2
  183. data/lib/wpscan/db/updater.rb +23 -13
  184. data/lib/wpscan/db/vuln_api.rb +19 -7
  185. data/lib/wpscan/db/wp_item.rb +2 -2
  186. data/lib/wpscan/errors/enumeration.rb +4 -4
  187. data/lib/wpscan/errors/http.rb +82 -3
  188. data/lib/wpscan/errors/saml.rb +28 -0
  189. data/lib/wpscan/errors/scan.rb +14 -0
  190. data/lib/wpscan/errors/update.rb +11 -3
  191. data/lib/wpscan/errors/vuln_api.rb +24 -0
  192. data/lib/wpscan/errors/wordpress.rb +2 -2
  193. data/lib/wpscan/errors/wp_auth.rb +37 -0
  194. data/lib/wpscan/errors.rb +4 -3
  195. data/lib/wpscan/exit_code.rb +25 -0
  196. data/lib/wpscan/finders/base_finders.rb +45 -0
  197. data/lib/wpscan/finders/dynamic_finder/finder.rb +1 -1
  198. data/lib/wpscan/finders/dynamic_finder/version/body_pattern.rb +1 -1
  199. data/lib/wpscan/finders/dynamic_finder/version/comment.rb +1 -1
  200. data/lib/wpscan/finders/dynamic_finder/version/header_pattern.rb +1 -1
  201. data/lib/wpscan/finders/dynamic_finder/version/javascript_var.rb +1 -1
  202. data/lib/wpscan/finders/dynamic_finder/version/query_parameter.rb +3 -5
  203. data/lib/wpscan/finders/dynamic_finder/version/xpath.rb +1 -1
  204. data/lib/wpscan/finders/dynamic_finder/wp_items/finder.rb +3 -3
  205. data/lib/wpscan/finders/dynamic_finder/wp_version.rb +1 -1
  206. data/lib/wpscan/finders/finder/breadth_first_dictionary_attack.rb +257 -0
  207. data/lib/wpscan/finders/finder/enumerator.rb +77 -0
  208. data/lib/wpscan/finders/finder/fingerprinter.rb +48 -0
  209. data/lib/wpscan/finders/finder/smart_url_checker/findings.rb +33 -0
  210. data/lib/wpscan/finders/finder/smart_url_checker.rb +60 -0
  211. data/lib/wpscan/finders/finder/wp_version/smart_url_checker.rb +1 -1
  212. data/lib/wpscan/finders/finder.rb +78 -0
  213. data/lib/wpscan/finders/finding.rb +54 -0
  214. data/lib/wpscan/finders/findings.rb +33 -0
  215. data/lib/wpscan/finders/independent_finder.rb +33 -0
  216. data/lib/wpscan/finders/independent_finders.rb +26 -0
  217. data/lib/wpscan/finders/same_type_finder.rb +19 -0
  218. data/lib/wpscan/finders/same_type_finders.rb +28 -0
  219. data/lib/wpscan/finders/unique_finder.rb +19 -0
  220. data/lib/wpscan/finders/unique_finders.rb +47 -0
  221. data/lib/wpscan/finders.rb +11 -12
  222. data/lib/wpscan/formatter/buffer.rb +17 -0
  223. data/lib/wpscan/formatter.rb +152 -0
  224. data/lib/wpscan/helper.rb +7 -1
  225. data/lib/wpscan/http_status_tracking.rb +128 -0
  226. data/lib/wpscan/numeric.rb +13 -0
  227. data/lib/wpscan/parsed_cli.rb +31 -2
  228. data/lib/wpscan/progressbar_null_output.rb +23 -0
  229. data/lib/wpscan/public_suffix/domain.rb +44 -0
  230. data/lib/wpscan/references.rb +118 -4
  231. data/lib/wpscan/scan.rb +127 -0
  232. data/lib/wpscan/target/hashes.rb +45 -0
  233. data/lib/wpscan/target/platform/php.rb +124 -0
  234. data/lib/wpscan/target/platform/wordpress/custom_directories.rb +3 -3
  235. data/lib/wpscan/target/platform/wordpress.rb +7 -8
  236. data/lib/wpscan/target/platform.rb +3 -0
  237. data/lib/wpscan/target/scope.rb +103 -0
  238. data/lib/wpscan/target/server/apache.rb +27 -0
  239. data/lib/wpscan/target/server/generic.rb +72 -0
  240. data/lib/wpscan/target/server/iis.rb +29 -0
  241. data/lib/wpscan/target/server/nginx.rb +27 -0
  242. data/lib/wpscan/target/server.rb +6 -0
  243. data/lib/wpscan/target.rb +129 -9
  244. data/lib/wpscan/typhoeus/hydra.rb +12 -0
  245. data/lib/wpscan/typhoeus/response.rb +24 -1
  246. data/lib/wpscan/version.rb +1 -1
  247. data/lib/wpscan/vulnerability.rb +49 -3
  248. data/lib/wpscan/vulnerability_filter.rb +68 -0
  249. data/lib/wpscan/vulnerable.rb +13 -1
  250. data/lib/wpscan/web_site.rb +152 -0
  251. data/lib/wpscan.rb +126 -29
  252. metadata +362 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a16ee5b06248c761525128f4b8f863aab9033aadf4c0889bd5d86e83ecc5800
4
- data.tar.gz: 901bb851ff149164f979ece4584a3ba6123f28368fc9e0ec33de1a662e5e3a24
3
+ metadata.gz: 4a35937f16511c0e2d5fe332f45489703704f91e2d38d48b46d4f42fb5fe153f
4
+ data.tar.gz: 2ced1bd5e24ce8d891d298d383c9a79b03d6d05fd614de60da7cf46808138a62
5
5
  SHA512:
6
- metadata.gz: 408795d09c4ab77f9d080f89d2fa952a1c27e65b54f707f81efed9a7c37c02387144ed88205688d44b224c3e69101c86a70b83bfcd2af42119957f07443148c6
7
- data.tar.gz: b4290ac6cb2825dbe35291472f7e410a511394b495589a33f59b1ab54a8b275d3adb2a2da08372510b7914c14297f1a4e48638da0c16c9248ce9c48e35f47f0c
6
+ metadata.gz: 2ebbec3ada4d857b9be598636dbeaa65d524f3be1733204920f8fe23d03bf8ef6e3a9e446a3dbbfabda70d6da41562c7bb28115d5133497b002ad69ec6464731
7
+ data.tar.gz: d909b0d2ee28f28fb698c07301a4d42c6118cc6e4d7245b05e0ba329bc90a8a52f2af189c10dc437a7973db5ff4afd46604e5ce0e3889bdb6efdaaeccc879ed3
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <a href="https://wpscan.com/">
3
- <img src="https://raw.githubusercontent.com/wpscanteam/wpscan/gh-pages/images/wpscan_logo.png" alt="WPScan logo">
3
+ <img src="https://raw.githubusercontent.com/wpscanteam/wpscan/5af15af5e7d67ee7c6e5e4ebcf75c1dfabfa123b/images/wpscan_logo.png" alt="WPScan logo">
4
4
  </a>
5
5
  </p>
6
6
 
@@ -17,19 +17,20 @@
17
17
  <a href="https://badge.fury.io/rb/wpscan" target="_blank"><img src="https://badge.fury.io/rb/wpscan.svg"></a>
18
18
  <a href="https://hub.docker.com/r/wpscanteam/wpscan/" target="_blank"><img src="https://img.shields.io/docker/pulls/wpscanteam/wpscan.svg"></a>
19
19
  <a href="https://github.com/wpscanteam/wpscan/actions?query=workflow%3ABuild" target="_blank"><img src="https://github.com/wpscanteam/wpscan/workflows/Build/badge.svg"></a>
20
- <a href="https://codeclimate.com/github/wpscanteam/wpscan" target="_blank"><img src="https://codeclimate.com/github/wpscanteam/wpscan/badges/gpa.svg"></a>
20
+ <a href="https://qlty.sh/gh/wpscanteam/projects/wpscan" target="_blank"><img src="https://qlty.sh/gh/wpscanteam/projects/wpscan/maintainability.svg" alt="Maintainability"></a>
21
+ <a href="https://coveralls.io/github/wpscanteam/wpscan?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/wpscanteam/wpscan/badge.svg?branch=master" alt="Coverage Status"></a>
21
22
  </p>
22
23
 
23
24
  # INSTALL
24
25
 
25
26
  ## Prerequisites
26
27
 
27
- - (Optional but highly recommended: [RVM](https://rvm.io/rvm/install))
28
- - Ruby >= 3.0 - Recommended: latest
29
- - Curl >= 7.72 - Recommended: latest
28
+ - (Optional but highly recommended: [rbenv](https://github.com/rbenv/rbenv))
29
+ - Ruby >= 3.3 - Recommended: latest stable
30
+ - Curl >= 7.72 - Recommended: latest stable
30
31
  - The 7.29 has a segfault
31
32
  - The < 7.72 could result in `Stream error in the HTTP/2 framing layer` in some cases
32
- - RubyGems - Recommended: latest
33
+ - RubyGems - Recommended: latest stable
33
34
  - Nokogiri might require packages to be installed via your package manager depending on your OS, see https://nokogiri.org/tutorials/installing_nokogiri.html
34
35
 
35
36
  ### In a Pentesting distribution
@@ -44,11 +45,33 @@ brew install wpscanteam/tap/wpscan
44
45
 
45
46
  ### From RubyGems
46
47
 
48
+ WPScan depends on gems with native extensions (e.g. `yajl-ruby`, `nokogiri`, `ffi`), so a working C toolchain and Ruby development headers must be present before `gem install wpscan`. Without them, the install fails with errors like `Failed to build gem native extension` or `make: x86_64-linux-gnu-gcc: No such file or directory` (see [#1844](https://github.com/wpscanteam/wpscan/issues/1844)).
49
+
50
+ - **Debian / Ubuntu**:
51
+ ```shell
52
+ sudo apt install build-essential ruby-dev
53
+ ```
54
+ - **Fedora / RHEL / CentOS**:
55
+ ```shell
56
+ sudo dnf install @development-tools ruby-devel
57
+ ```
58
+ - **Arch Linux**:
59
+ ```shell
60
+ sudo pacman -S base-devel ruby
61
+ ```
62
+ - **Alpine**:
63
+ ```shell
64
+ sudo apk add build-base ruby-dev
65
+ ```
66
+ - **macOS**: install the Xcode Command Line Tools (`xcode-select --install`).
67
+
68
+ Then install the gem:
69
+
47
70
  ```shell
48
71
  gem install wpscan
49
72
  ```
50
73
 
51
- 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))
74
+ On MacOSX, if a ```Gem::FilePermissionError``` is raised due to 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))
52
75
 
53
76
  # Updating
54
77
 
@@ -63,55 +86,87 @@ Pull the repo with ```docker pull wpscanteam/wpscan```
63
86
  Enumerating usernames
64
87
 
65
88
  ```shell
66
- docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u
89
+ docker run -it --rm -v wpscan-db:/wpscan/.cache/wpscan/db wpscanteam/wpscan --url https://target.tld/ --enumerate u
67
90
  ```
68
91
 
69
92
  Enumerating a range of usernames
70
93
 
71
94
  ```shell
72
- docker run -it --rm wpscanteam/wpscan --url https://target.tld/ --enumerate u1-100
95
+ docker run -it --rm -v wpscan-db:/wpscan/.cache/wpscan/db wpscanteam/wpscan --url https://target.tld/ --enumerate u1-100
73
96
  ```
74
97
 
75
98
  ** replace u1-100 with a range of your choice.
76
99
 
100
+ ## Persisting the local database
101
+
102
+ The image ships with a copy of the local database baked in at build time. Because the example commands above use `--rm`, any database update performed during a run is discarded when the container exits, so the next run starts again from the (potentially stale) baked-in copy.
103
+
104
+ Mounting a named volume at `/wpscan/.cache/wpscan/db` (the `wpscan` user's cache directory inside the container) keeps the database across runs, so `wpscan --update` only re-downloads files whose checksums actually changed and the 5-day staleness prompt behaves as it would for a local install:
105
+
106
+ ```shell
107
+ docker run -it --rm -v wpscan-db:/wpscan/.cache/wpscan/db wpscanteam/wpscan --update
108
+ ```
109
+
110
+ The named volume is created automatically on first use if it doesn't already exist.
111
+
77
112
  # Usage
78
113
 
79
114
  Full user documentation can be found here; https://github.com/wpscanteam/wpscan/wiki/WPScan-User-Documentation
80
115
 
81
- ```wpscan --url blog.tld``` This will scan the blog using default options with a good compromise between speed and accuracy. For example, the plugins will be checked passively but their version with a mixed detection mode (passively + aggressively). Potential config backup files will also be checked, along with other interesting findings.
116
+ ```wpscan --url blog.tld``` This will scan the blog using default options with a good compromise between speed and accuracy. It performs version detection, theme detection, and interesting findings discovery. To enumerate plugins, themes, users, backup folders, etc., use the `-e` option (e.g., `-e ap` for all plugins, `-e vp` for vulnerable plugins, `-e bf` for backup folders).
82
117
 
83
118
  If a more stealthy approach is required, then ```wpscan --stealthy --url blog.tld``` can be used.
84
119
  As a result, when using the ```--enumerate``` option, don't forget to set the ```--plugins-detection``` accordingly, as its default is 'passive'.
85
120
 
86
121
  For more options, open a terminal and type ```wpscan --help``` (if you built wpscan from the source, you should type the command outside of the git repo)
87
122
 
88
- The DB is located at ~/.wpscan/db
123
+ ## Database Location
124
+
125
+ The database location follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html):
126
+
127
+ - **New installations**: `~/.cache/wpscan/db` (or `$XDG_CACHE_HOME/wpscan/db` if set)
128
+ - **Existing installations**: `~/.wpscan/db` (legacy path, maintained for backward compatibility)
129
+
130
+ Runtime files such as the default HTTP cache and cookie jar are stored under `$TMPDIR/wpscan` when
131
+ `$TMPDIR` is set. Otherwise they use the same per-user XDG cache directory, for example
132
+ `~/.cache/wpscan/cache` and `~/.cache/wpscan/cookie_jar.txt`. These defaults can be overridden
133
+ with `--cache-dir` and `--cookie-jar`.
134
+
135
+ To migrate an existing installation to the XDG path:
136
+
137
+ ```shell
138
+ mv ~/.wpscan ~/.cache/wpscan
139
+ ```
89
140
 
90
141
  ## Optional: WordPress Vulnerability Database API
91
142
 
92
- The WPScan CLI tool uses the [WordPress Vulnerability Database API](https://wpscan.com/api) to retrieve WordPress vulnerability data in real time. For WPScan to retrieve the vulnerability data an API token must be supplied via the `--api-token` option, or via a configuration file, as discussed below. An API token can be obtained by registering an account on [WPScan.com](https://wpscan.com/register).
143
+ The WPScan CLI tool uses the [WordPress Vulnerability Database API](https://wpscan.com/api) to retrieve WordPress vulnerability data in real-time. For WPScan to retrieve the vulnerability data an API token must be supplied via the `--api-token` option, or via a configuration file, as discussed below. An API token can be obtained by registering an account on [WPScan.com](https://wpscan.com/register).
93
144
 
94
145
  Up to **25** API requests per day are given free of charge, that should be suitable to scan most WordPress websites at least once per day. When the daily 25 API requests are exhausted, WPScan will continue to work as normal but without any vulnerability data.
95
146
 
96
147
  ### How many API requests do you need?
97
148
 
98
- - Our WordPress scanner makes one API request for the WordPress version, one request per installed plugin and one request per installed theme.
149
+ - Our WordPress scanner makes one API request for the WordPress version, one request per installed plugin, and one request per the installed theme.
99
150
  - On average, a WordPress website has 22 installed plugins.
100
151
 
101
152
  ## Load CLI options from file/s
102
153
 
103
- WPScan can load all options (including the --url) from configuration files, the following locations are checked (order: first to last):
154
+ WPScan can load all options (including the `--url`) from configuration files, the following locations are checked (order: first to last):
104
155
 
105
- - ~/.wpscan/scan.json
106
- - ~/.wpscan/scan.yml
107
- - pwd/.wpscan/scan.json
108
- - pwd/.wpscan/scan.yml
156
+ - `$XDG_CONFIG_HOME/wpscan/scan.json` (if `XDG_CONFIG_HOME` is set)
157
+ - `$XDG_CONFIG_HOME/wpscan/scan.yml` (if `XDG_CONFIG_HOME` is set)
158
+ - `~/.config/wpscan/scan.json` (if `XDG_CONFIG_HOME` is not set)
159
+ - `~/.config/wpscan/scan.yml` (if `XDG_CONFIG_HOME` is not set)
160
+ - `~/.wpscan/scan.json`
161
+ - `~/.wpscan/scan.yml`
162
+ - `pwd/.wpscan/scan.json`
163
+ - `pwd/.wpscan/scan.yml`
109
164
 
110
165
  If those files exist, options from the `cli_options` key will be loaded and overridden if found twice.
111
166
 
112
167
  e.g:
113
168
 
114
- ~/.wpscan/scan.yml:
169
+ `~/.config/wpscan/scan.yml`:
115
170
 
116
171
  ```yml
117
172
  cli_options:
@@ -119,7 +174,7 @@ cli_options:
119
174
  verbose: true
120
175
  ```
121
176
 
122
- pwd/.wpscan/scan.yml:
177
+ `pwd/.wpscan/scan.yml`:
123
178
 
124
179
  ```yml
125
180
  cli_options:
@@ -127,11 +182,19 @@ cli_options:
127
182
  url: 'http://target.tld'
128
183
  ```
129
184
 
130
- Running ```wpscan``` in the current directory (pwd), is the same as ```wpscan -v --proxy socks5://127.0.0.1:9090 --url http://target.tld```
185
+ Running ```wpscan``` in the current directory (pwd) is the same as ```wpscan -v --proxy socks5://127.0.0.1:9090 --url http://target.tld```
186
+
187
+ Other command line options can be added by using snake case convention. e.g:
188
+ ```yml
189
+ cli_options:
190
+ user_agent: "Testing UA"
191
+ max_threads: 1
192
+ headers: "Custom-Header: aaaa; Another Header: bbb"
193
+ ```
131
194
 
132
195
  ## Save API Token in a file
133
196
 
134
- The feature mentioned above is useful to keep the API Token in a config file and not have to supply it via the CLI each time. To do so, create the ~/.wpscan/scan.yml file containing the below:
197
+ The feature mentioned above is useful to keep the API Token in a config file and not have to supply it via the CLI each time. To do so, create the ~/.config/wpscan/scan.yml file containing the below:
135
198
 
136
199
  ```yml
137
200
  cli_options:
@@ -142,6 +205,9 @@ cli_options:
142
205
 
143
206
  The API Token will be automatically loaded from the ENV variable `WPSCAN_API_TOKEN` if present. If the `--api-token` CLI option is also provided, the value from the CLI will be used.
144
207
 
208
+ ## API Service Status
209
+
210
+ If you experience connection issues with the WPScan API, you can check the service status at https://status.wpscan.com/. When API connection errors occur, WPScan will include a link to the status page in the error message.
145
211
 
146
212
  ## Enumerating usernames
147
213
 
@@ -157,13 +223,21 @@ wpscan --url https://target.tld/ --enumerate u1-100
157
223
 
158
224
  ** replace u1-100 with a range of your choice.
159
225
 
226
+ ## Enumerating backup folders
227
+
228
+ ```shell
229
+ wpscan --url https://target.tld/ --enumerate bf
230
+ ```
231
+
232
+ This will check for backup folders created by popular WordPress backup plugins. These folders may contain sensitive data like database dumps, configuration files, or full site backups.
233
+
160
234
  # LICENSE
161
235
 
162
236
  ## WPScan Public Source License
163
237
 
164
238
  The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2019 WPScan Team.
165
239
 
166
- Cases that include commercialization of WPScan require a commercial, non-free license. Otherwise, WPScan can be used without charge under the terms set out below.
240
+ Cases that include the commercialization of WPScan require a commercial, non-free license. Otherwise, WPScan can be used without charge under the terms set out below.
167
241
 
168
242
  ### 1. Definitions
169
243
 
@@ -175,15 +249,15 @@ Cases that include commercialization of WPScan require a commercial, non-free li
175
249
 
176
250
  ### 2. Commercialization
177
251
 
178
- A commercial use is one intended for commercial advantage or monetary compensation.
252
+ Commercial use is one intended for commercial advantage or monetary compensation.
179
253
 
180
254
  Example cases of commercialization are:
181
255
 
182
256
  - Using WPScan to provide commercial managed/Software-as-a-Service services.
183
257
  - Distributing WPScan as a commercial product or as part of one.
184
- - Using WPScan as a value added service/product.
258
+ - Using WPScan as a value-added service/product.
185
259
 
186
- Example cases which do not require a commercial license, and thus fall under the terms set out below, include (but are not limited to):
260
+ Example cases that do not require a commercial license, and thus fall under the terms set out below, include (but are not limited to):
187
261
 
188
262
  - Penetration testers (or penetration testing organizations) using WPScan as part of their assessment toolkit.
189
263
  - Penetration Testing Linux Distributions including but not limited to Kali Linux, SamuraiWTF, BackBox Linux.
@@ -214,9 +288,9 @@ Modification is permitted so long as it does not conflict with the Redistributio
214
288
 
215
289
  Any Contributions assume the Contributor grants the WPScan Team the unlimited, non-exclusive right to reuse, modify and relicense the Contributor's content.
216
290
 
217
- ### 7. Support
291
+ ### 7. Support, updates, and maintenance
218
292
 
219
- WPScan is provided under an AS-IS basis and without any support, updates or maintenance. Support, updates and maintenance may be given according to the sole discretion of the WPScan Team.
293
+ WPScan is provided under an AS-IS basis and without any support, updates, or maintenance. Support, updates and maintenance may be given according to the sole discretion of the WPScan Team.
220
294
 
221
295
  ### 8. Disclaimer of Warranty
222
296
 
@@ -224,11 +298,11 @@ WPScan is provided under this License on an “as is” basis, without warranty
224
298
 
225
299
  ### 9. Limitation of Liability
226
300
 
227
- To the extent permitted under Law, WPScan is provided under an AS-IS basis. The WPScan Team shall never, and without any limit, be liable for any damage, cost, expense or any other payment incurred as a result of WPScan's actions, failure, bugs and/or any other interaction between WPScan and end-equipment, computers, other software or any 3rd party, end-equipment, computer or services.
301
+ To the extent permitted under Law, WPScan is provided under an AS-IS basis. The WPScan Team shall never, and without any limit, be liable for any damage, cost, expense or any other payment incurred as a result of WPScan's actions, failure, bugs, and/or any other interaction between WPScan and end-equipment, computers, other software or any 3rd party, end-equipment, computer or services.
228
302
 
229
303
  ### 10. Disclaimer
230
304
 
231
- Running WPScan against websites without prior mutual consent may be illegal in your country. The WPScan Team accept no liability and are not responsible for any misuse or damage caused by WPScan.
305
+ Running WPScan against websites without prior mutual consent may be illegal in your country. The WPScan Team accepts no liability and is not responsible for any misuse or damage caused by WPScan.
232
306
 
233
307
  ### 11. Trademark
234
308
 
data/app/app.rb CHANGED
@@ -1,5 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Formatters
4
+ require_relative 'formatters/cli'
5
+ require_relative 'formatters/cli_no_colour'
6
+ require_relative 'formatters/cli_no_color'
7
+ require_relative 'formatters/json'
8
+ require_relative 'formatters/jsonl'
9
+ require_relative 'formatters/sarif'
10
+
11
+ # Base controllers
12
+ require_relative 'controllers/core'
13
+ require_relative 'controllers/interesting_findings'
14
+
15
+ # Base models
16
+ require_relative 'models/interesting_finding'
17
+ require_relative 'models/robots_txt'
18
+ require_relative 'models/fantastico_fileslist'
19
+ require_relative 'models/search_replace_db_2'
20
+ require_relative 'models/headers'
21
+ require_relative 'models/xml_rpc'
22
+ require_relative 'models/version'
23
+ require_relative 'models/user'
24
+
25
+ # Base finders
26
+ require_relative 'finders/interesting_findings'
27
+
28
+ # WordPress-specific extensions
3
29
  require_relative 'models'
4
30
  require_relative 'finders'
5
31
  require_relative 'controllers'
@@ -3,11 +3,11 @@
3
3
  module WPScan
4
4
  module Controller
5
5
  # Controller to add the aliases in the CLI
6
- class Aliases < CMSScanner::Controller::Base
6
+ class Aliases < WPScan::Controller::Base
7
7
  def cli_options
8
8
  [
9
9
  OptAlias.new(['--stealthy'],
10
- alias_for: '--random-user-agent --detection-mode passive --plugins-version-detection passive')
10
+ alias_for: '--random-user-agent --detection-mode passive')
11
11
  ]
12
12
  end
13
13
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Controller
5
+ # Fetches an authoritative inventory of installed plugins and themes from the
6
+ # WordPress REST API using admin credentials (--wp-auth). Pre-populates the
7
+ # target's @plugins / @themes / @main_theme so the regular enumeration
8
+ # controllers skip detection for those items.
9
+ class AuthenticatedInventory < WPScan::Controller::Base
10
+ def run
11
+ return unless ParsedCli.wp_auth
12
+
13
+ output('@info', msg: 'Fetching authoritative inventory via WP REST API (/wp-json/wp/v2/)') if user_interaction?
14
+
15
+ plugins = plugins_finder.aggressive(userpwd: userpwd)
16
+ themes = themes_finder.aggressive(userpwd: userpwd)
17
+
18
+ target.instance_variable_set(:@plugins, plugins)
19
+ target.instance_variable_set(:@themes, themes)
20
+
21
+ active = themes.find { |t| t.instance_variable_get(:@wp_json_active) }
22
+ target.instance_variable_set(:@main_theme, active) if active
23
+
24
+ formatter.output('plugins', { plugins: plugins, verbose: ParsedCli.verbose }, 'enumeration')
25
+ formatter.output('themes', { themes: themes, verbose: ParsedCli.verbose }, 'enumeration')
26
+ end
27
+
28
+ private
29
+
30
+ def plugins_finder
31
+ @plugins_finder ||= Finders::Plugins::WpJsonApi.new(target)
32
+ end
33
+
34
+ def themes_finder
35
+ @themes_finder ||= Finders::Themes::WpJsonApi.new(target)
36
+ end
37
+
38
+ def userpwd
39
+ "#{ParsedCli.wp_auth[:username]}:#{ParsedCli.wp_auth[:password]}"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Controller
5
+ # CLI Options for the Core Controller
6
+ class Core < Base
7
+ def base_cli_options
8
+ formats = WPScan::Formatter.availables
9
+
10
+ [
11
+ OptURL.new(['-u', '--url URL', 'The URL to scan'],
12
+ required_unless: %i[help hh version],
13
+ default_protocol: 'http'),
14
+ OptBoolean.new(['--force', 'Do not check if target returns a 403'])
15
+ ] + mixed_cli_options + [
16
+ OptFilePath.new(['-o', '--output FILE', 'Output to FILE'], writable: true, exists: false),
17
+ OptChoice.new(['-f', '--format FORMAT',
18
+ 'Output results in the format supplied'], choices: formats),
19
+ OptBoolean.new(['--[no-]stream',
20
+ 'Emit enumeration findings (plugins/themes/users) as they are discovered, ' \
21
+ 'instead of waiting until each enumeration step completes. ' \
22
+ 'Has no effect on the json or sarif output formats, which always batch.'],
23
+ default: true),
24
+ OptChoice.new(['--detection-mode MODE'],
25
+ choices: %w[mixed passive aggressive],
26
+ normalize: :to_sym,
27
+ default: :mixed),
28
+ OptArray.new(['--scope DOMAINS',
29
+ 'Comma separated (sub-)domains to consider in scope. ',
30
+ 'Wildcard(s) allowed in the trd of valid domains, e.g: *.target.tld'], advanced: true),
31
+ OptArray.new(['--exclude-vulns UUIDs',
32
+ 'Comma separated list of vulnerability UUIDs to exclude. ',
33
+ 'Format: UUID (e.g: c099c1da-3750-4e63-8af9-929e773bbe57)'], advanced: true)
34
+ ] + cli_browser_options
35
+ end
36
+
37
+ def mixed_cli_options
38
+ [
39
+ OptBoolean.new(['-h', '--help', 'Display the simple help and exit']),
40
+ OptBoolean.new(['--hh', 'Display the full help and exit']),
41
+ OptBoolean.new(['--version', 'Display the version and exit']),
42
+ OptBoolean.new(['--ignore-main-redirect',
43
+ 'Ignore the main redirect (if any) and scan the target url. ' \
44
+ 'Has no effect if --follow-redirect is set.'],
45
+ advanced: true),
46
+ OptBoolean.new(['--follow-redirect', 'Automatically update the URL to the destination of the redirect'],
47
+ advanced: true),
48
+ OptBoolean.new(['-v', '--verbose', 'Verbose mode']),
49
+ OptBoolean.new(['--[no-]banner', 'Whether or not to display the banner'], default: true),
50
+ OptPositiveInteger.new(['--max-scan-duration SECONDS',
51
+ 'Abort the scan if it exceeds the time provided in seconds'],
52
+ advanced: true),
53
+ OptPositiveInteger.new(['--max-log-file-size MiB',
54
+ 'Skip inspection of PHP log files (debug.log, error_log, ...) ' \
55
+ 'when their advertised or transferred size exceeds this value, in MiB. ' \
56
+ 'Guards against memory exhaustion on sites serving huge log files.'],
57
+ default: 20, advanced: true)
58
+ ]
59
+ end
60
+
61
+ # @return [ Array<OptParseValidator::OptBase> ]
62
+ def cli_browser_options
63
+ cli_browser_headers_options + [
64
+ OptBoolean.new(['--random-user-agent', '--rua',
65
+ 'Use a random user-agent for each scan']),
66
+ OptFilePath.new(['--user-agents-list FILE-PATH',
67
+ 'List of agents to use with --random-user-agent'],
68
+ exists: true,
69
+ advanced: true,
70
+ default: APP_DIR.join('user_agents.txt')),
71
+ OptCredentials.new(['--http-auth login:password',
72
+ 'Basic HTTP authentication, beware that the $ character must be properly escaped.']),
73
+ OptCredentials.new(['--wp-auth login:password',
74
+ 'WordPress admin credentials used to query the REST API for an authoritative ' \
75
+ 'inventory of installed plugins and themes (/wp-json/wp/v2/plugins and /themes). ' \
76
+ 'When provided, plugin/theme enumeration via -e is bypassed. ' \
77
+ 'The password MUST be a WordPress Application Password (WP >= 5.6, ' \
78
+ 'created at Users -> Profile -> Application Passwords). ' \
79
+ 'Real account passwords are rejected by WordPress core over Basic Auth.']),
80
+ OptPositiveInteger.new(['-t', '--max-threads VALUE', 'The max threads to use'],
81
+ default: 5),
82
+ OptPositiveInteger.new(['--throttle MilliSeconds', 'Milliseconds to wait before doing another web request. ' \
83
+ 'If used, the max threads will be set to 1.']),
84
+ OptPositiveInteger.new(['--request-timeout SECONDS', 'The request timeout in seconds'],
85
+ default: 60),
86
+ OptPositiveInteger.new(['--connect-timeout SECONDS', 'The connection timeout in seconds'],
87
+ default: 30),
88
+ OptBoolean.new(['--disable-tls-checks',
89
+ 'Disables SSL/TLS certificate verification, and downgrade to TLS1.0+ ' \
90
+ '(requires cURL 7.66 for the latter)'])
91
+ ] + cli_browser_proxy_options + cli_browser_cookies_options + cli_browser_cache_options
92
+ end
93
+
94
+ # @return [ Array<OptParseValidator::OptBase> ]
95
+ def cli_browser_headers_options
96
+ [
97
+ OptString.new(['--user-agent VALUE', '--ua']),
98
+ OptHeaders.new(['--headers HEADERS', 'Additional headers to append in requests'], advanced: true),
99
+ OptString.new(['--vhost VALUE', 'The virtual host (Host header) to use in requests'], advanced: true)
100
+ ]
101
+ end
102
+
103
+ # @return [ Array<OptParseValidator::OptBase> ]
104
+ def cli_browser_proxy_options
105
+ [
106
+ OptProxy.new(['--proxy protocol://IP:port',
107
+ 'Supported protocols depend on the cURL installed. ' \
108
+ 'Note: with socks5://, hostnames are resolved locally before being ' \
109
+ 'sent to the proxy; use socks5h:// to have the proxy resolve them ' \
110
+ '(required for .onion addresses when proxying through Tor).']),
111
+ OptCredentials.new(['--proxy-auth login:password'])
112
+ ]
113
+ end
114
+
115
+ # @return [ Array<OptParseValidator::OptBase> ]
116
+ def cli_browser_cookies_options
117
+ [
118
+ OptString.new(['--cookie-string COOKIE',
119
+ 'Cookie string to use in requests, ' \
120
+ 'format: cookie1=value1[; cookie2=value2]']),
121
+ OptFilePath.new(['--cookie-jar FILE-PATH', 'File to read and write cookies'],
122
+ writable: true,
123
+ readable: true,
124
+ create: true,
125
+ default: tmp_directory.join('cookie_jar.txt')),
126
+ OptBoolean.new(['--expect-saml',
127
+ 'Expect SAML authentication to be required. ' \
128
+ 'When the target redirects to a SAML IdP, an interactive browser ' \
129
+ 'is launched for login and the resulting session cookies are reused ' \
130
+ 'for the rest of the scan.'],
131
+ advanced: true)
132
+ ]
133
+ end
134
+
135
+ # @return [ Array<OptParseValidator::OptBase> ]
136
+ def cli_browser_cache_options
137
+ [
138
+ OptInteger.new(['--cache-ttl TIME_TO_LIVE', 'The cache time to live in seconds'],
139
+ default: 600, advanced: true),
140
+ OptBoolean.new(['--clear-cache', 'Clear the cache before the scan'], advanced: true),
141
+ OptDirectoryPath.new(['--cache-dir PATH'],
142
+ readable: true,
143
+ writable: true,
144
+ create: true,
145
+ default: tmp_directory.join('cache'),
146
+ advanced: true)
147
+ ]
148
+ end
149
+ end
150
+ end
151
+ end