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
@@ -4,8 +4,8 @@ module WPScan
4
4
  module Finders
5
5
  module Themes
6
6
  # Known Locations Themes Finder
7
- class KnownLocations < CMSScanner::Finders::Finder
8
- include CMSScanner::Finders::Finder::Enumerator
7
+ class KnownLocations < WPScan::Finders::Finder
8
+ include WPScan::Finders::Finder::Enumerator
9
9
 
10
10
  # @return [ Array<Integer> ]
11
11
  def valid_response_codes
@@ -14,22 +14,28 @@ module WPScan
14
14
 
15
15
  # @param [ Hash ] opts
16
16
  # @option opts [ String ] :list
17
+ # @option opts [ Findings ] :found Shared findings collection; see
18
+ # {Plugins::KnownLocations#aggressive} for the streaming rationale.
17
19
  #
18
20
  # @return [ Array<Theme> ]
19
21
  def aggressive(opts = {})
20
- found = []
22
+ shared = opts[:found]
23
+ local = shared ? nil : []
24
+ count = 0
21
25
 
22
26
  enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, slug|
23
27
  finding_opts = opts.merge(found_by: found_by,
24
28
  confidence: 80,
25
29
  interesting_entries: ["#{res.effective_url}, status: #{res.code}"])
26
30
 
27
- found << Model::Theme.new(slug, target, finding_opts)
31
+ theme = Model::Theme.new(slug, target, finding_opts)
32
+ (shared || local) << theme
33
+ count += 1
28
34
 
29
- raise Error::ThemesThresholdReached if opts[:threshold].positive? && found.size >= opts[:threshold]
35
+ raise Error::ThemesThresholdReached if opts[:threshold].positive? && count >= opts[:threshold]
30
36
  end
31
37
 
32
- found
38
+ local || []
33
39
  end
34
40
 
35
41
  # @param [ Hash ] opts
@@ -4,20 +4,16 @@ module WPScan
4
4
  module Finders
5
5
  module Themes
6
6
  # URLs In Homepage Finder
7
- class UrlsInHomepage < CMSScanner::Finders::Finder
7
+ class UrlsInHomepage < WPScan::Finders::Finder
8
8
  include WpItems::UrlsInPage
9
9
 
10
10
  # @param [ Hash ] opts
11
11
  #
12
12
  # @return [ Array<Theme> ]
13
13
  def passive(opts = {})
14
- found = []
15
-
16
- (items_from_links('themes') + items_from_codes('themes')).uniq.sort.each do |slug|
17
- found << Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
14
+ (items_from_links('themes') + items_from_codes('themes')).uniq.sort.map do |slug|
15
+ Model::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
18
16
  end
19
-
20
- found
21
17
  end
22
18
 
23
19
  # @return [ Typhoeus::Response ]
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Finders
5
+ module Themes
6
+ # Authenticated theme inventory via the WordPress REST API
7
+ # endpoint /wp-json/wp/v2/themes (WP >= 5.7).
8
+ #
9
+ # Requires admin credentials with the switch_themes capability.
10
+ class WpJsonApi < WPScan::Finders::Finder
11
+ FOUND_BY = 'WP REST API (Authenticated)'
12
+
13
+ # @param [ Hash ] opts
14
+ # @option opts [ String ] :userpwd "user:password" credentials for Basic Auth
15
+ #
16
+ # @return [ Array<Model::Theme> ]
17
+ def aggressive(opts = {})
18
+ response = Browser.get(api_url, userpwd: opts[:userpwd], headers: { 'Accept' => 'application/json' })
19
+
20
+ raise Error::WpAuthFailed.new(response.code, api_url) if [401, 403].include?(response.code)
21
+ raise Error::WpAuthEndpointUnavailable.new(response.code, api_url) unless response.code == 200
22
+
23
+ themes_from_response(response)
24
+ rescue JSON::ParserError, TypeError
25
+ []
26
+ end
27
+
28
+ # @param [ Typhoeus::Response ] response
29
+ #
30
+ # @return [ Array<Model::Theme> ]
31
+ def themes_from_response(response)
32
+ json = JSON.parse(response.body)
33
+ return [] unless json.is_a?(Enumerable)
34
+
35
+ json.filter_map { |entry| build_theme(entry, response.effective_url) }
36
+ end
37
+
38
+ # @return [ String ] The REST API URL for the themes endpoint
39
+ def api_url
40
+ @api_url ||= target.url('wp-json/wp/v2/themes')
41
+ end
42
+
43
+ private
44
+
45
+ # @param [ Hash ] entry
46
+ # @param [ String ] effective_url
47
+ #
48
+ # @return [ Model::Theme, nil ]
49
+ def build_theme(entry, effective_url)
50
+ slug = entry['stylesheet']
51
+ return nil if slug.nil? || slug.to_s.empty?
52
+
53
+ theme = Model::Theme.new(
54
+ slug,
55
+ target,
56
+ confidence: 100,
57
+ found_by: FOUND_BY,
58
+ interesting_entries: [effective_url]
59
+ )
60
+
61
+ version = entry['version']
62
+ theme.instance_variable_set(
63
+ :@version,
64
+ version && !version.to_s.empty? ? Model::Version.new(version, confidence: 100, found_by: FOUND_BY) : false
65
+ )
66
+
67
+ theme.instance_variable_set(:@wp_json_active, entry['status'] == 'active')
68
+
69
+ theme
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -3,13 +3,14 @@
3
3
  require_relative 'themes/urls_in_homepage'
4
4
  require_relative 'themes/urls_in_404_page'
5
5
  require_relative 'themes/known_locations'
6
+ require_relative 'themes/wp_json_api' # Authenticated, used by AuthenticatedInventory controller
6
7
 
7
8
  module WPScan
8
9
  module Finders
9
10
  module Themes
10
11
  # Themes Finder
11
12
  class Base
12
- include CMSScanner::Finders::SameTypeFinder
13
+ include WPScan::Finders::SameTypeFinder
13
14
 
14
15
  # @param [ WPScan::Target ] target
15
16
  def initialize(target)
@@ -5,7 +5,7 @@ module WPScan
5
5
  module TimthumbVersion
6
6
  # Timthumb Version Finder from the body of a bad request
7
7
  # See https://code.google.com/p/timthumb/source/browse/trunk/timthumb.php#435
8
- class BadRequest < CMSScanner::Finders::Finder
8
+ class BadRequest < WPScan::Finders::Finder
9
9
  # @return [ Version ]
10
10
  def aggressive(_opts = {})
11
11
  return unless Browser.get(target.url).body =~ /(TimThumb version\s*: ([^<]+))/
@@ -7,7 +7,7 @@ module WPScan
7
7
  module TimthumbVersion
8
8
  # Timthumb Version Finder
9
9
  class Base
10
- include CMSScanner::Finders::UniqueFinder
10
+ include WPScan::Finders::UniqueFinder
11
11
 
12
12
  # @param [ Model::Timthumb ] target
13
13
  def initialize(target)
@@ -6,8 +6,8 @@ module WPScan
6
6
  # Known Locations Timthumbs Finder
7
7
  # Note: A vulnerable version, 2.8.13 can be found here:
8
8
  # https://github.com/GabrielGil/TimThumb/blob/980c3d6a823477761570475e8b83d3e9fcd2d7ae/timthumb.php
9
- class KnownLocations < CMSScanner::Finders::Finder
10
- include CMSScanner::Finders::Finder::Enumerator
9
+ class KnownLocations < WPScan::Finders::Finder
10
+ include WPScan::Finders::Finder::Enumerator
11
11
 
12
12
  # @return [ Array<Integer> ]
13
13
  def valid_response_codes
@@ -37,8 +37,10 @@ module WPScan
37
37
  def target_urls(opts = {})
38
38
  urls = {}
39
39
 
40
- File.open(opts[:list]).each_with_index do |path, index|
41
- urls[target.url(path.chomp)] = index
40
+ File.open(opts[:list]) do |f|
41
+ f.each_with_index do |path, index|
42
+ urls[target.url(path.chomp)] = index
43
+ end
42
44
  end
43
45
 
44
46
  # Add potential timthumbs located in the main theme
@@ -7,7 +7,7 @@ module WPScan
7
7
  module Timthumbs
8
8
  # Timthumbs Finder
9
9
  class Base
10
- include CMSScanner::Finders::SameTypeFinder
10
+ include WPScan::Finders::SameTypeFinder
11
11
 
12
12
  # @param [ WPScan::Target ] target
13
13
  def initialize(target)
@@ -4,8 +4,8 @@ module WPScan
4
4
  module Finders
5
5
  module Users
6
6
  # Author Id Brute Forcing
7
- class AuthorIdBruteForcing < CMSScanner::Finders::Finder
8
- include CMSScanner::Finders::Finder::Enumerator
7
+ class AuthorIdBruteForcing < WPScan::Finders::Finder
8
+ include WPScan::Finders::Finder::Enumerator
9
9
 
10
10
  # @return [ Array<Integer> ]
11
11
  def valid_response_codes
@@ -14,10 +14,13 @@ module WPScan
14
14
 
15
15
  # @param [ Hash ] opts
16
16
  # @option opts [ Range ] :range Mandatory
17
+ # @option opts [ Findings ] :found Shared findings collection; see
18
+ # {Plugins::KnownLocations#aggressive} for the streaming rationale.
17
19
  #
18
20
  # @return [ Array<User> ]
19
21
  def aggressive(opts = {})
20
- found = []
22
+ shared = opts[:found]
23
+ local = shared ? nil : []
21
24
  found_by_msg = 'Author Id Brute Forcing - %s (Aggressive Detection)'
22
25
 
23
26
  enumerate(target_urls(opts), opts.merge(check_full_response: true)) do |res, id|
@@ -25,15 +28,16 @@ module WPScan
25
28
 
26
29
  next unless username
27
30
 
28
- found << Model::User.new(
31
+ user = Model::User.new(
29
32
  username,
30
33
  id: id,
31
34
  found_by: format(found_by_msg, found_by),
32
35
  confidence: confidence
33
36
  )
37
+ (shared || local) << user
34
38
  end
35
39
 
36
- found
40
+ local || []
37
41
  end
38
42
 
39
43
  # @param [ Hash ] opts
@@ -55,7 +59,7 @@ module WPScan
55
59
  end
56
60
 
57
61
  def full_request_params
58
- { followlocation: true }
62
+ { followlocation: true, maxredirs: 10 }
59
63
  end
60
64
 
61
65
  # @param [ Typhoeus::Response ] res
@@ -68,7 +72,7 @@ module WPScan
68
72
 
69
73
  username = display_name_from_body(res.body)
70
74
 
71
- return username, 'Display Name', 50 if username
75
+ [username, 'Display Name', 50] if username
72
76
  end
73
77
 
74
78
  # @param [ String, Addressable::URI ] uri
@@ -4,7 +4,7 @@ module WPScan
4
4
  module Finders
5
5
  module Users
6
6
  # Author Posts
7
- class AuthorPosts < CMSScanner::Finders::Finder
7
+ class AuthorPosts < WPScan::Finders::Finder
8
8
  # @param [ Hash ] opts
9
9
  #
10
10
  # @return [ Array<User> ]
@@ -5,7 +5,7 @@ module WPScan
5
5
  module Users
6
6
  # Since WP 5.5, /wp-sitemap-users-1.xml is generated and contains
7
7
  # the usernames of accounts who made a post
8
- class AuthorSitemap < CMSScanner::Finders::Finder
8
+ class AuthorSitemap < WPScan::Finders::Finder
9
9
  # @param [ Hash ] opts
10
10
  #
11
11
  # @return [ Array<User> ]
@@ -10,7 +10,7 @@ module WPScan
10
10
  # WP >= 3.1 - The password you entered for the username admin is incorrect.
11
11
  # Non existent username: Invalid username.
12
12
  #
13
- class LoginErrorMessages < CMSScanner::Finders::Finder
13
+ class LoginErrorMessages < WPScan::Finders::Finder
14
14
  # @param [ Hash ] opts
15
15
  # @option opts [ String ] :list
16
16
  #
@@ -5,7 +5,7 @@ module WPScan
5
5
  module Users
6
6
  # Since WP 4.4, the oembed API can disclose a user
7
7
  # https://github.com/wpscanteam/wpscan/issues/1049
8
- class OembedApi < CMSScanner::Finders::Finder
8
+ class OembedApi < WPScan::Finders::Finder
9
9
  # @param [ Hash ] opts
10
10
  #
11
11
  # @return [ Array<User> ]
@@ -36,6 +36,8 @@ module WPScan
36
36
 
37
37
  oembed_data = oembed_data.first if oembed_data.is_a?(Array)
38
38
 
39
+ oembed_data = {} unless oembed_data.is_a?(Hash)
40
+
39
41
  if oembed_data['author_url'] =~ %r{/author/([^/]+)/?\z}
40
42
  details = [Regexp.last_match[1], 'Author URL', 90]
41
43
  elsif oembed_data['author_name'] && !oembed_data['author_name'].empty?
@@ -8,7 +8,7 @@ module WPScan
8
8
  # Since 4.7 - Need more investigation as it seems WP 4.7.1 reduces the exposure, see https://github.com/wpscanteam/wpscan/issues/1038)
9
9
  # For the pagination, see https://github.com/wpscanteam/wpscan/issues/1285
10
10
  #
11
- class WpJsonApi < CMSScanner::Finders::Finder
11
+ class WpJsonApi < WPScan::Finders::Finder
12
12
  MAX_PER_PAGE = 100 # See https://developer.wordpress.org/rest-api/using-the-rest-api/pagination/
13
13
 
14
14
  # @param [ Hash ] opts
@@ -42,12 +42,16 @@ module WPScan
42
42
  def users_from_response(response)
43
43
  found = []
44
44
 
45
- JSON.parse(response.body)&.each do |user|
46
- found << Model::User.new(user['slug'],
47
- id: user['id'],
48
- found_by: found_by,
49
- confidence: 100,
50
- interesting_entries: [response.effective_url])
45
+ json = JSON.parse(response.body)
46
+
47
+ if json.is_a?(Enumerable)
48
+ json.each do |user|
49
+ found << Model::User.new(user['slug'],
50
+ id: user['id'],
51
+ found_by: found_by,
52
+ confidence: 100,
53
+ interesting_entries: [response.effective_url])
54
+ end
51
55
  end
52
56
 
53
57
  found
data/app/finders/users.rb CHANGED
@@ -24,7 +24,7 @@ module WPScan
24
24
  module Users
25
25
  # Users Finder
26
26
  class Base
27
- include CMSScanner::Finders::SameTypeFinder
27
+ include WPScan::Finders::SameTypeFinder
28
28
 
29
29
  # @param [ WPScan::Target ] target
30
30
  def initialize(target)
@@ -4,7 +4,7 @@ module WPScan
4
4
  module Finders
5
5
  module WpVersion
6
6
  # Atom Generator Version Finder
7
- class AtomGenerator < CMSScanner::Finders::Finder
7
+ class AtomGenerator < WPScan::Finders::Finder
8
8
  include Finder::WpVersion::SmartURLChecker
9
9
 
10
10
  def process_urls(urls, _opts = {})
@@ -4,7 +4,7 @@ module WPScan
4
4
  module Finders
5
5
  module WpVersion
6
6
  # RDF Generator Version Finder
7
- class RDFGenerator < CMSScanner::Finders::Finder
7
+ class RDFGenerator < WPScan::Finders::Finder
8
8
  include Finder::WpVersion::SmartURLChecker
9
9
 
10
10
  def process_urls(urls, _opts = {})
@@ -4,7 +4,7 @@ module WPScan
4
4
  module Finders
5
5
  module WpVersion
6
6
  # Readme Version Finder
7
- class Readme < CMSScanner::Finders::Finder
7
+ class Readme < WPScan::Finders::Finder
8
8
  # @return [ WpVersion ]
9
9
  def aggressive(_opts = {})
10
10
  readme_url = target.url('readme.html') # Maybe move this into the Target ?
@@ -4,7 +4,7 @@ module WPScan
4
4
  module Finders
5
5
  module WpVersion
6
6
  # RSS Generator Version Finder
7
- class RSSGenerator < CMSScanner::Finders::Finder
7
+ class RSSGenerator < WPScan::Finders::Finder
8
8
  include Finder::WpVersion::SmartURLChecker
9
9
 
10
10
  def process_urls(urls, _opts = {})
@@ -4,8 +4,8 @@ module WPScan
4
4
  module Finders
5
5
  module WpVersion
6
6
  # Unique Fingerprinting Version Finder
7
- class UniqueFingerprinting < CMSScanner::Finders::Finder
8
- include CMSScanner::Finders::Finder::Fingerprinter
7
+ class UniqueFingerprinting < WPScan::Finders::Finder
8
+ include WPScan::Finders::Finder::Fingerprinter
9
9
 
10
10
  # @return [ WpVersion ]
11
11
  def aggressive(opts = {})
@@ -23,7 +23,7 @@ module WPScan
23
23
  module WpVersion
24
24
  # Wp Version Finder
25
25
  class Base
26
- include CMSScanner::Finders::UniqueFinder
26
+ include WPScan::Finders::UniqueFinder
27
27
 
28
28
  # @param [ WPScan::Target ] target
29
29
  def initialize(target)
data/app/finders.rb CHANGED
@@ -8,6 +8,7 @@ require_relative 'finders/timthumb_version'
8
8
  require_relative 'finders/timthumbs'
9
9
  require_relative 'finders/config_backups'
10
10
  require_relative 'finders/db_exports'
11
+ require_relative 'finders/backup_folders'
11
12
  require_relative 'finders/medias'
12
13
  require_relative 'finders/users'
13
14
  require_relative 'finders/plugins'
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Formatter
5
+ # CLI Formatter
6
+ class Cli < Base
7
+ def streams?
8
+ true
9
+ end
10
+
11
+ # ANSI sequence to clear the current line and return to column 0.
12
+ # Emitted at the start of each streamed enumeration finding so the
13
+ # ruby-progressbar line (which redraws via \r and leaves its previous
14
+ # render visible when interleaved with foreign output) gets wiped
15
+ # before the finding prints. No-op on non-TTY stdout so file/pipe
16
+ # output stays clean.
17
+ def bar_clear
18
+ $stdout.tty? ? "\e[2K\r" : ''
19
+ end
20
+
21
+ # @return [ String ]
22
+ def info_icon
23
+ green('[+]')
24
+ end
25
+
26
+ # @return [ String ]
27
+ def notice_icon
28
+ blue('[i]')
29
+ end
30
+
31
+ # @return [ String ]
32
+ def warning_icon
33
+ amber('[!]')
34
+ end
35
+
36
+ # @return [ String ]
37
+ def critical_icon
38
+ red('[!]')
39
+ end
40
+
41
+ # @param [ String ] text
42
+ # @return [ String ]
43
+ def bold(text)
44
+ colorize(text, 1)
45
+ end
46
+
47
+ # @param [ String ] text
48
+ # @return [ String ]
49
+ def red(text)
50
+ colorize(text, 31)
51
+ end
52
+
53
+ # @param [ String ] text
54
+ # @return [ String ]
55
+ def green(text)
56
+ colorize(text, 32)
57
+ end
58
+
59
+ # @param [ String ] text
60
+ # @return [ String ]
61
+ def amber(text)
62
+ colorize(text, 33)
63
+ end
64
+
65
+ # @param [ String ] text
66
+ # @return [ String ]
67
+ def blue(text)
68
+ colorize(text, 34)
69
+ end
70
+
71
+ # @param [ String ] text
72
+ # @param [ Integer ] color_code
73
+ # @return [ String ]
74
+ def colorize(text, color_code)
75
+ "\e[#{color_code}m#{text}\e[0m"
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Formatter
5
+ # Because Reason https://github.com/wpscanteam/WPScan/issues/56
6
+ class CliNoColor < CliNoColour
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Formatter
5
+ # CLI No Colour Formatter
6
+ class CliNoColour < Cli
7
+ # Override to get the cli views
8
+ def format
9
+ 'cli'
10
+ end
11
+
12
+ def colorize(text, _color_code)
13
+ text
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Formatter
5
+ # JSON Formatter
6
+ class Json < Base
7
+ include Buffer
8
+
9
+ def beautify
10
+ puts JSON.pretty_generate(JSON.parse("{#{buffer.chomp.chomp(',')}}"))
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Formatter
5
+ # JSON Lines formatter — streams one self-contained JSON object per
6
+ # template render as the scan progresses. Reuses every app/views/json/*.erb
7
+ # template via base_format, so output content matches `-f json` but is
8
+ # emitted incrementally and can be piped directly into tools like `jq`.
9
+ class Jsonl < Base
10
+ def base_format
11
+ 'json'
12
+ end
13
+
14
+ def streams?
15
+ true
16
+ end
17
+
18
+ def output(tpl, vars = {}, controller_name = nil)
19
+ rendered = render(tpl, vars, controller_name)
20
+ .encode('UTF-8', invalid: :replace, undef: :replace)
21
+ fragment = rendered.strip.chomp(',')
22
+ return if fragment.empty?
23
+
24
+ $stdout.puts JSON.generate(JSON.parse("{#{fragment}}"))
25
+ $stdout.flush
26
+ end
27
+ end
28
+ end
29
+ end