wpscan 3.0.8 → 3.1.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/README.md +1 -1
  4. data/app/controllers.rb +1 -0
  5. data/app/controllers/aliases.rb +12 -0
  6. data/app/controllers/core.rb +3 -5
  7. data/app/controllers/enumeration.rb +2 -28
  8. data/app/controllers/enumeration/enum_methods.rb +12 -2
  9. data/app/controllers/wp_version.rb +4 -0
  10. data/app/finders/main_theme/css_style.rb +2 -2
  11. data/app/finders/main_theme/urls_in_homepage.rb +3 -3
  12. data/app/finders/plugin_version.rb +1 -8
  13. data/app/finders/plugins.rb +13 -4
  14. data/app/finders/plugins/body_pattern.rb +27 -0
  15. data/app/finders/plugins/comment.rb +31 -0
  16. data/app/finders/plugins/config_parser.rb +31 -0
  17. data/app/finders/plugins/header_pattern.rb +41 -0
  18. data/app/finders/plugins/javascript_var.rb +29 -0
  19. data/app/finders/plugins/known_locations.rb +5 -5
  20. data/app/finders/plugins/query_parameter.rb +25 -0
  21. data/app/finders/plugins/urls_in_homepage.rb +4 -8
  22. data/app/finders/plugins/xpath.rb +29 -0
  23. data/app/finders/theme_version.rb +1 -1
  24. data/app/finders/theme_version/woo_framework_meta_generator.rb +2 -2
  25. data/app/finders/themes/known_locations.rb +5 -5
  26. data/app/finders/themes/urls_in_homepage.rb +2 -2
  27. data/app/finders/users/login_error_messages.rb +1 -4
  28. data/app/finders/users/wp_json_api.rb +2 -2
  29. data/app/finders/wp_items/urls_in_homepage.rb +1 -1
  30. data/app/finders/wp_version.rb +21 -18
  31. data/app/models/plugin.rb +4 -4
  32. data/app/models/theme.rb +6 -6
  33. data/app/models/timthumb.rb +1 -3
  34. data/app/models/wp_item.rb +15 -15
  35. data/app/views/json/enumeration/plugins.erb +1 -1
  36. data/app/views/json/enumeration/themes.erb +1 -1
  37. data/app/views/json/wp_item.erb +1 -1
  38. data/bin/wpscan +2 -1
  39. data/lib/wpscan/db.rb +14 -10
  40. data/lib/wpscan/db/dynamic_finders/base.rb +41 -0
  41. data/lib/wpscan/db/dynamic_finders/plugin.rb +111 -0
  42. data/lib/wpscan/db/dynamic_finders/theme.rb +16 -0
  43. data/lib/wpscan/db/dynamic_finders/wordpress.rb +75 -0
  44. data/lib/wpscan/db/updater.rb +2 -2
  45. data/lib/wpscan/finders.rb +13 -1
  46. data/lib/wpscan/finders/dynamic_finder/finder.rb +66 -0
  47. data/lib/wpscan/finders/dynamic_finder/version/body_pattern.rb +28 -0
  48. data/lib/wpscan/finders/dynamic_finder/version/comment.rb +16 -0
  49. data/lib/wpscan/finders/dynamic_finder/version/config_parser.rb +52 -0
  50. data/lib/wpscan/finders/dynamic_finder/version/finder.rb +29 -0
  51. data/lib/wpscan/finders/dynamic_finder/version/header_pattern.rb +28 -0
  52. data/lib/wpscan/finders/dynamic_finder/version/javascript_var.rb +56 -0
  53. data/lib/wpscan/finders/dynamic_finder/version/query_parameter.rb +62 -0
  54. data/lib/wpscan/finders/dynamic_finder/version/xpath.rb +34 -0
  55. data/lib/wpscan/finders/dynamic_finder/wp_item_version.rb +42 -0
  56. data/lib/wpscan/finders/dynamic_finder/wp_items/finder.rb +96 -0
  57. data/lib/wpscan/finders/dynamic_finder/wp_version.rb +60 -0
  58. data/lib/wpscan/helper.rb +11 -0
  59. data/lib/wpscan/target/platform/wordpress/custom_directories.rb +16 -1
  60. data/lib/wpscan/version.rb +1 -1
  61. metadata +32 -24
  62. data/app/finders/plugin_version/layer_slider/translation_file.rb +0 -40
  63. data/app/finders/plugin_version/revslider/release_log.rb +0 -35
  64. data/app/finders/plugin_version/shareaholic/meta_tag.rb +0 -27
  65. data/app/finders/plugin_version/sitepress_multilingual_cms/meta_generator.rb +0 -27
  66. data/app/finders/plugin_version/sitepress_multilingual_cms/version_parameter.rb +0 -31
  67. data/app/finders/plugin_version/w3_total_cache/headers.rb +0 -28
  68. data/app/finders/plugins/comments.rb +0 -31
  69. data/app/finders/plugins/headers.rb +0 -36
  70. data/app/finders/wp_version/homepage_stylesheet_numbers.rb +0 -59
  71. data/app/finders/wp_version/install_stylesheet_numbers.rb +0 -16
  72. data/app/finders/wp_version/meta_generator.rb +0 -27
  73. data/app/finders/wp_version/opml_generator.rb +0 -23
  74. data/app/finders/wp_version/sitemap_generator.rb +0 -23
  75. data/app/finders/wp_version/upgrade_stylesheet_numbers.rb +0 -13
  76. data/lib/wpscan/db/dynamic_finders.rb +0 -55
  77. data/lib/wpscan/finders/finder/plugin_version/comments.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 27668feb03359084f981bac99a0fdc0951835c33
4
- data.tar.gz: da0d1d819410d5d08d1455c7ffc55320a95d19cc
3
+ metadata.gz: 247fd91f253010fbefa767737b15d9abce9abdc5
4
+ data.tar.gz: bf4527c1501a3b641a4a242043baf8d302e0e5e8
5
5
  SHA512:
6
- metadata.gz: 607686c500d2f27f1136dce37e2ffb8987db83f100c10442a7f7519678d2f0d612680a8c8830a589af412f1425a28ee66f2036d748e472b6247630a6659376a5
7
- data.tar.gz: 852cf5b4f174e9288c2b91f06df5e10468f9a687dadc23efb73ce9fd2bd51141e41e2525687cb86880827d96314bf2bf1ec09520acbb969c586ba0e2e05b61f6
6
+ metadata.gz: 6f7ae471f65c4abab39653e95153a10c5893f7995f64b403bb9e4bac6748e93394daa4db9e4e2175293165dee60813a168af7f8043cbd249232a1b6abec00cfd
7
+ data.tar.gz: 86c27e7770b2674f286fcf55d49fe6b0cb2540d0a24a790e80c268b9901ab00cfd0d0e793ae294aebda586f414212a5077a1f58b489b272413c672833118dcab
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  WPScan Public Source License
2
2
 
3
- The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2017 WPScan Team.
3
+ The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2018 WPScan Team.
4
4
 
5
5
  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.
6
6
 
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  ## WPScan Public Source License
11
11
 
12
- The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2017 WPScan Team.
12
+ The WPScan software (henceforth referred to simply as "WPScan") is dual-licensed - Copyright 2011-2018 WPScan Team.
13
13
 
14
14
  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.
15
15
 
data/app/controllers.rb CHANGED
@@ -4,3 +4,4 @@ require_relative 'controllers/wp_version'
4
4
  require_relative 'controllers/main_theme'
5
5
  require_relative 'controllers/enumeration'
6
6
  require_relative 'controllers/brute_force'
7
+ require_relative 'controllers/aliases'
@@ -0,0 +1,12 @@
1
+ module WPScan
2
+ module Controller
3
+ # Controller to add the aliases in the CLI
4
+ class Aliases < CMSScanner::Controller::Base
5
+ def cli_options
6
+ [
7
+ OptAlias.new(['--stealthy'], alias_for: '--random-user-agent --detection-mode passive')
8
+ ]
9
+ end
10
+ end
11
+ end
12
+ end
@@ -46,14 +46,12 @@ module WPScan
46
46
  end
47
47
 
48
48
  def before_scan
49
- output('banner')
49
+ output('banner') unless parsed_options[:banner] == false
50
50
 
51
51
  update_db if update_db_required?
52
-
53
- super(false) # disable banner output
54
-
52
+ setup_cache
53
+ check_target_availability
55
54
  load_server_module
56
-
57
55
  check_wordpress_state
58
56
  end
59
57
 
@@ -6,34 +6,8 @@ module WPScan
6
6
  # Enumeration Controller
7
7
  class Enumeration < CMSScanner::Controller::Base
8
8
  def before_scan
9
- # Create the Dynamic PluginVersion Finders
10
- DB::DynamicPluginFinders.db_data.each do |name, config|
11
- %w[Comments].each do |klass|
12
- next unless config[klass] && config[klass]['version']
13
-
14
- constant_name = name.tr('-', '_').camelize
15
-
16
- unless Finders::PluginVersion.constants.include?(constant_name.to_sym)
17
- Finders::PluginVersion.const_set(constant_name, Module.new)
18
- end
19
-
20
- mod = WPScan::Finders::PluginVersion.const_get(constant_name)
21
-
22
- raise "#{mod} has already a #{klass} class" if mod.constants.include?(klass.to_sym)
23
-
24
- case klass
25
- when 'Comments' then create_plugins_comments_finders(mod, config[klass])
26
- end
27
- end
28
- end
29
- end
30
-
31
- def create_plugins_comments_finders(mod, config)
32
- mod.const_set(
33
- :Comments, Class.new(Finders::Finder::PluginVersion::Comments) do
34
- const_set(:PATTERN, config['pattern'])
35
- end
36
- )
9
+ DB::DynamicFinders::Plugin.create_versions_finders
10
+ DB::DynamicFinders::Theme.create_versions_finders
37
11
  end
38
12
 
39
13
  def run
@@ -52,7 +52,12 @@ module WPScan
52
52
  output('@info', msg: enum_message('plugins')) if user_interaction?
53
53
  # Enumerate the plugins & find their versions to avoid doing that when #version
54
54
  # is called in the view
55
- plugins = target.plugins(opts).each(&:version)
55
+ plugins = target.plugins(opts)
56
+
57
+ output('@info', msg: 'Checking Plugin Versions') if user_interaction? && !plugins.empty?
58
+
59
+ plugins.each(&:version)
60
+
56
61
  plugins.select!(&:vulnerable?) if parsed_options[:enumerate][:vulnerable_plugins]
57
62
 
58
63
  output('plugins', plugins: plugins)
@@ -90,7 +95,12 @@ module WPScan
90
95
  output('@info', msg: enum_message('themes')) if user_interaction?
91
96
  # Enumerate the themes & find their versions to avoid doing that when #version
92
97
  # is called in the view
93
- themes = target.themes(opts).each(&:version)
98
+ themes = target.themes(opts)
99
+
100
+ output('@info', msg: 'Checking Theme Versions') if user_interaction? && !themes.empty?
101
+
102
+ themes.each(&:version)
103
+
94
104
  themes.select!(&:vulnerable?) if parsed_options[:enumerate][:vulnerable_themes]
95
105
 
96
106
  output('themes', themes: themes)
@@ -15,6 +15,10 @@ module WPScan
15
15
  ]
16
16
  end
17
17
 
18
+ def before_scan
19
+ WPScan::DB::DynamicFinders::Wordpress.create_versions_finders
20
+ end
21
+
18
22
  def run
19
23
  output(
20
24
  'version',
@@ -5,9 +5,9 @@ module WPScan
5
5
  class CssStyle < CMSScanner::Finders::Finder
6
6
  include Finders::WpItems::URLsInHomepage
7
7
 
8
- def create_theme(name, style_url, opts)
8
+ def create_theme(slug, style_url, opts)
9
9
  WPScan::Theme.new(
10
- name,
10
+ slug,
11
11
  target,
12
12
  opts.merge(found_by: found_by, confidence: 70, style_url: style_url)
13
13
  )
@@ -11,10 +11,10 @@ module WPScan
11
11
  def passive(opts = {})
12
12
  found = []
13
13
 
14
- names = items_from_links('themes', false) + items_from_codes('themes', false)
14
+ slugs = items_from_links('themes', false) + items_from_codes('themes', false)
15
15
 
16
- names.each_with_object(Hash.new(0)) { |name, counts| counts[name] += 1 }.each do |name, occurences|
17
- found << WPScan::Theme.new(name, target, opts.merge(found_by: found_by, confidence: 2 * occurences))
16
+ slugs.each_with_object(Hash.new(0)) { |slug, counts| counts[slug] += 1 }.each do |slug, occurences|
17
+ found << WPScan::Theme.new(slug, target, opts.merge(found_by: found_by, confidence: 2 * occurences))
18
18
  end
19
19
 
20
20
  found
@@ -1,11 +1,4 @@
1
1
  require_relative 'plugin_version/readme'
2
- # Plugins Specific
3
- require_relative 'plugin_version/layer_slider/translation_file'
4
- require_relative 'plugin_version/revslider/release_log'
5
- require_relative 'plugin_version/sitepress_multilingual_cms/version_parameter'
6
- require_relative 'plugin_version/sitepress_multilingual_cms/meta_generator'
7
- require_relative 'plugin_version/w3_total_cache/headers'
8
- require_relative 'plugin_version/shareaholic/meta_tag'
9
2
 
10
3
  module WPScan
11
4
  module Finders
@@ -25,7 +18,7 @@ module WPScan
25
18
  #
26
19
  # @param [ WPScan::Plugin ] plugin
27
20
  def load_specific_finders(plugin)
28
- module_name = plugin.classify_name.to_sym
21
+ module_name = plugin.classify
29
22
 
30
23
  return unless Finders::PluginVersion.constants.include?(module_name)
31
24
 
@@ -1,7 +1,13 @@
1
1
  require_relative 'plugins/urls_in_homepage'
2
- require_relative 'plugins/headers'
3
- require_relative 'plugins/comments'
4
2
  require_relative 'plugins/known_locations'
3
+ # From the DynamicFinders
4
+ require_relative 'plugins/comment'
5
+ require_relative 'plugins/xpath'
6
+ require_relative 'plugins/header_pattern'
7
+ require_relative 'plugins/body_pattern'
8
+ require_relative 'plugins/javascript_var'
9
+ require_relative 'plugins/query_parameter'
10
+ require_relative 'plugins/config_parser' # Not loaded below as not implemented
5
11
 
6
12
  module WPScan
7
13
  module Finders
@@ -14,8 +20,11 @@ module WPScan
14
20
  def initialize(target)
15
21
  finders <<
16
22
  Plugins::UrlsInHomepage.new(target) <<
17
- Plugins::Headers.new(target) <<
18
- Plugins::Comments.new(target) <<
23
+ Plugins::HeaderPattern.new(target) <<
24
+ Plugins::Comment.new(target) <<
25
+ Plugins::Xpath.new(target) <<
26
+ Plugins::BodyPattern.new(target) <<
27
+ Plugins::JavascriptVar.new(target) <<
19
28
  Plugins::KnownLocations.new(target)
20
29
  end
21
30
  end
@@ -0,0 +1,27 @@
1
+ module WPScan
2
+ module Finders
3
+ module Plugins
4
+ # Plugins finder from Dynamic Finder 'BodyPattern'
5
+ class BodyPattern < WPScan::Finders::DynamicFinder::WpItems::Finder
6
+ DEFAULT_CONFIDENCE = 30
7
+
8
+ # @param [ Hash ] opts The options from the #passive, #aggressive methods
9
+ # @param [ Typhoeus::Response ] response
10
+ # @param [ String ] slug
11
+ # @param [ String ] klass
12
+ # @param [ Hash ] config The related dynamic finder config hash
13
+ #
14
+ # @return [ Plugin ] The detected plugin in the response, related to the config
15
+ def process_response(opts, response, slug, klass, config)
16
+ return unless response.body =~ config['pattern']
17
+
18
+ Plugin.new(
19
+ slug,
20
+ target,
21
+ opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ module WPScan
2
+ module Finders
3
+ module Plugins
4
+ # Plugins finder from the Dynamic Finder 'Comment'
5
+ class Comment < WPScan::Finders::DynamicFinder::WpItems::Finder
6
+ DEFAULT_CONFIDENCE = 30
7
+
8
+ # @param [ Hash ] opts The options from the #passive, #aggressive methods
9
+ # @param [ Typhoeus::Response ] response
10
+ # @param [ String ] slug
11
+ # @param [ String ] klass
12
+ # @param [ Hash ] config The related dynamic finder config hash
13
+ #
14
+ # @return [ Plugin ] The detected plugin in the response, related to the config
15
+ def process_response(opts, response, slug, klass, config)
16
+ response.html.xpath(config['xpath'] || '//comment()').each do |node|
17
+ comment = node.text.to_s.strip
18
+
19
+ next unless comment =~ config['pattern']
20
+
21
+ return Plugin.new(
22
+ slug,
23
+ target,
24
+ opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)
25
+ )
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module WPScan
2
+ module Finders
3
+ module Plugins
4
+ # Plugins finder from Dynamic Finder 'ConfigParser'
5
+ class ConfigParser < WPScan::Finders::DynamicFinder::WpItems::Finder
6
+ DEFAULT_CONFIDENCE = 40
7
+
8
+ # @param [ Hash ] opts The options from the #passive, #aggressive methods
9
+ # @param [ Typhoeus::Response ] response
10
+ # @param [ String ] slug
11
+ # @param [ String ] klass
12
+ # @param [ Hash ] config The related dynamic finder config hash
13
+ #
14
+ # @return [ Plugin ] The detected plugin in the response, related to the config
15
+ def _process_response(_opts, _response, slug, klass, config)
16
+ #
17
+ # TODO. Currently not implemented, and not even loaded by the Finders, as this
18
+ # finder only has an aggressive method, which has been disabled (globally)
19
+ # when checking for plugins
20
+ #
21
+
22
+ Plugin.new(
23
+ slug,
24
+ target,
25
+ opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ module WPScan
2
+ module Finders
3
+ module Plugins
4
+ # Plugins finder from Dynamic Finder 'HeaderPattern'
5
+ class HeaderPattern < WPScan::Finders::DynamicFinder::WpItems::Finder
6
+ DEFAULT_CONFIDENCE = 30
7
+
8
+ # @param [ Hash ] opts
9
+ #
10
+ # @return [ Array<Plugin> ]
11
+ def passive(opts = {})
12
+ found = []
13
+ headers = target.homepage_res.headers
14
+
15
+ return found if headers.empty?
16
+
17
+ DB::DynamicFinders::Plugin.passive_header_pattern_finder_configs.each do |slug, configs|
18
+ configs.each do |klass, config|
19
+ next unless headers[config['header']] && headers[config['header']].to_s =~ config['pattern']
20
+
21
+ found << Plugin.new(
22
+ slug,
23
+ target,
24
+ opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)
25
+ )
26
+ end
27
+ end
28
+
29
+ found
30
+ end
31
+
32
+ # @param [ Hash ] opts
33
+ #
34
+ # @return [ nil ]
35
+ def aggressive(_opts = {})
36
+ # None
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ module WPScan
2
+ module Finders
3
+ module Plugins
4
+ # Plugins finder from the Dynamic Finder 'JavascriptVar'
5
+ class JavascriptVar < WPScan::Finders::DynamicFinder::WpItems::Finder
6
+ DEFAULT_CONFIDENCE = 60
7
+
8
+ # @param [ Hash ] opts The options from the #passive, #aggressive methods
9
+ # @param [ Typhoeus::Response ] response
10
+ # @param [ String ] slug
11
+ # @param [ String ] klass
12
+ # @param [ Hash ] config The related dynamic finder config hash
13
+ #
14
+ # @return [ Plugin ] The detected plugin in the response, related to the config
15
+ def process_response(opts, response, slug, klass, config)
16
+ response.html.xpath(config['xpath'] || '//script[not(@src)]').each do |node|
17
+ next if config['pattern'] && !node.text.match(config['pattern'])
18
+
19
+ return Plugin.new(
20
+ slug,
21
+ target,
22
+ opts.merge(found_by: found_by(klass), confidence: config['confidence'] || DEFAULT_CONFIDENCE)
23
+ )
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -12,12 +12,12 @@ module WPScan
12
12
  def aggressive(opts = {})
13
13
  found = []
14
14
 
15
- enumerate(target_urls(opts), opts) do |res, name|
15
+ enumerate(target_urls(opts), opts) do |res, slug|
16
16
  # TODO: follow the location (from enumerate()) and remove the 301 here ?
17
17
  # As a result, it might remove false positive due to redirection to the homepage
18
18
  next unless [200, 401, 403, 301].include?(res.code)
19
19
 
20
- found << WPScan::Plugin.new(name, target, opts.merge(found_by: found_by, confidence: 80))
20
+ found << WPScan::Plugin.new(slug, target, opts.merge(found_by: found_by, confidence: 80))
21
21
  end
22
22
 
23
23
  found
@@ -28,12 +28,12 @@ module WPScan
28
28
  #
29
29
  # @return [ Hash ]
30
30
  def target_urls(opts = {})
31
- names = opts[:list] || DB::Plugins.vulnerable_slugs
31
+ slugs = opts[:list] || DB::Plugins.vulnerable_slugs
32
32
  urls = {}
33
33
  plugins_url = target.plugins_url
34
34
 
35
- names.each do |name|
36
- urls["#{plugins_url}#{URI.encode(name)}/"] = name
35
+ slugs.each do |slug|
36
+ urls["#{plugins_url}#{URI.encode(slug)}/"] = slug
37
37
  end
38
38
 
39
39
  urls
@@ -0,0 +1,25 @@
1
+ module WPScan
2
+ module Finders
3
+ module Plugins
4
+ # Plugins finder from Dynamic Finder 'QueryParameter'
5
+ class QueryParameter < WPScan::Finders::DynamicFinder::WpItems::Finder
6
+ DEFAULT_CONFIDENCE = 10
7
+
8
+ def passive(_opts = {})
9
+ # Handled by UrlsInHomePage, so no need to check this twice
10
+ end
11
+
12
+ # @param [ Hash ] opts The options from the #passive, #aggressive methods
13
+ # @param [ Typhoeus::Response ] response
14
+ # @param [ String ] slug
15
+ # @param [ String ] klass
16
+ # @param [ Hash ] config The related dynamic finder config hash
17
+ #
18
+ # @return [ Plugin ] The detected plugin in the response, related to the config
19
+ def process_response(opts, response, slug, klass, config)
20
+ # TODO: when a real case will be found
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end