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
@@ -1,40 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module PluginVersion
4
- module LayerSlider
5
- # Version from a Translation file
6
- #
7
- # See https://github.com/wpscanteam/wpscan/issues/765
8
- class TranslationFile < CMSScanner::Finders::Finder
9
- # @param [ Hash ] opts
10
- #
11
- # @return [ Version ]
12
- def aggressive(_opts = {})
13
- potential_urls.each do |url|
14
- res = Browser.get(url)
15
-
16
- next unless res.code == 200 && res.body =~ /Project-Id-Version: LayerSlider WP v?([0-9\.][^\\\s]+)/
17
-
18
- return WPScan::Version.new(
19
- Regexp.last_match[1],
20
- found_by: 'Translation File (Aggressive Detection)',
21
- confidence: 90,
22
- interesting_entries: ["#{url}, Match: '#{Regexp.last_match}'"]
23
- )
24
- end
25
- nil
26
- end
27
-
28
- # @return [ Array<String> ] The potential URLs where the version is disclosed
29
- def potential_urls
30
- # Recent versions seem to use the 'locales' directory instead of the 'languages' one.
31
- # Maybe also check other locales ?
32
- %w[locales languages].reduce([]) do |a, e|
33
- a << target.url("#{e}/LayerSlider-en_US.po")
34
- end
35
- end
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,35 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module PluginVersion
4
- module Revslider
5
- # Version from the release_log.html
6
- #
7
- # See https://github.com/wpscanteam/wpscan/issues/817
8
- class ReleaseLog < CMSScanner::Finders::Finder
9
- # @param [ Hash ] opts
10
- #
11
- # @return [ Version ]
12
- def aggressive(_opts = {})
13
- res = Browser.get(release_log_url)
14
-
15
- res.html.css('h3.version-number:first').each do |node|
16
- next unless node.text =~ /\AVersion ([0-9\.]+).*\z/i
17
-
18
- return WPScan::Version.new(
19
- Regexp.last_match[1],
20
- found_by: found_by,
21
- confidence: 90,
22
- interesting_entries: ["#{release_log_url}, Match: '#{Regexp.last_match}'"]
23
- )
24
- end
25
- nil
26
- end
27
-
28
- def release_log_url
29
- target.url('release_log.html')
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,27 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module PluginVersion
4
- module Shareaholic
5
- # Version from the meta
6
- class MetaTag < CMSScanner::Finders::Finder
7
- # @param [ Hash ] opts
8
- #
9
- # @return [ Version ]
10
- def passive(_opts = {})
11
- target.target.homepage_res.html.css('meta[name="shareaholic:wp_version"]').each do |node|
12
- next unless node['content'] =~ /\A([0-9\.]+)/i
13
-
14
- return WPScan::Version.new(
15
- Regexp.last_match(1),
16
- found_by: found_by,
17
- confidence: 50,
18
- interesting_entries: ["#{target.target.url}, Match: '#{node.to_s.strip}'"]
19
- )
20
- end
21
- nil
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,27 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module PluginVersion
4
- module SitepressMultilingualCms
5
- # Version from the meta generator
6
- class MetaGenerator < CMSScanner::Finders::Finder
7
- # @param [ Hash ] opts
8
- #
9
- # @return [ Version ]
10
- def passive(_opts = {})
11
- target.target.homepage_res.html.css('meta[name="generator"]').each do |node|
12
- next unless node['content'] =~ /\AWPML\sver:([0-9\.]+)\sstt/i
13
-
14
- return WPScan::Version.new(
15
- Regexp.last_match(1),
16
- found_by: 'Meta Generator (Passive detection)',
17
- confidence: 50,
18
- interesting_entries: ["#{target.target.url}, Match: '#{node}'"]
19
- )
20
- end
21
- nil
22
- end
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,31 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module PluginVersion
4
- module SitepressMultilingualCms
5
- # Version from the v parameter in href / src of stylesheets / scripts
6
- class VersionParameter < CMSScanner::Finders::Finder
7
- # @param [ Hash ] opts
8
- #
9
- # @return [ Version ]
10
- def passive(_opts = {})
11
- pattern = %r{#{Regexp.escape(target.target.plugins_dir)}/sitepress-multilingual-cms/}i
12
-
13
- target.target.in_scope_urls(target.target.homepage_res, '//link|//script') do |url|
14
- uri = Addressable::URI.parse(url)
15
-
16
- next unless uri.path =~ pattern && uri.query =~ /v=([0-9\.]+)/
17
-
18
- return WPScan::Version.new(
19
- Regexp.last_match[1],
20
- found_by: found_by,
21
- confidence: 50,
22
- interesting_entries: [url]
23
- )
24
- end
25
- nil
26
- end
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,28 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module PluginVersion
4
- module W3TotalCache
5
- # Version from Headers
6
- class Headers < CMSScanner::Finders::Finder
7
- PATTERN = %r{W3 Total Cache/([0-9.]+)}i
8
-
9
- # @param [ Hash ] opts
10
- #
11
- # @return [ Version ]
12
- def passive(_opts = {})
13
- headers = target.target.headers
14
-
15
- return unless headers && headers['X-Powered-By'].to_s =~ PATTERN
16
-
17
- WPScan::Version.new(
18
- Regexp.last_match[1],
19
- found_by: found_by,
20
- confidence: 80,
21
- interesting_entries: ["#{target.target.url}, Match: '#{Regexp.last_match}'"]
22
- )
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,31 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module Plugins
4
- # Plugins from Comments Finder
5
- class Comments < CMSScanner::Finders::Finder
6
- # @param [ Hash ] opts
7
- # @option opts [ Boolean ] :unique Default: true
8
- #
9
- # @return [ Array<Plugin> ]
10
- def passive(opts = {})
11
- found = []
12
- opts[:unique] = true unless opts.key?(:unique)
13
-
14
- target.homepage_res.html.xpath('//comment()').each do |node|
15
- comment = node.text.to_s.strip
16
-
17
- DB::DynamicPluginFinders.comments.each do |name, config|
18
- next unless comment =~ config['pattern']
19
-
20
- plugin = WPScan::Plugin.new(name, target, opts.merge(found_by: found_by, confidence: 70))
21
-
22
- found << plugin unless opts[:unique] && found.include?(plugin)
23
- end
24
- end
25
-
26
- found
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,36 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module Plugins
4
- # Plugins from Headers Finder
5
- class Headers < CMSScanner::Finders::Finder
6
- # @param [ Hash ] opts
7
- #
8
- # @return [ Array<Plugin> ]
9
- def passive(opts = {})
10
- plugin_names_from_headers(opts).reduce([]) do |a, e|
11
- a << WPScan::Plugin.new(e, target, opts.merge(found_by: found_by, confidence: 60))
12
- end
13
- end
14
-
15
- # X-Powered-By: W3 Total Cache/0.9.2.5
16
- # WP-Super-Cache: Served supercache file from PHP
17
- #
18
- # @return [ Array<String> ]
19
- def plugin_names_from_headers(_opts = {})
20
- found = []
21
- headers = target.homepage_res.headers
22
-
23
- if headers
24
- powered_by = headers['X-Powered-By'].to_s
25
- wp_super_cache = headers['wp-super-cache'].to_s
26
-
27
- found << 'w3-total-cache' if powered_by =~ Finders::PluginVersion::W3TotalCache::Headers::PATTERN
28
- found << 'wp-super-cache' if wp_super_cache =~ /supercache/i
29
- end
30
-
31
- found
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,59 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module WpVersion
4
- # Stylesheets Version Finder from Homepage
5
- #
6
- # TODO: Maybe put such methods in the CMSScanner to have a generic
7
- # way of getting those versions, and allow the confidence to be
8
- # customised
9
- class HomepageStylesheetNumbers < CMSScanner::Finders::Finder
10
- # @return [ Array<WpVersion> ]
11
- def passive(_opts = {})
12
- wp_versions(target.homepage_url)
13
- end
14
-
15
- protected
16
-
17
- # @param [ String ] url
18
- #
19
- # @return [ Array<WpVersion> ]
20
- def wp_versions(url)
21
- found = []
22
-
23
- scan_page(url).each do |version_number, occurences|
24
- next unless WPScan::WpVersion.valid?(version_number) # Skip invalid versions
25
-
26
- found << WPScan::WpVersion.new(
27
- version_number,
28
- found_by: found_by,
29
- confidence: 5 * occurences.count,
30
- interesting_entries: occurences
31
- )
32
- end
33
-
34
- found
35
- end
36
-
37
- # @param [ String ] url
38
- #
39
- # @return [ Hash ]
40
- def scan_page(url)
41
- found = {}
42
- pattern = /\bver=([0-9\.]+)/i
43
-
44
- target.in_scope_urls(Browser.get(url), '//link|//script') do |stylesheet_url, _tag|
45
- uri = Addressable::URI.parse(stylesheet_url)
46
- next unless uri.query&.match(pattern)
47
-
48
- version = Regexp.last_match[1].to_s
49
-
50
- found[version] ||= []
51
- found[version] << stylesheet_url
52
- end
53
-
54
- found
55
- end
56
- end
57
- end
58
- end
59
- end
@@ -1,16 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module WpVersion
4
- # Stylesheets Version Finder from Install page
5
- class InstallStylesheetNumbers < HomepageStylesheetNumbers
6
- # Overrides the parent
7
- def passive(_ops = {}); end
8
-
9
- # @return [ Array<WpVersion> ]
10
- def aggressive(_opts = {})
11
- wp_versions(target.url('wp-admin/install.php'))
12
- end
13
- end
14
- end
15
- end
16
- end
@@ -1,27 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module WpVersion
4
- # Meta Generator Version Finder
5
- class MetaGenerator < CMSScanner::Finders::Finder
6
- # @return [ WpVersion ]
7
- def passive(_opts = {})
8
- target.homepage_res.html.css('meta[name="generator"]').each do |node|
9
- next unless node.attribute('content').to_s =~ /wordpress ([0-9\.]+)/i
10
-
11
- number = Regexp.last_match(1)
12
-
13
- next unless WPScan::WpVersion.valid?(number)
14
-
15
- return WPScan::WpVersion.new(
16
- number,
17
- found_by: 'Meta Generator (Passive detection)',
18
- confidence: 80,
19
- interesting_entries: ["#{target.url}, Match: '#{node.to_s.strip}'"]
20
- )
21
- end
22
- nil
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,23 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module WpVersion
4
- # Sitemap Generator Version Finder
5
- class OpmlGenerator < CMSScanner::Finders::Finder
6
- # @return [ WpVersion ]
7
- def aggressive(_opts = {})
8
- target.comments_from_page(%r{\Agenerator="wordpress/([^"]+)"\z}i, 'wp-links-opml.php') do |match, node|
9
- next unless WPScan::WpVersion.valid?(match[1])
10
-
11
- return WPScan::WpVersion.new(
12
- match[1],
13
- found_by: 'OPML Generator (Aggressive Detection)',
14
- confidence: 80,
15
- interesting_entries: ["#{target.url('wp-links-opml.php')}, Match: '#{node.to_s.strip}'"]
16
- )
17
- end
18
- nil
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,23 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module WpVersion
4
- # Sitemap Generator Version Finder
5
- class SitemapGenerator < CMSScanner::Finders::Finder
6
- # @return [ WpVersion ]
7
- def aggressive(_opts = {})
8
- target.comments_from_page(%r{\Agenerator="wordpress/([^"]+)"\z}i, 'sitemap.xml') do |match, node|
9
- next unless WPScan::WpVersion.valid?(match[1])
10
-
11
- return WPScan::WpVersion.new(
12
- match[1],
13
- found_by: 'Sitemap Generator (Aggressive Detection)',
14
- confidence: 80,
15
- interesting_entries: ["#{target.url('sitemap.xml')}, #{node.to_s.strip}"]
16
- )
17
- end
18
- nil
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,13 +0,0 @@
1
- module WPScan
2
- module Finders
3
- module WpVersion
4
- # Stylesheets Version Finder from Upgrade page
5
- class UpgradeStylesheetNumbers < InstallStylesheetNumbers
6
- # @return [ Array<WpVersion> ]
7
- def aggressive(_opts = {})
8
- wp_versions(target.url('wp-admin/upgrade.php'))
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,55 +0,0 @@
1
- module WPScan
2
- module DB
3
- # Dynamic Finders
4
- class DynamicFinders
5
- # @return [ String ]
6
- def self.db_file
7
- @db_file ||= File.join(DB_DIR, 'dynamic_finders_01.yml')
8
- end
9
-
10
- # @return [ Hash ]
11
- def self.db_data
12
- @db_data ||= YAML.safe_load(File.read(db_file), [Regexp])
13
- end
14
-
15
- # @return [ Hash ]
16
- def self.finder_configs(finder_klass)
17
- configs = {}
18
-
19
- db_data.each do |slug, config|
20
- next unless config[finder_klass]
21
-
22
- configs[slug] = config[finder_klass].dup
23
- end
24
-
25
- configs
26
- end
27
- end
28
-
29
- # Dynamic Plugin Finders
30
- class DynamicPluginFinders < DynamicFinders
31
- # @return [ Hash ]
32
- def self.db_data
33
- @db_data ||= super['plugins'] || {}
34
- end
35
-
36
- # @return [ Hash ]
37
- def self.comments
38
- @comments ||= finder_configs('Comments')
39
- end
40
-
41
- # @return [ Hash ]
42
- def self.urls_in_page
43
- @urls_in_page ||= finder_configs('UrlsInPage')
44
- end
45
- end
46
-
47
- # Dynamic Theme Finders (none ATM)
48
- class DynamicThemeFinders < DynamicFinders
49
- # @return [ Hash ]
50
- def self.db_data
51
- @db_data ||= super['themes'] || {}
52
- end
53
- end
54
- end
55
- end