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
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WPScan
4
+ module Model
5
+ class WpItem
6
+ # Fetches and exposes the public wordpress.org info API response for a
7
+ # plugin or theme. The HTTP call is performed at most once per item.
8
+ module WordpressOrgData
9
+ # Timeout (in seconds) for the wordpress.org API lookup. Kept low so a
10
+ # slow or unreachable wordpress.org does not noticeably stall the scan.
11
+ WORDPRESS_ORG_API_TIMEOUT = 5
12
+
13
+ # @return [ String, nil ] The wordpress.org API URL returning info for
14
+ # this item. Subclasses override this; nil disables the lookup.
15
+ def wordpress_org_api_url
16
+ nil
17
+ end
18
+
19
+ # @return [ Hash ] Empty hash if the item is not on wordpress.org or the
20
+ # lookup fails.
21
+ def wordpress_org_data
22
+ return @wordpress_org_data if defined?(@wordpress_org_data)
23
+
24
+ @wordpress_org_data = fetch_wordpress_org_data
25
+ end
26
+
27
+ # Number of active installs as reported by the wordpress.org API.
28
+ # See https://codex.wordpress.org/WordPress.org_API
29
+ #
30
+ # @return [ Integer, nil ]
31
+ def active_installs
32
+ wordpress_org_data['active_installs']
33
+ end
34
+
35
+ private
36
+
37
+ # @return [ Hash ]
38
+ def fetch_wordpress_org_data
39
+ url = wordpress_org_api_url
40
+ return {} unless url
41
+
42
+ res = Browser.get(url, connecttimeout: WORDPRESS_ORG_API_TIMEOUT, timeout: WORDPRESS_ORG_API_TIMEOUT)
43
+ return {} unless res.code == 200
44
+
45
+ data = JSON.parse(res.body)
46
+ data.is_a?(Hash) ? data : {}
47
+ rescue StandardError
48
+ {}
49
+ end
50
+ end
51
+
52
+ include WordpressOrgData
53
+ end
54
+ end
55
+ end
@@ -6,8 +6,8 @@ module WPScan
6
6
  class WpItem
7
7
  include Vulnerable
8
8
  include Finders::Finding
9
- include CMSScanner::Target::Platform::PHP
10
- include CMSScanner::Target::Server::Generic
9
+ include WPScan::Target::Platform::PHP
10
+ include WPScan::Target::Server::Generic
11
11
 
12
12
  # Most common readme filenames, based on checking all public plugins and themes.
13
13
  READMES = %w[readme.txt README.txt README.md readme.md Readme.txt].freeze
@@ -71,9 +71,109 @@ module WPScan
71
71
  @popular ||= metadata['popular'] ? true : false
72
72
  end
73
73
 
74
- # @return [ String ]
74
+ # @return [ String, nil ]
75
75
  def last_updated
76
- @last_updated ||= metadata['last_updated']
76
+ resolve_last_updated unless defined?(@last_updated)
77
+ @last_updated
78
+ end
79
+
80
+ # @return [ String, nil ] 'db', 'wordpress.org', or nil when last_updated is unknown
81
+ def last_updated_source
82
+ resolve_last_updated unless defined?(@last_updated_source)
83
+ @last_updated_source
84
+ end
85
+
86
+ # WordPress.org takes precedence over local DB metadata since the WPScan
87
+ # DB is not synced in real time and may be stale.
88
+ def resolve_last_updated
89
+ if (api_value = wordpress_org_data['last_updated'])
90
+ @last_updated = api_value
91
+ @last_updated_source = 'WordPress.org'
92
+ elsif (db_value = metadata['last_updated'])
93
+ @last_updated = db_value
94
+ @last_updated_source = 'db'
95
+ else
96
+ @last_updated = nil
97
+ @last_updated_source = nil
98
+ end
99
+ end
100
+
101
+ # @return [ String, nil ] Human-friendly relative time hint for last_updated
102
+ # (e.g. "3 months ago"). nil when last_updated cannot be parsed.
103
+ def last_updated_relative
104
+ @last_updated_relative ||= relative_time_for(last_updated)
105
+ end
106
+
107
+ # Parenthesized annotation appended to the CLI "Last Updated" line, e.g.
108
+ # " (3 months ago, per wordpress.org)". Empty string when there is nothing
109
+ # to annotate.
110
+ #
111
+ # @return [ String ]
112
+ def last_updated_cli_suffix
113
+ parts = []
114
+ parts << last_updated_relative if last_updated_relative
115
+ parts << 'per WordPress.org' if last_updated_source == 'WordPress.org'
116
+ parts.empty? ? '' : " (#{parts.join(', ')})"
117
+ end
118
+
119
+ # ISO 8601 (UTC) representation of last_updated, used by the JSON output so
120
+ # downstream consumers always see a consistent format regardless of whether
121
+ # the value came from the local DB or the wordpress.org API.
122
+ #
123
+ # @return [ String, nil ]
124
+ def last_updated_iso
125
+ return @last_updated_iso if defined?(@last_updated_iso)
126
+
127
+ @last_updated_iso = parse_last_updated&.iso8601
128
+ end
129
+
130
+ # Friendly representation of last_updated, used in CLI output, matching the
131
+ # wordpress.org API style (e.g. "2026-04-14 12:01pm GMT"). Falls back to the
132
+ # raw string when the value cannot be parsed.
133
+ #
134
+ # @return [ String, nil ]
135
+ def last_updated_display
136
+ return @last_updated_display if defined?(@last_updated_display)
137
+
138
+ time = parse_last_updated
139
+ @last_updated_display = time ? time.strftime('%Y-%m-%d %-l:%M%P GMT') : last_updated
140
+ end
141
+
142
+ # @return [ Time, nil ] last_updated parsed as UTC Time, or nil
143
+ def parse_last_updated
144
+ value = last_updated
145
+ return nil if value.nil? || value.to_s.strip.empty?
146
+
147
+ Time.parse(value.to_s).utc
148
+ rescue ArgumentError, TypeError
149
+ nil
150
+ end
151
+
152
+ # @param [ String, nil ] value A timestamp parseable by Time.parse
153
+ # @return [ String, nil ]
154
+ def relative_time_for(value)
155
+ return nil if value.nil? || value.to_s.strip.empty?
156
+
157
+ time = Time.parse(value.to_s).utc
158
+ delta = Time.now.utc - time
159
+ return 'in the future' if delta.negative?
160
+
161
+ seconds = delta.to_i
162
+ case seconds
163
+ when 0...60 then 'just now'
164
+ when 60...3600 then pluralize_unit(seconds / 60, 'minute')
165
+ when 3600...86_400 then pluralize_unit(seconds / 3600, 'hour')
166
+ when 86_400...2_592_000 then pluralize_unit(seconds / 86_400, 'day')
167
+ when 2_592_000...31_536_000 then pluralize_unit(seconds / 2_592_000, 'month')
168
+ else pluralize_unit(seconds / 31_536_000, 'year')
169
+ end
170
+ rescue ArgumentError, TypeError
171
+ nil
172
+ end
173
+
174
+ # @return [ String ]
175
+ def pluralize_unit(count, unit)
176
+ "#{count} #{unit}#{'s' if count != 1} ago"
77
177
  end
78
178
 
79
179
  # @return [ Boolean ]
@@ -133,9 +233,9 @@ module WPScan
133
233
  #
134
234
  # @return [ Boolean ]
135
235
  def directory_listing?(path = nil, params = {})
136
- return if detection_opts[:mode] == :passive
236
+ return false if detection_opts[:mode] == :passive
137
237
 
138
- super(path, params)
238
+ super
139
239
  end
140
240
 
141
241
  # @param [ String ] path
@@ -143,12 +243,12 @@ module WPScan
143
243
  #
144
244
  # @return [ Boolean ]
145
245
  def error_log?(path = 'error_log', params = {})
146
- return if detection_opts[:mode] == :passive
246
+ return false if detection_opts[:mode] == :passive
147
247
 
148
- super(path, params)
248
+ super
149
249
  end
150
250
 
151
- # See CMSScanner::Target#head_and_get
251
+ # See WPScan::Target#head_and_get
152
252
  #
153
253
  # This is used by the error_log? above in the super()
154
254
  # to have the correct path (ie readme.txt checked from the plugin/theme location
@@ -3,13 +3,13 @@
3
3
  module WPScan
4
4
  module Model
5
5
  # WP Version
6
- class WpVersion < CMSScanner::Model::Version
6
+ class WpVersion < WPScan::Model::Version
7
7
  include Vulnerable
8
8
 
9
9
  def initialize(number, opts = {})
10
10
  raise Error::InvalidWordPressVersion unless WpVersion.valid?(number.to_s)
11
11
 
12
- super(number, opts)
12
+ super
13
13
  end
14
14
 
15
15
  # @param [ String ] number
@@ -2,9 +2,79 @@
2
2
 
3
3
  module WPScan
4
4
  module Model
5
- # Override of the CMSScanner::XMLRPC to include the references
6
- class XMLRPC < CMSScanner::Model::XMLRPC
7
- include References # To be able to use the :wpvulndb reference if needed
5
+ # XML-RPC interface.
6
+ class XMLRPC < InterestingFinding
7
+ include References
8
+
9
+ # @return [ String ]
10
+ def to_s
11
+ @to_s ||= "XML-RPC seems to be enabled: #{url}"
12
+ end
13
+
14
+ # @return [ Browser ]
15
+ def browser
16
+ @browser ||= WPScan::Browser.instance
17
+ end
18
+
19
+ # @return [ Array<String> ]
20
+ def available_methods
21
+ return @available_methods if @available_methods
22
+
23
+ @available_methods = []
24
+
25
+ res = method_call('system.listMethods').run
26
+ doc = Nokogiri::XML.parse(res.body)
27
+
28
+ doc.search('methodResponse params param value array data value string').each do |s|
29
+ @available_methods << s.text
30
+ end
31
+
32
+ @available_methods
33
+ end
34
+
35
+ # @return [ Boolean ] Whether or not the XMLRPC is enabled
36
+ def enabled?
37
+ !available_methods.empty?
38
+ end
39
+
40
+ # @param [ String ] method_name
41
+ # @param [ Array ] method_params
42
+ # @param [ Hash ] request_params
43
+ #
44
+ # @return [ Typhoeus::Request ]
45
+ def method_call(method_name, method_params = [], request_params = {})
46
+ browser.forge_request(
47
+ url,
48
+ request_params.merge(
49
+ method: :post,
50
+ body: ::XMLRPC::Create.new.methodCall(method_name, *method_params)
51
+ )
52
+ )
53
+ end
54
+
55
+ # @param [ Array<Array> ] methods_and_params
56
+ # @param [ Hash ] request_params
57
+ #
58
+ # Example of methods_and_params:
59
+ # [
60
+ # [method1, param1, param2],
61
+ # [method2, param1],
62
+ # [method3]
63
+ # ]
64
+ #
65
+ # @return [ Typhoeus::Request ]
66
+ def multi_call(methods_and_params = [], request_params = {})
67
+ browser.forge_request(
68
+ url,
69
+ request_params.merge(
70
+ method: :post,
71
+ body: ::XMLRPC::Create.new.methodCall(
72
+ 'system.multicall',
73
+ methods_and_params.collect { |m| { methodName: m[0], params: m[1..] } }
74
+ )
75
+ )
76
+ )
77
+ end
8
78
 
9
79
  # @return [ Hash ]
10
80
  def references
data/app/models.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  module WPScan
4
4
  module Model
5
- include CMSScanner::Model
6
5
  end
7
6
  end
8
7
 
@@ -10,9 +9,11 @@ require_relative 'models/interesting_finding'
10
9
  require_relative 'models/wp_version'
11
10
  require_relative 'models/xml_rpc'
12
11
  require_relative 'models/wp_item'
12
+ require_relative 'models/wp_item/wordpress_org_data'
13
13
  require_relative 'models/timthumb'
14
14
  require_relative 'models/media'
15
15
  require_relative 'models/plugin'
16
16
  require_relative 'models/theme'
17
17
  require_relative 'models/config_backup'
18
18
  require_relative 'models/db_export'
19
+ require_relative 'models/backup_folder'
@@ -0,0 +1,46 @@
1
+ # Windows
2
+ Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.0 Safari/532.5
3
+ Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.14 (KHTML, like Gecko) Chrome/9.0.601.0 Safari/534.14
4
+ Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.27 (KHTML, like Gecko) Chrome/12.0.712.0 Safari/534.27
5
+ Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.24 Safari/535.1
6
+ Mozilla/5.0 (Windows; U; Windows NT 5.1; tr; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 ( .NET CLR 3.5.30729; .NET4.0E)
7
+ Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
8
+ Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
9
+ Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
10
+ Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6
11
+ Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.1) Gecko/20100101 Firefox/10.0.1
12
+ Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20120403211507 Firefox/12.0
13
+ Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20120427 Firefox/15.0a1
14
+ Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)
15
+ Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
16
+ Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0)
17
+ Opera/9.80 (Windows NT 6.1; U; es-ES) Presto/2.9.181 Version/12.00
18
+ Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.19.4 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5
19
+
20
+ # MAC
21
+ Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.15 Safari/534.13
22
+ Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15
23
+ Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1
24
+ Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418.8 (KHTML, like Gecko) Safari/419.3
25
+ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3
26
+ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2; rv:10.0.1) Gecko/20100101 Firefox/10.0.1
27
+ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10
28
+
29
+ # Linux
30
+ Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.20 Safari/535.1
31
+ Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Ubuntu/10.10 Chromium/12.0.703.0 Chrome/12.0.703.0 Safari/534.24
32
+ Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.9) Gecko/20100915 Gentoo Firefox/3.6.9
33
+ Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1.16) Gecko/20120421 Gecko Firefox/11.0
34
+ Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0
35
+ Opera/9.80 (X11; Linux x86_64; U; pl) Presto/2.7.62 Version/11.00
36
+ Mozilla/5.0 (X11; U; Linux x86_64; us; rv:1.9.1.19) Gecko/20110430 shadowfox/7.0 (like Firefox/7.0
37
+
38
+ # iPad
39
+ Mozilla/5.0 (iPad; CPU OS 7_1_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D201 Safari/9537.53
40
+ Mozilla/5.0 (iPad; CPU OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53
41
+ Mozilla/5.0 (iPad; CPU OS 6_1_3 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25
42
+
43
+ # iPhone
44
+ Mozilla/5.0 (iPhone; CPU iPhone OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A465 Safari/9537.53
45
+ Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_3 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B511 Safari/9537.53
46
+ Mozilla/5.0 (iPhone; CPU iPhone OS 7_1_1 like Mac OS X) AppleWebKit/537.51.2 (KHTML, like Gecko) Version/7.0 Mobile/11D201 Safari/9537.53
@@ -6,9 +6,9 @@ _______________________________________________________________
6
6
  \ /\ / | | ____) | (__| (_| | | | |
7
7
  \/ \/ |_| |_____/ \___|\__,_|_| |_|
8
8
 
9
- WordPress Security Scanner by the WPScan Team
9
+ WordPress Security Scanner
10
10
  Version <%= WPScan::VERSION %>
11
- <%= ' ' * ((63 - WPScan::DB::Sponsor.text.length)/2) + WPScan::DB::Sponsor.text %>
12
- @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
11
+ An Automattic endeavor
12
+ https://automattic.com
13
13
  _______________________________________________________________
14
14
 
@@ -0,0 +1,15 @@
1
+ <%= info_icon %> Finished: <%= @stop_time.asctime %>
2
+ <%= info_icon %> Requests Done: <%= @requests_done %>
3
+ <%= info_icon %> Cached Requests: <%= @cached_requests %>
4
+ <% if @response_status_codes && !@response_status_codes.empty? -%>
5
+ <%= info_icon %> Most response codes received: <%= @response_status_codes.map { |label, count| "#{label}: #{count}" }.join(', ') %>
6
+ <% end -%>
7
+ <% if @response_status_codes_warnings && !@response_status_codes_warnings.empty? -%>
8
+ <% @response_status_codes_warnings.each do |warning| -%>
9
+ <%= warning_icon %> <%= warning %>
10
+ <% end -%>
11
+ <% end -%>
12
+ <%= info_icon %> Data Sent: <%= @data_sent.bytes_to_human %>
13
+ <%= info_icon %> Data Received: <%= @data_received.bytes_to_human %>
14
+ <%= info_icon %> Memory used: <%= @used_memory.bytes_to_human %>
15
+ <%= info_icon %> Elapsed time: <%= Time.at(@elapsed).utc.strftime('%H:%M:%S') %>
@@ -0,0 +1,4 @@
1
+ <%= @help %>
2
+ <% if @simple -%>
3
+ [!] To see full list of options use --hh.
4
+ <% end -%>
@@ -0,0 +1,11 @@
1
+ <%= info_icon %> URL: <%= @url %> [<%= @ip %>]
2
+ <% if @url != @effective_url -%>
3
+ <%= info_icon %> Effective URL: <%= @effective_url %>
4
+ <% end -%>
5
+ <%= info_icon %> Started: <%= @start_time.asctime %>
6
+ <%= info_icon %> Command Line: wpscan <%= @command_line %>
7
+ <%= info_icon %> Hostname: <%= @hostname %>
8
+ <% if WPScan::ParsedCli.exclude_vulns && !WPScan::ParsedCli.exclude_vulns.empty? -%>
9
+ <%= warning_icon %> Excluded vulnerabilities: <%= Array(WPScan::ParsedCli.exclude_vulns).join(', ') %>
10
+ <% end -%>
11
+
@@ -0,0 +1,11 @@
1
+ <% if @backup_folders.empty? -%>
2
+ <%= notice_icon %> No Backup Folders Found.
3
+ <% else -%>
4
+ <%= notice_icon %> Backup Folder(s) Identified:
5
+ <% @backup_folders.each do |backup_folder| -%>
6
+
7
+ <% icon = backup_folder.severity == :high ? critical_icon : warning_icon -%>
8
+ <%= icon %> <%= backup_folder %>
9
+ <%= render('@finding', item: backup_folder) -%>
10
+ <% end -%>
11
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <%= bar_clear -%>
2
+
3
+ <%= info_icon %> <%= @plugin %>
4
+ <%= render('@wp_item', wp_item: @plugin) -%>
5
+ |
6
+ <%= render('@finding', item: @plugin) -%>
7
+ |
8
+ <% if @plugin.version -%>
9
+ | Version: <%= @plugin.version %> (<%= @plugin.version.confidence %>% confidence)
10
+ <%= render('@finding', item: @plugin.version) -%>
11
+ <% else -%>
12
+ | The version could not be determined.
13
+ <% end -%>
@@ -4,17 +4,6 @@
4
4
  <% else -%>
5
5
  <%= notice_icon %> Plugin(s) Identified:
6
6
  <% @plugins.each do |plugin| -%>
7
-
8
- <%= info_icon %> <%= plugin %>
9
- <%= render('@wp_item', wp_item: plugin) -%>
10
- |
11
- <%= render('@finding', item: plugin) -%>
12
- |
13
- <% if plugin.version -%>
14
- | Version: <%= plugin.version %> (<%= plugin.version.confidence %>% confidence)
15
- <%= render('@finding', item: plugin.version) -%>
16
- <% else -%>
17
- | The version could not be determined.
18
- <% end -%>
7
+ <%= render('plugin', plugin: plugin) -%>
19
8
  <% end -%>
20
9
  <% end %>
@@ -0,0 +1,4 @@
1
+ <%= bar_clear -%>
2
+
3
+ <%= info_icon %> <%= @theme %>
4
+ <%= render('@theme', theme: @theme, show_parents: false) -%>
@@ -4,8 +4,6 @@
4
4
  <% else -%>
5
5
  <%= notice_icon %> Theme(s) Identified:
6
6
  <% @themes.each do |theme| -%>
7
-
8
- <%= info_icon %> <%= theme %>
9
- <%= render('@theme', theme: theme, show_parents: false) -%>
7
+ <%= render('theme', theme: theme) -%>
10
8
  <% end -%>
11
9
  <% end %>
@@ -0,0 +1,4 @@
1
+ <%= bar_clear -%>
2
+
3
+ <%= info_icon %> <%= @user %>
4
+ <%= render('@finding', item: @user) -%>
@@ -4,8 +4,6 @@
4
4
  <% else -%>
5
5
  <%= notice_icon %> User(s) Identified:
6
6
  <% @users.each do |user| -%>
7
-
8
- <%= info_icon %> <%= user %>
9
- <%= render('@finding', item: user) -%>
7
+ <%= render('user', user: user) -%>
10
8
  <% end -%>
11
9
  <% end %>
@@ -18,7 +18,7 @@
18
18
  <% end -%>
19
19
  <% end -%>
20
20
  <% end -%>
21
- <% if @item.respond_to?(:vulnerabilities) && !(vulns = @item.vulnerabilities).empty? -%>
21
+ <% if @item.respond_to?(:filtered_vulnerabilities) && !(vulns = @item.filtered_vulnerabilities).empty? -%>
22
22
  <% vulns_size = vulns.size -%>
23
23
  |
24
24
  | <%= critical_icon %> <%= vulns_size %> <%= vulns_size == 1 ? 'vulnerability' : 'vulnerabilities' %> identified:
@@ -0,0 +1,10 @@
1
+ <% unless @a.empty? -%>
2
+ <% if @a.size == 1 -%>
3
+ | <%= @s %>: <%= @a.first %>
4
+ <% else -%>
5
+ | <%= @p %>:
6
+ <% @a.each do |line| -%>
7
+ | - <%= line %>
8
+ <% end -%>
9
+ <% end -%>
10
+ <% end -%>
@@ -0,0 +1,23 @@
1
+ <% unless @findings.empty? -%>
2
+ Interesting Finding(s):
3
+ <% @findings.each do |finding| -%>
4
+
5
+ <%= info_icon %> <%= finding %>
6
+ <%= render('_array', a: finding.interesting_entries, s: 'Interesting Entry', p: 'Interesting Entries') -%>
7
+ | Found By: <%= finding.found_by %>
8
+ <% if finding.confidence > 0 -%>
9
+ | Confidence: <%= finding.confidence %>%
10
+ <% end -%>
11
+ <% unless (confirmed = finding.confirmed_by).empty? -%>
12
+ <% if confirmed.size == 1 -%>
13
+ | Confirmed By: <%= confirmed.first.found_by %><% if confirmed.first.confidence > 0 %>, <%= confirmed.first.confidence %>% confidence<% end %>
14
+ <% else -%>
15
+ | Confirmed By:
16
+ <% confirmed.each do |c| -%>
17
+ | - <%= c.found_by %><% if c.confidence > 0 %>, <%= c.confidence %>% confidence<% end %>
18
+ <% end -%>
19
+ <% end -%>
20
+ <% end -%>
21
+ <%= render('_array', a: finding.references_urls, s: 'Reference', p: 'References') -%>
22
+ <% end -%>
23
+ <% end %>
@@ -0,0 +1,5 @@
1
+
2
+ Scan Aborted: <%= @reason %>
3
+ <% if @verbose -%>
4
+ Trace: <%= @trace.join("\n") %>
5
+ <% end %>
@@ -0,0 +1,5 @@
1
+
2
+ Update Aborted: <%= @reason %>
3
+ <% if @verbose -%>
4
+ Trace: <%= @trace.join("\n") %>
5
+ <% end %>
@@ -1,6 +1,8 @@
1
1
  <% unless @status.empty? -%>
2
2
  <% if @status['http_error'] -%>
3
3
  <%= critical_icon %> WPScan DB API, <%= @status['http_error'].to_s %>
4
+ <% elsif @status['parse_error'] -%>
5
+ <%= critical_icon %> WPScan DB API returned an invalid response. Please check your network/proxy configuration or try again later.
4
6
  <% else -%>
5
7
  <%= info_icon %> WPScan DB API OK
6
8
  | Plan: <%= @status['plan'] %>
@@ -1,10 +1,16 @@
1
1
  | <%= critical_icon %> Title: <%= @v.title %>
2
+ <% if @v.uuid -%>
3
+ | UUID: <%= @v.uuid %>
4
+ <% end -%>
2
5
  <% if @v.cvss -%>
3
6
  | CVSS: <%= @v.cvss[:score] %> (<%= @v.cvss[:vector] %>)
4
7
  <% end -%>
5
8
  <% if @v.fixed_in -%>
6
9
  | Fixed in: <%= @v.fixed_in %>
7
10
  <% end -%>
11
+ <% if @v.poc -%>
12
+ | PoC: available
13
+ <% end -%>
8
14
  <% unless (references = @v.references_urls).empty? -%>
9
15
  <% if references.size == 1 -%>
10
16
  | Reference: <%= references.first %>
@@ -3,7 +3,10 @@
3
3
  | Latest Version: <%= @wp_item.latest_version %><% if @wp_item.version %> (up to date)<% end %>
4
4
  <% end -%>
5
5
  <% if @wp_item.last_updated -%>
6
- | Last Updated: <%= @wp_item.last_updated %>
6
+ | Last Updated: <%= @wp_item.last_updated_display %><%= @wp_item.last_updated_cli_suffix %>
7
+ <% end -%>
8
+ <% if @wp_item.active_installs -%>
9
+ | Active Installs: <%= ActiveSupport::NumberHelper.number_to_delimited(@wp_item.active_installs) %> (per WordPress.org)
7
10
  <% end -%>
8
11
  <% if @wp_item.readme_url -%>
9
12
  | Readme: <%= @wp_item.readme_url %>
@@ -1,11 +1,5 @@
1
1
  "banner": {
2
- "description": "WordPress Security Scanner by the WPScan Team",
2
+ "description": "WordPress Security Scanner",
3
3
  "version": <%= WPScan::VERSION.to_json %>,
4
- "authors": [
5
- "@_WPScan_",
6
- "@ethicalhack3r",
7
- "@erwan_lr",
8
- "@firefart"
9
- ],
10
- "sponsor": <%= WPScan::DB::Sponsor.text.to_json %>
4
+ "sponsor": "An Automattic endeavor"
11
5
  },
@@ -0,0 +1,13 @@
1
+ "stop_time": <%= @stop_time.to_i %>,
2
+ "elapsed": <%= @elapsed.to_i %>,
3
+ "requests_done": <%= @requests_done.to_i %>,
4
+ "cached_requests": <%= @cached_requests.to_i %>,
5
+ "response_status_codes": <%= @response_status_codes.to_json %>,
6
+ "response_status_codes_warning": <%= @response_status_codes_warning %>,
7
+ "response_status_codes_warnings": <%= @response_status_codes_warnings.to_json %>,
8
+ "data_sent": <%= @data_sent.to_i %>,
9
+ "data_sent_humanised": <%= @data_sent.bytes_to_human.to_json %>,
10
+ "data_received": <%= @data_received.to_i %>,
11
+ "data_received_humanised": <%= @data_received.bytes_to_human.to_json %>,
12
+ "used_memory": <%= @used_memory.to_i %>,
13
+ "used_memory_humanised": <%= @used_memory.bytes_to_human.to_json %>,
@@ -0,0 +1,4 @@
1
+ "help": <%= @help.to_s.to_json %>,
2
+ <% if @simple -%>
3
+ "full_help": "To see full list of options use --hh.",
4
+ <% end -%>
@@ -0,0 +1,10 @@
1
+ "start_time": <%= @start_time.to_i %>,
2
+ "start_memory": <%= @start_memory.to_i %>,
3
+ "command_line": <%= "wpscan #{@command_line}".to_json %>,
4
+ "hostname": <%= @hostname.to_s.to_json %>,
5
+ "target_url": <%= @url.to_s.to_json %>,
6
+ "target_ip": <%= @ip.to_s.to_json %>,
7
+ "effective_url": <%= @effective_url.to_s.to_json %>,
8
+ <% if WPScan::ParsedCli.exclude_vulns && !WPScan::ParsedCli.exclude_vulns.empty? -%>
9
+ "excluded_vulnerabilities": <%= Array(WPScan::ParsedCli.exclude_vulns).to_json %>,
10
+ <% end -%>