wpscan 3.0.8 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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