Class: WPScan::Controller::Enumeration

Inherits:
CMSScanner::Controller::Base
  • Object
show all
Defined in:
app/controllers/enumeration.rb,
app/controllers/enumeration/cli_options.rb,
app/controllers/enumeration/enum_methods.rb

Overview

Enumeration Methods

Instance Method Summary collapse

Instance Method Details

#before_scanObject



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'app/controllers/enumeration.rb', line 8

def before_scan
  # Create the Dynamic PluginVersion Finders
  DB::DynamicPluginFinders.db_data.each do |name, config|
    %w[Comments].each do |klass|
      next unless config[klass] && config[klass]['version']

      constant_name = name.tr('-', '_').camelize

      unless Finders::PluginVersion.constants.include?(constant_name.to_sym)
        Finders::PluginVersion.const_set(constant_name, Module.new)
      end

      mod = WPScan::Finders::PluginVersion.const_get(constant_name)

      raise "#{mod} has already a #{klass} class" if mod.constants.include?(klass.to_sym)

      case klass
      when 'Comments' then create_plugins_comments_finders(mod, config[klass])
      end
    end
  end
end

#cli_config_backups_optsArray<OptParseValidator::OptBase>



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'app/controllers/enumeration/cli_options.rb', line 104

def cli_config_backups_opts
  [
    OptFilePath.new(
      ['--config-backups-list FILE-PATH', 'List of config backups\' filenames to use'],
      exists: true, default: File.join(DB_DIR, 'config_backups.txt')
    ),
    OptChoice.new(
      ['--config-backups-detection MODE',
       'Use the supplied mode to enumerate Configs, instead of the global (--detection-mode) mode.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    )
  ]
end

#cli_enum_choicesArray<OptParseValidator::OptBase>

rubocop:disable Metrics/MethodLength



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'app/controllers/enumeration/cli_options.rb', line 12

def cli_enum_choices
  [
    OptMultiChoices.new(
      ['--enumerate [OPTS]', '-e', 'Enumeration Process'],
      choices: {
        vp: OptBoolean.new(['--vulnerable-plugins']),
        ap: OptBoolean.new(['--all-plugins']),
        p:  OptBoolean.new(['--plugins']),
        vt: OptBoolean.new(['--vulnerable-themes']),
        at: OptBoolean.new(['--all-themes']),
        t:  OptBoolean.new(['--themes']),
        tt: OptBoolean.new(['--timthumbs']),
        cb: OptBoolean.new(['--config-backups']),
        u:  OptIntegerRange.new(['--users', 'User ids range. e.g: u1-5'], value_if_empty: '1-10'),
        m:  OptIntegerRange.new(['--medias', 'Media ids range. e.g m1-15'], value_if_empty: '1-100')
      },
      value_if_empty: 'vp,vt,tt,cb,u,m',
      incompatible: [%i[vp ap p], %i[vt at t]]
    ),
    OptRegexp.new(
      [
        '--exclude-content-based REGEXP_OR_STRING',
        'Exclude all responses having their body matching (case insensitive) during parts of the enumeration.',
        'Regexp delimiters are not required.'
      ], options: Regexp::IGNORECASE
    )
  ]
end

#cli_medias_optsArray<OptParseValidator::OptBase>



119
120
121
122
123
124
125
126
127
# File 'app/controllers/enumeration/cli_options.rb', line 119

def cli_medias_opts
  [
    OptChoice.new(
      ['--medias-detection MODE',
       'Use the supplied mode to enumerate Medias, instead of the global (--detection-mode) mode.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    )
  ]
end

#cli_optionsObject



5
6
7
8
# File 'app/controllers/enumeration/cli_options.rb', line 5

def cli_options
  cli_enum_choices + cli_plugins_opts + cli_themes_opts +
    cli_timthumbs_opts + cli_config_backups_opts + cli_medias_opts + cli_users_opts
end

#cli_plugins_optsArray<OptParseValidator::OptBase>



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/controllers/enumeration/cli_options.rb', line 43

def cli_plugins_opts
  [
    OptSmartList.new(['--plugins-list LIST', 'List of plugins to enumerate']),
    OptChoice.new(
      ['--plugins-detection MODE',
       'Use the supplied mode to enumerate Plugins, instead of the global (--detection-mode) mode.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    ),
    OptBoolean.new(
      ['--plugins-version-all',
       'Check all the plugins version locations according to the choosen mode (--detection-mode, ' \
       '--plugins-detection and --plugins-version-detection)']
    ),
    OptChoice.new(
      ['--plugins-version-detection MODE',
       'Use the supplied mode to check plugins versions instead of the --detection-mode ' \
       'or --plugins-detection modes.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    )
  ]
end

#cli_themes_optsArray<OptParseValidator::OptBase>



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'app/controllers/enumeration/cli_options.rb', line 66

def cli_themes_opts
  [
    OptSmartList.new(['--themes-list LIST', 'List of themes to enumerate']),
    OptChoice.new(
      ['--themes-detection MODE',
       'Use the supplied mode to enumerate Themes, instead of the global (--detection-mode) mode.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    ),
    OptBoolean.new(
      ['--themes-version-all',
       'Check all the themes version locations according to the choosen mode (--detection-mode, ' \
       '--themes-detection and --themes-version-detection)']
    ),
    OptChoice.new(
      ['--themes-version-detection MODE',
       'Use the supplied mode to check themes versions instead of the --detection-mode ' \
       'or --themes-detection modes.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    )
  ]
end

#cli_timthumbs_optsArray<OptParseValidator::OptBase>



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/controllers/enumeration/cli_options.rb', line 89

def cli_timthumbs_opts
  [
    OptFilePath.new(
      ['--timthumbs-list FILE-PATH', 'List of timthumbs\' location to use'],
      exists: true, default: File.join(DB_DIR, 'timthumbs-v3.txt')
    ),
    OptChoice.new(
      ['--timthumbs-detection MODE',
       'Use the supplied mode to enumerate Timthumbs, instead of the global (--detection-mode) mode.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    )
  ]
end

#cli_users_optsArray<OptParseValidator::OptBase>



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'app/controllers/enumeration/cli_options.rb', line 130

def cli_users_opts
  [
    OptSmartList.new(
      ['--users-list LIST',
       'List of users to check during the users enumeration from the Login Error Messages']
    ),
    OptChoice.new(
      ['--users-detection MODE',
       'Use the supplied mode to enumerate Users, instead of the global (--detection-mode) mode.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    )
  ]
end

#create_plugins_comments_finders(mod, config) ⇒ Object



31
32
33
34
35
36
37
# File 'app/controllers/enumeration.rb', line 31

def create_plugins_comments_finders(mod, config)
  mod.const_set(
    :Comments, Class.new(Finders::Finder::PluginVersion::Comments) do
      const_set(:PATTERN, config['pattern'])
    end
  )
end

#default_opts(type) ⇒ Hash



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'app/controllers/enumeration/enum_methods.rb', line 25

def default_opts(type)
  mode = parsed_options[:"#{type}_detection"] || parsed_options[:detection_mode]

  {
    mode: mode,
    exclude_content: parsed_options[:exclude_content_based],
    show_progression: user_interaction?,
    version_detection: {
      mode: parsed_options[:"#{type}_version_detection"] || mode,
      confidence_threshold: parsed_options[:"#{type}_version_all"] ? 0 : 100
    }
  }
end

#enum_config_backupsObject



122
123
124
125
126
127
# File 'app/controllers/enumeration/enum_methods.rb', line 122

def enum_config_backups
  opts = default_opts('config_backups').merge(list: parsed_options[:config_backups_list])

  output('@info', msg: 'Enumerating Config Backups') if user_interaction?
  output('config_backups', config_backups: target.config_backups(opts))
end

#enum_mediasObject



129
130
131
132
133
134
# File 'app/controllers/enumeration/enum_methods.rb', line 129

def enum_medias
  opts = default_opts('medias').merge(range: parsed_options[:enumerate][:medias])

  output('@info', msg: 'Enumerating Medias') if user_interaction?
  output('medias', medias: target.medias(opts))
end

#enum_message(type) ⇒ String



8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'app/controllers/enumeration/enum_methods.rb', line 8

def enum_message(type)
  return unless %w[plugins themes].include?(type)

  details = if parsed_options[:enumerate][:"vulnerable_#{type}"]
              'Vulnerable'
            elsif parsed_options[:enumerate][:"all_#{type}"]
              'All'
            else
              'Most Popular'
            end

  "Enumerating #{details} #{type.capitalize}"
end

#enum_pluginsObject



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'app/controllers/enumeration/enum_methods.rb', line 46

def enum_plugins
  opts = default_opts('plugins').merge(
    list: plugins_list_from_opts(parsed_options),
    sort: true
  )

  output('@info', msg: enum_message('plugins')) if user_interaction?
  # Enumerate the plugins & find their versions to avoid doing that when #version
  # is called in the view
  plugins = target.plugins(opts).each(&:version)
  plugins.select!(&:vulnerable?) if parsed_options[:enumerate][:vulnerable_plugins]

  output('plugins', plugins: plugins)
end

#enum_plugins?(opts) ⇒ Boolean



42
43
44
# File 'app/controllers/enumeration/enum_methods.rb', line 42

def enum_plugins?(opts)
  opts[:plugins] || opts[:all_plugins] || opts[:vulnerable_plugins]
end

#enum_themesObject



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'app/controllers/enumeration/enum_methods.rb', line 84

def enum_themes
  opts = default_opts('themes').merge(
    list: themes_list_from_opts(parsed_options),
    sort: true
  )

  output('@info', msg: enum_message('themes')) if user_interaction?
  # Enumerate the themes & find their versions to avoid doing that when #version
  # is called in the view
  themes = target.themes(opts).each(&:version)
  themes.select!(&:vulnerable?) if parsed_options[:enumerate][:vulnerable_themes]

  output('themes', themes: themes)
end

#enum_themes?(opts) ⇒ Boolean



80
81
82
# File 'app/controllers/enumeration/enum_methods.rb', line 80

def enum_themes?(opts)
  opts[:themes] || opts[:all_themes] || opts[:vulnerable_themes]
end

#enum_timthumbsObject



115
116
117
118
119
120
# File 'app/controllers/enumeration/enum_methods.rb', line 115

def enum_timthumbs
  opts = default_opts('timthumbs').merge(list: parsed_options[:timthumbs_list])

  output('@info', msg: 'Enumerating Timthumbs') if user_interaction?
  output('timthumbs', timthumbs: target.timthumbs(opts))
end

#enum_usersObject



143
144
145
146
147
148
149
150
151
# File 'app/controllers/enumeration/enum_methods.rb', line 143

def enum_users
  opts = default_opts('users').merge(
    range: enum_users_range,
    list: parsed_options[:users_list]
  )

  output('@info', msg: 'Enumerating Users') if user_interaction?
  output('users', users: target.users(opts))
end

#enum_users?(opts) ⇒ Boolean



139
140
141
# File 'app/controllers/enumeration/enum_methods.rb', line 139

def enum_users?(opts)
  opts[:users] || (parsed_options[:passwords] && !parsed_options[:username] && !parsed_options[:usernames])
end

#enum_users_rangeRange

If the –enumerate is used, the default value is handled by the Option However, when using –passwords alone, the default has to be set by the code below



156
157
158
# File 'app/controllers/enumeration/enum_methods.rb', line 156

def enum_users_range
  parsed_options[:enumerate] ? parsed_options[:enumerate][:users] : cli_enum_choices[0].choices[:u].validate(nil)
end

#plugins_list_from_opts(opts) ⇒ Array<String>



64
65
66
67
68
69
70
71
72
73
74
75
# File 'app/controllers/enumeration/enum_methods.rb', line 64

def plugins_list_from_opts(opts)
  # List file provided by the user via the cli
  return opts[:plugins_list] if opts[:plugins_list]

  if opts[:enumerate][:all_plugins]
    DB::Plugins.all_slugs
  elsif opts[:enumerate][:plugins]
    DB::Plugins.popular_slugs
  else
    DB::Plugins.vulnerable_slugs
  end
end

#runObject



39
40
41
42
43
44
45
46
47
48
49
50
# File 'app/controllers/enumeration.rb', line 39

def run
  enum = parsed_options[:enumerate] || {}

  enum_plugins if enum_plugins?(enum)
  enum_themes  if enum_themes?(enum)

  %i[timthumbs config_backups medias].each do |key|
    send("enum_#{key}".to_sym) if enum.key?(key)
  end

  enum_users if enum_users?(enum)
end

#themes_list_from_opts(opts) ⇒ Array<String>



102
103
104
105
106
107
108
109
110
111
112
113
# File 'app/controllers/enumeration/enum_methods.rb', line 102

def themes_list_from_opts(opts)
  # List file provided by the user via the cli
  return opts[:themes_list] if opts[:themes_list]

  if opts[:enumerate][:all_themes]
    DB::Themes.all_slugs
  elsif opts[:enumerate][:themes]
    DB::Themes.popular_slugs
  else
    DB::Themes.vulnerable_slugs
  end
end