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
@@ -0,0 +1,111 @@
1
+ module WPScan
2
+ module DB
3
+ module DynamicFinders
4
+ class Plugin < Base
5
+ # @return [ Hash ]
6
+ def self.db_data
7
+ @db_data ||= super['plugins'] || {}
8
+ end
9
+
10
+ def self.version_finder_module
11
+ Finders::PluginVersion
12
+ end
13
+
14
+ # @param [ Symbol ] finder_class
15
+ # @param [ Boolean ] aggressive
16
+ # @return [ Hash ]
17
+ def self.finder_configs(finder_class, aggressive = false)
18
+ configs = {}
19
+
20
+ return configs unless allowed_classes.include?(finder_class)
21
+
22
+ db_data.each do |slug, finders|
23
+ # Quite sure better can be done with some kind of logic statement in the select
24
+ fs = if aggressive
25
+ finders.reject { |_f, c| c['path'].nil? }
26
+ else
27
+ finders.select { |_f, c| c['path'].nil? }
28
+ end
29
+
30
+ fs.each do |finder_name, config|
31
+ klass = config['class'] ? config['class'] : finder_name
32
+
33
+ next unless klass.to_sym == finder_class
34
+
35
+ configs[slug] ||= {}
36
+ configs[slug][finder_name] = config
37
+ end
38
+ end
39
+
40
+ configs
41
+ end
42
+
43
+ # @return [ Hash ]
44
+ def self.versions_finders_configs
45
+ return @versions_finders_configs if @versions_finders_configs
46
+
47
+ @versions_finders_configs = {}
48
+
49
+ db_data.each do |slug, finders|
50
+ finders.each do |finder_name, config|
51
+ next unless config.key?('version')
52
+
53
+ @versions_finders_configs[slug] ||= {}
54
+ @versions_finders_configs[slug][finder_name] = config
55
+ end
56
+ end
57
+
58
+ @versions_finders_configs
59
+ end
60
+
61
+ # @param [ String ] slug
62
+ # @return [ Constant ]
63
+ def self.maybe_create_modudle(slug)
64
+ # What about slugs such as js_composer which will be done as JsComposer, just like js-composer
65
+ constant_name = classify_slug(slug)
66
+
67
+ unless version_finder_module.constants.include?(constant_name)
68
+ version_finder_module.const_set(constant_name, Module.new)
69
+ end
70
+
71
+ version_finder_module.const_get(constant_name)
72
+ end
73
+
74
+ def self.create_versions_finders
75
+ versions_finders_configs.each do |slug, finders|
76
+ # Kind of an issue here, module is created even if there is no valid classes
77
+ # Could put the #maybe_ directly in the #send() BUT it would be checked everytime,
78
+ # which is kind of a waste
79
+ mod = maybe_create_modudle(slug)
80
+
81
+ finders.each do |finder_class, config|
82
+ klass = config['class'] ? config['class'] : finder_class
83
+
84
+ # Instead of raising exceptions, skip unallowed/already defined finders
85
+ # So that, when new DF configs are put in the .yml
86
+ # users with old version of WPScan will still be able to scan blogs
87
+ # when updating the DB but not the tool
88
+ next if mod.constants.include?(finder_class.to_sym) ||
89
+ !allowed_classes.include?(klass.to_sym)
90
+
91
+ version_finder_super_class(klass).create_child_class(mod, finder_class.to_sym, config)
92
+ end
93
+ end
94
+ end
95
+
96
+ # The idea here would be to check if the class exist in
97
+ # the Finders::DynamicFinders::Plugins/Themes::klass or WpItemVersion::klass
98
+ # and return the related constant when one has been found.
99
+ #
100
+ # So far, the Finders::DynamicFinders::WPItemVersion is enought
101
+ # as nothing else is used
102
+ #
103
+ # @param [ String, Symbol ] klass
104
+ # @return [ Constant ]
105
+ def self.version_finder_super_class(klass)
106
+ "WPScan::Finders::DynamicFinder::WpItemVersion::#{klass}".constantize
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,16 @@
1
+ module WPScan
2
+ module DB
3
+ module DynamicFinders
4
+ class Theme < Plugin
5
+ # @return [ Hash ]
6
+ def self.db_data
7
+ @db_data ||= super['themes'] || {}
8
+ end
9
+
10
+ def self.version_finder_module
11
+ Finders::ThemeVersion
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,75 @@
1
+ module WPScan
2
+ module DB
3
+ module DynamicFinders
4
+ class Wordpress < Base
5
+ # @return [ Hash ]
6
+ def self.db_data
7
+ @db_data ||= super['wordpress'] || {}
8
+ end
9
+
10
+ # @return [ Constant ]
11
+ def self.version_finder_module
12
+ Finders::WpVersion
13
+ end
14
+
15
+ # @return [ Array<Symbol> ]
16
+ def self.allowed_classes
17
+ @allowed_classes ||= %i[
18
+ Comment Xpath HeaderPattern BodyPattern JavascriptVar QueryParameter WpItemQueryParameter
19
+ ]
20
+ end
21
+
22
+ # @param [ Symbol ] finder_class
23
+ # @param [ Boolean ] aggressive
24
+ # @return [ Hash ]
25
+ def self.finder_configs(finder_class, aggressive = false)
26
+ configs = {}
27
+
28
+ return configs unless allowed_classes.include?(finder_class)
29
+
30
+ finders = if aggressive
31
+ db_data.reject { |_f, c| c['path'].nil? }
32
+ else
33
+ db_data.select { |_f, c| c['path'].nil? }
34
+ end
35
+
36
+ finders.each do |finder_name, config|
37
+ klass = config['class'] ? config['class'] : finder_name
38
+
39
+ next unless klass.to_sym == finder_class
40
+
41
+ configs[finder_name] = config
42
+ end
43
+
44
+ configs
45
+ end
46
+
47
+ # @return [ Hash ]
48
+ def self.versions_finders_configs
49
+ @versions_finders_configs ||= db_data.select { |_finder_name, config| config.key?('version') }
50
+ end
51
+
52
+ def self.create_versions_finders
53
+ versions_finders_configs.each do |finder_class, config|
54
+ klass = config['class'] ? config['class'] : finder_class
55
+
56
+ # Instead of raising exceptions, skip unallowed/already defined finders
57
+ # So that, when new DF configs are put in the .yml
58
+ # users with old version of WPScan will still be able to scan blogs
59
+ # when updating the DB but not the tool
60
+ next if version_finder_module.constants.include?(finder_class.to_sym) ||
61
+ !allowed_classes.include?(klass.to_sym)
62
+
63
+ version_finder_super_class(klass).create_child_class(version_finder_module, finder_class.to_sym, config)
64
+ end
65
+ end
66
+
67
+ # @param [ String, Symbol ] klass
68
+ # @return [ Constant ]
69
+ def self.version_finder_super_class(klass)
70
+ "WPScan::Finders::DynamicFinder::WpVersion::#{klass}".constantize
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -7,10 +7,10 @@ module WPScan
7
7
  FILES = %w[
8
8
  plugins.json themes.json wordpresses.json
9
9
  timthumbs-v3.txt user-agents.txt config_backups.txt
10
- dynamic_finders_01.yml wp_fingerprints.json LICENSE
10
+ dynamic_finders.yml wp_fingerprints.json LICENSE
11
11
  ].freeze
12
12
 
13
- OLD_FILES = %w[wordpress.db dynamic_finders.yml].freeze
13
+ OLD_FILES = %w[wordpress.db dynamic_finders_01.yml].freeze
14
14
 
15
15
  attr_reader :repo_directory
16
16
 
@@ -1,5 +1,17 @@
1
1
  require 'wpscan/finders/finder/wp_version/smart_url_checker'
2
- require 'wpscan/finders/finder/plugin_version/comments'
2
+
3
+ require 'wpscan/finders/dynamic_finder/finder'
4
+ require 'wpscan/finders/dynamic_finder/wp_items/finder'
5
+ require 'wpscan/finders/dynamic_finder/version/finder'
6
+ require 'wpscan/finders/dynamic_finder/version/xpath'
7
+ require 'wpscan/finders/dynamic_finder/version/comment'
8
+ require 'wpscan/finders/dynamic_finder/version/header_pattern'
9
+ require 'wpscan/finders/dynamic_finder/version/body_pattern'
10
+ require 'wpscan/finders/dynamic_finder/version/javascript_var'
11
+ require 'wpscan/finders/dynamic_finder/version/query_parameter'
12
+ require 'wpscan/finders/dynamic_finder/version/config_parser'
13
+ require 'wpscan/finders/dynamic_finder/wp_item_version'
14
+ require 'wpscan/finders/dynamic_finder/wp_version'
3
15
 
4
16
  module WPScan
5
17
  # Custom Finders
@@ -0,0 +1,66 @@
1
+ module WPScan
2
+ module Finders
3
+ module DynamicFinder
4
+ # To be used as a base when creating a dynamic finder
5
+ class Finder < CMSScanner::Finders::Finder
6
+ # @param [ Array ] args
7
+ def self.child_class_constant(*args)
8
+ args.each do |arg|
9
+ if arg.is_a?(Hash)
10
+ child_class_constants.merge!(arg)
11
+ else
12
+ child_class_constants[arg] = nil
13
+ end
14
+ end
15
+ end
16
+
17
+ # Needed to have inheritance of the @child_class_constants
18
+ # If inheritance is not needed, then the #child_class_constant can be used in the classe definition, ie
19
+ # child_class_constant :FILES, PATTERN: /aaa/i
20
+ # @return [ Hash ]
21
+ def self.child_class_constants
22
+ @child_class_constants ||= { PATH: nil }
23
+ end
24
+
25
+ # @param [ Constant ] mod
26
+ # @param [ Constant ] klass
27
+ # @param [ Hash ] config
28
+ def self.create_child_class(mod, klass, config)
29
+ # Can't use the #child_class_constants directly in the Class.new(self) do; end below
30
+ class_constants = child_class_constants
31
+
32
+ mod.const_set(
33
+ klass, Class.new(self) do
34
+ class_constants.each do |key, value|
35
+ const_set(key, config[key.downcase.to_s] || value)
36
+ end
37
+ end
38
+ )
39
+ end
40
+
41
+ # This method has to be overriden in child classes
42
+ #
43
+ # @param [ Typhoeus::Response ] response
44
+ # @param [ Hash ] opts
45
+ # @return [ Mixed ]
46
+ def find(_response, _opts = {})
47
+ raise NoMethodError
48
+ end
49
+
50
+ # @param [ Hash ] opts
51
+ def passive(opts = {})
52
+ return if self.class::PATH
53
+
54
+ find(target.homepage_res, opts)
55
+ end
56
+
57
+ # @param [ Hash ] opts
58
+ def aggressive(opts = {})
59
+ return unless self.class::PATH
60
+
61
+ find(Browser.get(target.url(self.class::PATH)), opts)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,28 @@
1
+ module WPScan
2
+ module Finders
3
+ module DynamicFinder
4
+ module Version
5
+ # Version finder using Body Pattern method. Tipically used when the response is not
6
+ # an HTML doc and Xpath can't be used
7
+ class BodyPattern < WPScan::Finders::DynamicFinder::Version::Finder
8
+ # @return [ Hash ]
9
+ def self.child_class_constants
10
+ @child_class_constants ||= super().merge(PATTERN: nil, CONFIDENCE: 60)
11
+ end
12
+
13
+ # @param [ Typhoeus::Response ] response
14
+ # @param [ Hash ] opts
15
+ # @return [ Version ]
16
+ def find(response, _opts = {})
17
+ return unless response.body =~ self.class::PATTERN
18
+
19
+ create_version(
20
+ Regexp.last_match[:v],
21
+ interesting_entries: ["#{response.effective_url}, Match: '#{Regexp.last_match}'"]
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ module WPScan
2
+ module Finders
3
+ module DynamicFinder
4
+ module Version
5
+ # Version finder in Comment, which is basically an Xpath one with a default
6
+ # Xpath of //comment()
7
+ class Comment < WPScan::Finders::DynamicFinder::Version::Xpath
8
+ # @return [ Hash ]
9
+ def self.child_class_constants
10
+ @child_class_constants ||= super().merge(PATTERN: nil, XPATH: '//comment()')
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,52 @@
1
+ module WPScan
2
+ module Finders
3
+ module DynamicFinder
4
+ module Version
5
+ # Version finder using by parsing config files, such as composer.json
6
+ # and so on
7
+ class ConfigParser < WPScan::Finders::DynamicFinder::Version::Finder
8
+ ALLOWED_PARSERS = [JSON, YAML].freeze
9
+
10
+ def self.child_class_constants
11
+ @child_class_constants ||= super.merge(
12
+ PARSER: nil, KEY: nil, PATTERN: /(?<v>\d+\.[\.\d]+)/, CONFIDENCE: 70
13
+ )
14
+ end
15
+
16
+ # @param [ String ] body
17
+ # @return [ Hash, nil ] The parsed body, with an available parser, if possible
18
+ def parse(body)
19
+ parsers = ALLOWED_PARSERS.include?(self.class::PARSER) ? [self.class::PARSER] : ALLOWED_PARSERS
20
+
21
+ parsers.each do |parser|
22
+ begin
23
+ return parser.respond_to?(:safe_load) ? parser.safe_load(body) : parser.load(body)
24
+ rescue StandardError
25
+ next
26
+ end
27
+ end
28
+
29
+ nil # Make sure nil is returned in case none of the parsers manage to parse the body correctly
30
+ end
31
+
32
+ # No Passive way
33
+ def passive(opts = {}); end
34
+
35
+ # @param [ Typhoeus::Response ] response
36
+ # @param [ Hash ] opts
37
+ # @return [ Version ]
38
+ def find(response, _opts = {})
39
+ parsed_body = parse(response.body)
40
+
41
+ return unless (data = parsed_body&.dig(*self.class::KEY.split(':'))) && data =~ self.class::PATTERN
42
+
43
+ create_version(
44
+ Regexp.last_match[:v],
45
+ interesting_entries: ["#{response.effective_url}, Match: '#{Regexp.last_match}'"]
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ module WPScan
2
+ module Finders
3
+ module DynamicFinder
4
+ module Version
5
+ # To be used as a base when creating
6
+ # a dynamic finder to find the version of a WP Item (such as theme/plugin)
7
+ class Finder < Finders::DynamicFinder::Finder
8
+ protected
9
+
10
+ # @param [ String ] number
11
+ # @param [ Hash ] finding_opts
12
+ # @return [ WPScan::Version ]
13
+ def create_version(number, finding_opts)
14
+ WPScan::Version.new(number, version_finding_opts(finding_opts))
15
+ end
16
+
17
+ # @param [ Hash ] opts
18
+ # @retutn [ Hash ]
19
+ def version_finding_opts(opts)
20
+ opts[:found_by] ||= found_by
21
+ opts[:confidence] ||= self.class::CONFIDENCE
22
+
23
+ opts
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ module WPScan
2
+ module Finders
3
+ module DynamicFinder
4
+ module Version
5
+ # Version finder using Header Pattern method
6
+ class HeaderPattern < WPScan::Finders::DynamicFinder::Version::Finder
7
+ # @return [ Hash ]
8
+ def self.child_class_constants
9
+ @child_class_constants ||= super().merge(HEADER: nil, PATTERN: nil, CONFIDENCE: 60)
10
+ end
11
+
12
+ # @param [ Typhoeus::Response ] response
13
+ # @param [ Hash ] opts
14
+ # @return [ Version ]
15
+ def find(response, _opts = {})
16
+ return unless response.headers && response.headers[self.class::HEADER]
17
+ return unless response.headers[self.class::HEADER].to_s =~ self.class::PATTERN
18
+
19
+ create_version(
20
+ Regexp.last_match[:v],
21
+ interesting_entries: ["#{response.effective_url}, Match: '#{Regexp.last_match}'"]
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end