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

#cli_config_backups_optsArray<OptParseValidator::OptBase>

Returns:

  • (Array<OptParseValidator::OptBase>)


121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'app/controllers/enumeration/cli_options.rb', line 121

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

#cli_db_exports_optsArray<OptParseValidator::OptBase>

Returns:

  • (Array<OptParseValidator::OptBase>)


136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'app/controllers/enumeration/cli_options.rb', line 136

def cli_db_exports_opts
  [
    OptFilePath.new(
      ['--db-exports-list FILE-PATH', 'List of DB exports\' paths to use'],
      exists: true, default: DB_DIR.join('db_exports.txt').to_s, advanced: true
    ),
    OptChoice.new(
      ['--db-exports-detection MODE',
       'Use the supplied mode to enumerate DB Exports, instead of the global (--detection-mode) mode.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym, advanced: true
    )
  ]
end

#cli_enum_choicesArray<OptParseValidator::OptBase>

Returns:

  • (Array<OptParseValidator::OptBase>)


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
40
41
42
43
44
45
46
# File 'app/controllers/enumeration/cli_options.rb', line 14

def cli_enum_choices
  [
    OptMultiChoices.new(
      ['-e', '--enumerate [OPTS]', 'Enumeration Process'],
      choices: {
        vp: OptBoolean.new(['--vulnerable-plugins']),
        ap: OptBoolean.new(['--all-plugins']),
        p: OptBoolean.new(['--popular-plugins']),
        vt: OptBoolean.new(['--vulnerable-themes']),
        at: OptBoolean.new(['--all-themes']),
        t: OptBoolean.new(['--popular-themes']),
        tt: OptBoolean.new(['--timthumbs']),
        cb: OptBoolean.new(['--config-backups']),
        dbe: OptBoolean.new(['--db-exports']),
        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',
                                'Note: Permalink setting must be set to "Plain" for those to be detected'],
                               value_if_empty: '1-100')
      },
      value_if_empty: 'vp,vt,tt,cb,dbe,u,m',
      incompatible: [%i[vp ap p], %i[vt at t]],
      default: { all_plugins: true, config_backups: true }
    ),
    OptRegexp.new(
      [
        '--exclude-content-based REGEXP_OR_STRING',
        'Exclude all responses matching the Regexp (case insensitive) during parts of the enumeration.',
        'Both the headers and body are checked. Regexp delimiters are not required.'
      ], options: Regexp::IGNORECASE
    )
  ]
end

#cli_medias_optsArray<OptParseValidator::OptBase>

Returns:

  • (Array<OptParseValidator::OptBase>)


151
152
153
154
155
156
157
158
159
# File 'app/controllers/enumeration/cli_options.rb', line 151

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, advanced: true
    )
  ]
end

#cli_optionsObject



7
8
9
10
11
# File 'app/controllers/enumeration/cli_options.rb', line 7

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

#cli_plugins_optsArray<OptParseValidator::OptBase>

Returns:

  • (Array<OptParseValidator::OptBase>)


49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'app/controllers/enumeration/cli_options.rb', line 49

def cli_plugins_opts
  [
    OptSmartList.new(['--plugins-list LIST', 'List of plugins to enumerate'], advanced: true),
    OptChoice.new(
      ['--plugins-detection MODE',
       'Use the supplied mode to enumerate Plugins.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym, default: :passive
    ),
    OptBoolean.new(
      ['--plugins-version-all',
       'Check all the plugins version locations according to the choosen mode (--detection-mode, ' \
       '--plugins-detection and --plugins-version-detection)'],
      advanced: true
    ),
    OptChoice.new(
      ['--plugins-version-detection MODE',
       'Use the supplied mode to check plugins\' versions.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym, default: :mixed
    ),
    OptInteger.new(
      ['--plugins-threshold THRESHOLD',
       'Raise an error when the number of detected plugins via known locations reaches the threshold. ' \
       'Set to 0 to ignore the threshold.'], default: 100, advanced: true
    )
  ]
end

#cli_themes_optsArray<OptParseValidator::OptBase>

Returns:

  • (Array<OptParseValidator::OptBase>)


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'app/controllers/enumeration/cli_options.rb', line 77

def cli_themes_opts
  [
    OptSmartList.new(['--themes-list LIST', 'List of themes to enumerate'], advanced: true),
    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, advanced: true
    ),
    OptBoolean.new(
      ['--themes-version-all',
       'Check all the themes version locations according to the choosen mode (--detection-mode, ' \
       '--themes-detection and --themes-version-detection)'],
      advanced: true
    ),
    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, advanced: true
    ),
    OptInteger.new(
      ['--themes-threshold THRESHOLD',
       'Raise an error when the number of detected themes via known locations reaches the threshold. ' \
       'Set to 0 to ignore the threshold.'], default: 20, advanced: true
    )
  ]
end

#cli_timthumbs_optsArray<OptParseValidator::OptBase>

Returns:

  • (Array<OptParseValidator::OptBase>)


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

def cli_timthumbs_opts
  [
    OptFilePath.new(
      ['--timthumbs-list FILE-PATH', 'List of timthumbs\' location to use'],
      exists: true, default: DB_DIR.join('timthumbs-v3.txt').to_s, advanced: true
    ),
    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, advanced: true
    )
  ]
end

#cli_users_optsArray<OptParseValidator::OptBase>

Returns:

  • (Array<OptParseValidator::OptBase>)


162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'app/controllers/enumeration/cli_options.rb', line 162

def cli_users_opts
  [
    OptSmartList.new(
      ['--users-list LIST',
       'List of users to check during the users enumeration from the Login Error Messages'],
      advanced: true
    ),
    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, advanced: true
    ),
    OptRegexp.new(
      [
        '--exclude-usernames REGEXP_OR_STRING',
        'Exclude usernames matching the Regexp/string (case insensitive). Regexp delimiters are not required.'
      ], options: Regexp::IGNORECASE
    )
  ]
end

#default_opts(type) ⇒ Hash

Parameters:

  • type (String)

    (plugins, themes etc)

Returns:

  • (Hash)


41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'app/controllers/enumeration/enum_methods.rb', line 41

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

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

#enum_config_backupsObject



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

def enum_config_backups
  opts = default_opts('config_backups').merge(list: ParsedCli.config_backups_list)

  output('@info', msg: "Enumerating Config Backups #{enum_detection_message(opts[:mode])}") if user_interaction?
  output('config_backups', config_backups: target.config_backups(opts))
end

#enum_db_exportsObject



163
164
165
166
167
168
# File 'app/controllers/enumeration/enum_methods.rb', line 163

def enum_db_exports
  opts = default_opts('db_exports').merge(list: ParsedCli.db_exports_list)

  output('@info', msg: "Enumerating DB Exports #{enum_detection_message(opts[:mode])}") if user_interaction?
  output('db_exports', db_exports: target.db_exports(opts))
end

#enum_detection_message(detection_mode) ⇒ String

Parameters:

  • detection_mode (Symbol)

Returns:

  • (String)


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

def enum_detection_message(detection_mode)
  detection_method = if detection_mode == :mixed
                       'Passive and Aggressive'
                     else
                       detection_mode.to_s.capitalize
                     end

  "(via #{detection_method} Methods)"
end

#enum_mediasObject



170
171
172
173
174
175
176
177
178
179
180
# File 'app/controllers/enumeration/enum_methods.rb', line 170

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

  if user_interaction?
    output('@info',
           msg: "Enumerating Medias #{enum_detection_message(opts[:mode])} "\
                '(Permalink setting must be set to "Plain" for those to be detected)')
  end

  output('medias', medias: target.medias(opts))
end

#enum_message(type, detection_mode) ⇒ String

Returns The related enumration message depending on the ParsedCli and type supplied.

Parameters:

  • type (String)

    (plugins or themes)

  • detection_mode (Symbol)

Returns:

  • (String)

    The related enumration message depending on the ParsedCli and type supplied



11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'app/controllers/enumeration/enum_methods.rb', line 11

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

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

  "Enumerating #{details} #{type.capitalize} #{enum_detection_message(detection_mode)}"
end

#enum_pluginsObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'app/controllers/enumeration/enum_methods.rb', line 62

def enum_plugins
  opts = default_opts('plugins').merge(
    list: plugins_list_from_opts(ParsedCli.options),
    threshold: ParsedCli.plugins_threshold,
    sort: true
  )

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

  if user_interaction? && !plugins.empty?
    output('@info',
           msg: "Checking Plugin Versions #{enum_detection_message(opts[:version_detection][:mode])}")
  end

  plugins.each(&:version)

  plugins.select!(&:vulnerable?) if ParsedCli.enumerate[:vulnerable_plugins]

  output('plugins', plugins: plugins)
end

#enum_plugins?(opts) ⇒ Boolean

Returns Wether or not to enumerate the plugins.

Parameters:

  • opts (Hash)

Returns:

  • (Boolean)

    Wether or not to enumerate the plugins



58
59
60
# File 'app/controllers/enumeration/enum_methods.rb', line 58

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

#enum_themesObject



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'app/controllers/enumeration/enum_methods.rb', line 109

def enum_themes
  opts = default_opts('themes').merge(
    list: themes_list_from_opts(ParsedCli.options),
    threshold: ParsedCli.themes_threshold,
    sort: true
  )

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

  if user_interaction? && !themes.empty?
    output('@info',
           msg: "Checking Theme Versions #{enum_detection_message(opts[:version_detection][:mode])}")
  end

  themes.each(&:version)

  themes.select!(&:vulnerable?) if ParsedCli.enumerate[:vulnerable_themes]

  output('themes', themes: themes)
end

#enum_themes?(opts) ⇒ Boolean

Returns Wether or not to enumerate the themes.

Parameters:

  • opts (Hash)

Returns:

  • (Boolean)

    Wether or not to enumerate the themes



105
106
107
# File 'app/controllers/enumeration/enum_methods.rb', line 105

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

#enum_timthumbsObject



149
150
151
152
153
154
# File 'app/controllers/enumeration/enum_methods.rb', line 149

def enum_timthumbs
  opts = default_opts('timthumbs').merge(list: ParsedCli.timthumbs_list)

  output('@info', msg: "Enumerating Timthumbs #{enum_detection_message(opts[:mode])}") if user_interaction?
  output('timthumbs', timthumbs: target.timthumbs(opts))
end

#enum_usersObject



189
190
191
192
193
194
195
196
197
# File 'app/controllers/enumeration/enum_methods.rb', line 189

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

  output('@info', msg: "Enumerating Users #{enum_detection_message(opts[:mode])}") if user_interaction?
  output('users', users: target.users(opts))
end

#enum_users?(opts) ⇒ Boolean

Returns Wether or not to enumerate the users.

Parameters:

  • opts (Hash)

Returns:

  • (Boolean)

    Wether or not to enumerate the users



185
186
187
# File 'app/controllers/enumeration/enum_methods.rb', line 185

def enum_users?(opts)
  opts[:users] || (ParsedCli.passwords && !ParsedCli.username && !ParsedCli.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

Returns:

  • (Range)

    The user ids range to enumerate



202
203
204
# File 'app/controllers/enumeration/enum_methods.rb', line 202

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

#plugins_list_from_opts(opts) ⇒ Array<String>

Returns The plugins list associated to the cli options.

Parameters:

  • opts (Hash)

Returns:

  • (Array<String>)

    The plugins list associated to the cli options



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

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][:popular_plugins]
    DB::Plugins.popular_slugs
  else
    DB::Plugins.vulnerable_slugs
  end
end

#runObject



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

def run
  enum = ParsedCli.enumerate || {}

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

  %i[timthumbs config_backups db_exports 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>

Returns The themes list associated to the cli options.

Parameters:

  • opts (Hash)

Returns:

  • (Array<String>)

    The themes list associated to the cli options



136
137
138
139
140
141
142
143
144
145
146
147
# File 'app/controllers/enumeration/enum_methods.rb', line 136

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][:popular_themes]
    DB::Themes.popular_slugs
  else
    DB::Themes.vulnerable_slugs
  end
end