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>



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>



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>



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>



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>



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>



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>



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>



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



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



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



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



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



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



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



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>



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>



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