Class: Metasploit::Credential::Importer::Core

Inherits:
Object
  • Object
show all
Includes:
Creation, Base
Defined in:
lib/metasploit/credential/importer/core.rb

Overview

Creates Core objects and their associated Public, Private, and Realm objects from a CSV file.

Successful import will also create a Origin::Import

Constant Summary collapse

BLANK_TOKEN =

This token represents an explict Blank entry. An empty field instead indicates that we do not know what this value is

"<BLANK>"
VALID_LONG_CSV_HEADERS =

Valid headers for a CSV containing heterogenous Private types and values for Realm

[:username, :private_type, :private_data,
                          :realm_key, :realm_value, :host_address,
                          :service_port, :service_name,
                          :service_protocol, :status, :access_level,
                          :last_attempted_at
]
VALID_SHORT_CSV_HEADERS =

Valid headers for a “short” CSV containing only data for Public and Private objects

[:username,  :private_data]

Constants included from Base

Base::LONG_FORM_ALLOWED_PRIVATE_TYPE_NAMES, Base::SHORT_FORM_ALLOWED_PRIVATE_TYPE_NAMES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Creation

#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_core, #create_credential_login, #create_credential_origin, #create_credential_origin_cracked_password, #create_credential_origin_import, #create_credential_origin_manual, #create_credential_origin_service, #create_credential_origin_session, #create_credential_private, #create_credential_public, #create_credential_realm, #create_credential_service, #invalidate_login

Methods included from Base

#initialize

Instance Attribute Details

#csv_objectCSV

An instance of ‘CSV` from whence cometh the sweet sweet credential input

Returns:

  • (CSV)


41
42
43
# File 'lib/metasploit/credential/importer/core.rb', line 41

def csv_object
  @csv_object
end

#private_credential_typeString

The name of one of the subclasses of Private. This will be the same for all the Private objects created during the import.

Returns:

  • (String)


49
50
51
# File 'lib/metasploit/credential/importer/core.rb', line 49

def private_credential_type
  @private_credential_type
end

Instance Method Details

#import!void

This method returns an undefined value.

If no #private_credential_type is set, assumes that the CSV contains a mixture of private types and realms. Otherwise, assume that this is a short form import and process accordingly.



84
85
86
87
88
89
90
91
# File 'lib/metasploit/credential/importer/core.rb', line 84

def import!
  if csv_object.first.headers.include? 'private_type'
    result =  import_long_form
  else
    result =  import_short_form
  end
  return result
end

#import_long_formvoid

This method returns an undefined value.

Performs an import of a “long” CSV - one that that contains realms and heterogenous private types Performs a pretty naive import from the data in #csv_object, allowing the import to have different private types per row, and attempting to reduce database lookups by storing found or created Realm objects in a lookup Hash that gets updated with every new Realm found, and then consulted in analysis of subsequent rows.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/metasploit/credential/importer/core.rb', line 100

def import_long_form
  all_creds_valid = true
  realms = Hash.new
  Metasploit::Credential::Core.transaction do
    core_opts = []
    rows = []
    csv_object.each do |row|

      next if row.header_row?
      next unless row['username'].present? || row['private_data'].present?

      username      = row['username'].present? ? row['username'] : ''

      realm_key     = row['realm_key']
      realm_value   = row['realm_value']  # Use the name of the Realm as a lookup for getting the object

      private_class = row['private_type'].present? ? row['private_type'].constantize : ''
      private_data  = row['private_data'].present? ? row['private_data'] : ''

      if realms[realm_value].nil?
        realms[realm_value]  = Metasploit::Credential::Realm.where(key: realm_key, value: realm_value).first_or_create
      end

      realm_object_for_row   = realms[realm_value]

      public_object = create_public_from_field(username)

      if private_class.present? &&  LONG_FORM_ALLOWED_PRIVATE_TYPE_NAMES.include?(private_class.name)
        if private_data.strip == BLANK_TOKEN
          private_object_for_row = Metasploit::Credential::BlankPassword.first_or_create
        elsif private_class == Metasploit::Credential::SSHKey
          private_object_for_row = Metasploit::Credential::SSHKey.where(data: key_data_from_file(private_data)).first_or_create
        else
          private_object_for_row = private_class.where(data: private_data).first_or_create
        end
      end
      all_creds_valid = all_creds_valid && public_object && private_object_for_row && (public_object.valid? && private_object_for_row.valid?)

      core_opts << {origin:origin, workspace_id: workspace.id,
       public: public_object,
       private: private_object_for_row,
       realm: realm_object_for_row}

      rows << row



    end
    if all_creds_valid
      core_opts.each_index do |index|
        row = rows[index]


        # Host and Service information for Logins
        host_address      = row['host_address']
        service_port      = row['service_port']
        service_protocol  = row['service_protocol']
        service_name      = row['service_name']
        # These were not initially included in the export, so handle
        # legacy cases:
        access_level      = row['access_level'].present? ? row['access_level'] : ''
        last_attempted_at = row['last_attempted_at'].present? ? row['last_attempted_at'] : ''
        status            = row['status'].present? ? row['status'] : ''

        if Metasploit::Credential::Core.where(core_opts[index]).blank?
          core = create_credential_core(core_opts[index])
        else
          core = Metasploit::Credential::Core.where(core_opts[index]).first
        end


        if host_address.present? && service_port.present? && service_protocol.present?
           = {
              core: core,
              address: host_address,
              port: service_port,
              protocol: service_protocol,
              workspace_id: workspace.id,
              service_name: service_name.present? ? service_name : ""
          }
          [:last_attempted_at] = last_attempted_at unless status.blank?
          [:status]            = status unless status.blank?
          [:access_level]      = access_level unless access_level.blank?

          ()

        end
      end
    end
    end
  return all_creds_valid
end

#import_short_formBoolean

Performs an import of a “short” form of CSV - one that contains only one type of Private and no Realm data

Returns:

  • (Boolean)


197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/metasploit/credential/importer/core.rb', line 197

def import_short_form
  core_opts = []
  all_creds_valid = true
  Metasploit::Credential::Core.transaction do
    csv_object.each do |row|
      next if row.header_row?

      username     = row['username'].present? ? row['username'] : ''
      private_data  = row['private_data'].present? ? row['private_data'] : ''

      public_object = create_public_from_field(username)

      if private_data.strip == BLANK_TOKEN
        private_object_for_row = Metasploit::Credential::BlankPassword.first_or_create
      else
        private_object_for_row = @private_credential_type.constantize.where(data: private_data).first_or_create
      end

      # need to check private_object_for_row.valid? to raise a user facing message if any cred had invalid private

      all_creds_valid = all_creds_valid && (public_object.valid? && private_object_for_row.valid?)


      core_opts << {origin:origin, workspace_id: workspace.id,
                                    public: public_object,
                                    private: private_object_for_row}
    end
    if all_creds_valid
      core_opts.each do |item|
        if Metasploit::Credential::Core.where(origin: item[:origin], workspace_id: item[:workspace_id], public: item[:public], private: item[:private]).blank?
          create_credential_core(item)
        end
      end
    end


  end
  return  all_creds_valid
end

#key_data_from_file(key_file_name) ⇒ String

The key data inside the file at key_file_name

Parameters:

  • key_file_name (String)

Returns:

  • (String)


76
77
78
79
# File 'lib/metasploit/credential/importer/core.rb', line 76

def key_data_from_file(key_file_name)
  full_key_file_path = File.join(File.dirname(input.path), Metasploit::Credential::Importer::Zip::KEYS_SUBDIRECTORY_NAME, key_file_name)
  File.open(full_key_file_path, 'r').read
end