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>

Returns:

  • (Array<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

Returns:

  • (Array<OptParseValidator::OptBase>)


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>

Returns:

  • (Array<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>

Returns:

  • (Array<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
  [
    OptFilePath.new(['--plugins-list FILE-PATH', 'List of plugins\' location to use'], exists: true),
    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>

Returns:

  • (Array<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
  [
    OptFilePath.new(['--themes-list FILE-PATH', 'List of themes\' location to use'], exists: 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
    ),
    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>

Returns:

  • (Array<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>

Returns:

  • (Array<OptParseValidator::OptBase>)


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

def cli_users_opts
  [
    OptFilePath.new(
      ['--users-list FILE-PATH',
       'List of users to check during the users enumeration from the Login Error Messages'],
      exists: 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
    )
  ]
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, Regexp.new(config['pattern'], Regexp::IGNORECASE))
    end
  )
end

#default_opts(type) ⇒ Hash

Parameters:

  • type (String)

    (plugins, themes etc)

Returns:

  • (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

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

Parameters:

  • type (String)

    (plugins or themes)

Returns:

  • (String)

    The related enumration message depending on the parsed_options and type supplied



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

Returns Wether or not to enumerate the plugins.

Parameters:

  • opts (Hash)

Returns:

  • (Boolean)

    Wether or not to enumerate the plugins



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

Returns Wether or not to enumerate the themes.

Parameters:

  • opts (Hash)

Returns:

  • (Boolean)

    Wether or not to enumerate the themes



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

Returns Wether or not to enumerate the users.

Parameters:

  • opts (Hash)

Returns:

  • (Boolean)

    Wether or not to enumerate the users



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

Returns:

  • (Range)

    The user ids range to enumerate



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>

Returns The plugins list associated to the cli options.

Parameters:

  • opts (Hash)

Returns:

  • (Array<String>)

    The plugins list associated to the cli options



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 File.open(opts[:plugins_list]).map(&:chomp) 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>

Returns The themes list associated to the cli options.

Parameters:

  • opts (Hash)

Returns:

  • (Array<String>)

    The themes list associated to the cli options



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 File.open(opts[:themes_list]).map(&:chomp) 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