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
# File 'app/controllers/enumeration.rb', line 8

def before_scan
  DB::DynamicFinders::Plugin.create_versions_finders
  DB::DynamicFinders::Theme.create_versions_finders
end

#cli_config_backups_optsArray<OptParseValidator::OptBase>



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

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 Config Backups, instead of the global (--detection-mode) mode.'],
      choices: %w[mixed passive aggressive], normalize: :to_sym
    )
  ]
end

#cli_db_exports_optsArray<OptParseValidator::OptBase>



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

def cli_db_exports_opts
  [
    OptFilePath.new(
      ['--db-exports-list FILE-PATH', 'List of DB exports\' paths to use'],
      exists: true, default: File.join(DB_DIR, 'db_exports.txt')
    ),
    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
    )
  ]
end

#cli_enum_choicesArray<OptParseValidator::OptBase>

rubocop:disable Metrics/MethodLength



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
40
41
42
# File 'app/controllers/enumeration/cli_options.rb', line 13

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']),
        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'], 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>



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

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
9
# 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_db_exports_opts +
    cli_medias_opts + cli_users_opts
end

#cli_plugins_optsArray<OptParseValidator::OptBase>



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

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, 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)']
    ),
    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, default: :mixed
    )
  ]
end

#cli_themes_optsArray<OptParseValidator::OptBase>



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

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>



92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/controllers/enumeration/cli_options.rb', line 92

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>



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

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

#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



132
133
134
135
136
137
# File 'app/controllers/enumeration/enum_methods.rb', line 132

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_db_exportsObject



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

def enum_db_exports
  opts = default_opts('db_exports').merge(list: parsed_options[:db_exports_list])

  output('@info', msg: 'Enumerating DB Exports') if user_interaction?
  output('db_exports', db_exports: target.db_exports(opts))
end

#enum_mediasObject



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

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
60
61
62
63
64
# 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)

  output('@info', msg: 'Checking Plugin Versions') if user_interaction? && !plugins.empty?

  plugins.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



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'app/controllers/enumeration/enum_methods.rb', line 89

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)

  output('@info', msg: 'Checking Theme Versions') if user_interaction? && !themes.empty?

  themes.each(&:version)

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

  output('themes', themes: themes)
end

#enum_themes?(opts) ⇒ Boolean



85
86
87
# File 'app/controllers/enumeration/enum_methods.rb', line 85

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

#enum_timthumbsObject



125
126
127
128
129
130
# File 'app/controllers/enumeration/enum_methods.rb', line 125

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



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

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



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

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



173
174
175
# File 'app/controllers/enumeration/enum_methods.rb', line 173

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

#plugins_list_from_opts(opts) ⇒ Array<String>



69
70
71
72
73
74
75
76
77
78
79
80
# File 'app/controllers/enumeration/enum_methods.rb', line 69

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



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

def run
  enum = parsed_options[: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>



112
113
114
115
116
117
118
119
120
121
122
123
# File 'app/controllers/enumeration/enum_methods.rb', line 112

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