metasploit-credential 6.0.3 → 6.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 400f381379e4097dcac14f58cdc30441d19302fbbbb71a47f605d12e0de742f6
4
- data.tar.gz: 00ad5bd371f705f0ceddf8a119aa0e24588b60a97fa4745298e1420ed3e41151
3
+ metadata.gz: c890cd98fc73ff6cbd909ded7207646f534477a4308f60f1e7dc09231cd29d88
4
+ data.tar.gz: 62fd27bcb61c852fb705cb89a0848771f6c4ae64b6a3c76202228ea6d2037969
5
5
  SHA512:
6
- metadata.gz: 414447fd2762a658471737dbc8698eeb05cf213d1a6019d517a4f83b979dca8891c7b19dcd627769265508951262d4f96f238ad891c2cc5d9d6fc4ebf17b16fb
7
- data.tar.gz: a68f92d5f6b1d57449d451411b5e36890aa370e6f9d307d5a4bdc0fa63e120551a26d37844a765b1310b8cabc68fe414d949f149ef560791b8ec7d0ebf01f200
6
+ metadata.gz: 06bf39bbda84fc40c4f25085a39e4f27211a50246879fbe3b50f034c502a9b6fcf7cebbe9292912a4bfc42f8804837a45e8cbc332253b5f3fc5b122983f6b23e
7
+ data.tar.gz: d361ca8222849ab597accea494fef812339b971537bcd9cdbc3259eb19271a57f31cd1773c8338282211bf43569ee507888eb09f7326799b9ec6d66140bc50cd
checksums.yaml.gz.sig CHANGED
@@ -1,2 +1,2 @@
1
- w (��m���O@f��ᐾ5��TxhJ/TLՋK˕9�l�OwKGٻ���s�«& 1��w_2iEs<')G��J���I��o�^,Auv�-�}�Zw/P���0�Ӈg9�M���Ǭ��o�����Q��s����.��`s��M5ra����Z�;]*�D����~��A](ܢ4=B�g ����`
2
- qU��d
1
+ �,-�Ds����4'w��nB�rI�}m��cz.�?�{?D�l sBG�� >ה) N�:��� u��#[3�[�������ޚ{��0aƺ%�|����#�P~?��P'F��#,N;��$��.�U���<�8:�w��t��{-
2
+ �r4A��oz� �<��Q�0g��}�_�� �����K����L���GrS!O�� �2��l��wb� j�v�Z}n�2Z�+����O[�h�?���.���O�X.�
@@ -73,7 +73,8 @@ class Metasploit::Credential::KrbEncKey < Metasploit::Credential::PasswordHash
73
73
  # Callbacks
74
74
  #
75
75
 
76
- before_validation :normalize_data
76
+ serialize :data, Metasploit::Credential::CaseInsensitiveSerializer
77
+ validates_uniqueness_of :data, :case_sensitive => false
77
78
 
78
79
  #
79
80
  # Validations
@@ -162,15 +163,6 @@ class Metasploit::Credential::KrbEncKey < Metasploit::Credential::PasswordHash
162
163
  }
163
164
  end
164
165
 
165
- # Normalizes {#data} by making it all lowercase so that the unique validation and index on
166
- # ({Metasploit::Credential::Private#type}, {#data}) catches collision in a case-insensitive manner without the need
167
- # to use case-insensitive comparisons.
168
- def normalize_data
169
- if data
170
- self.data = data.downcase
171
- end
172
- end
173
-
174
166
  # Validates that {#data} is in the expected data format
175
167
  def data_format
176
168
  unless DATA_REGEXP.match(data)
@@ -55,15 +55,18 @@ class Metasploit::Credential::NTLMHash < Metasploit::Credential::ReplayableHash
55
55
  # @return [String] `'<LAN Manager hex digest>:<NT LAN Manager hex digest>'`
56
56
 
57
57
  #
58
- # Callbacks
58
+ # Serializers
59
59
  #
60
60
 
61
- before_validation :normalize_data
61
+ # Hash results are always downcased when stored in the database
62
+ # This serializer allows for ORM to search in a case-insensitive
63
+ serialize :data, Metasploit::Credential::CaseInsensitiveSerializer
62
64
 
63
65
  #
64
66
  # Validations
65
67
  #
66
68
 
69
+ validates_uniqueness_of :data, :case_sensitive => false
67
70
  validate :data_format
68
71
 
69
72
  #
@@ -130,15 +133,6 @@ class Metasploit::Credential::NTLMHash < Metasploit::Credential::ReplayableHash
130
133
 
131
134
  private
132
135
 
133
- # Normalizes {#data} by making it all lowercase so that the unique validation and index on
134
- # ({Metasploit::Credential::Private#type}, {#data}) catches collision in a case-insensitive manner without the need
135
- # to use case-insensitive comparisons.
136
- def normalize_data
137
- if data
138
- self.data = data.downcase
139
- end
140
- end
141
-
142
136
  # Validates that {#data} is in the NTLM data format of <LAN Manager hex digest>:<NT LAN Manager hex digest>. Both hex
143
137
  # digests are 32 lowercase hexadecimal characters.
144
138
  def data_format
@@ -0,0 +1,84 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ # A private Pkcs12 file.
5
+ class Metasploit::Credential::Pkcs12 < Metasploit::Credential::Private
6
+ #
7
+ # Attributes
8
+ #
9
+
10
+ # @!attribute data
11
+ # A private pkcs12 file, base64 encoded - i.e. starting with 'MIIMhgIBAzCCDFAGCSqGSIb3DQEHAaCC....'
12
+ #
13
+ # @return [String]
14
+
15
+ #
16
+ #
17
+ # Validations
18
+ #
19
+ #
20
+
21
+ #
22
+ # Attribute Validations
23
+ #
24
+
25
+ validates :data,
26
+ presence: true
27
+ #
28
+ # Method Validations
29
+ #
30
+
31
+ validate :readable
32
+
33
+ #
34
+ # Instance Methods
35
+ #
36
+
37
+ # Converts the private pkcs12 data in {#data} to an `OpenSSL::PKCS12` instance.
38
+ #
39
+ # @return [OpenSSL::PKCS12]
40
+ # @raise [ArgumentError] if {#data} cannot be loaded
41
+ def openssl_pkcs12
42
+ if data
43
+ begin
44
+ password = ''
45
+ OpenSSL::PKCS12.new(Base64.strict_decode64(data), password)
46
+ rescue OpenSSL::PKCS12::PKCS12Error => error
47
+ raise ArgumentError.new(error)
48
+ end
49
+ end
50
+ end
51
+
52
+ # The {#data key data}'s fingerprint, suitable for displaying to the
53
+ # user.
54
+ #
55
+ # @return [String]
56
+ def to_s
57
+ return '' unless data
58
+
59
+ cert = openssl_pkcs12.certificate
60
+ result = []
61
+ result << "subject:#{cert.subject.to_s}"
62
+ result << "issuer:#{cert.issuer.to_s}"
63
+ result.join(',')
64
+ end
65
+
66
+ private
67
+
68
+ #
69
+ # Validates that {#data} can be read by OpenSSL and a `OpenSSL::PKCS12` can be created from {#data}. Any exception
70
+ # raised will be reported as a validation error.
71
+ #
72
+ # @return [void]
73
+ def readable
74
+ if data
75
+ begin
76
+ openssl_pkcs12
77
+ rescue => error
78
+ errors.add(:data, "#{error.class} #{error}")
79
+ end
80
+ end
81
+ end
82
+
83
+ Metasploit::Concern.run(self)
84
+ end
@@ -13,7 +13,8 @@ class Metasploit::Credential::PostgresMD5 < Metasploit::Credential::ReplayableHa
13
13
  # Callbacks
14
14
  #
15
15
 
16
- before_validation :normalize_data
16
+ serialize :data, Metasploit::Credential::CaseInsensitiveSerializer
17
+ validates_uniqueness_of :data, :case_sensitive => false
17
18
 
18
19
  #
19
20
  # Validations
@@ -23,15 +24,6 @@ class Metasploit::Credential::PostgresMD5 < Metasploit::Credential::ReplayableHa
23
24
 
24
25
  private
25
26
 
26
- # Normalizes {#data} by making it all lowercase so that the unique validation and index on
27
- # ({Metasploit::Credential::Private#type}, {#data}) catches collision in a case-insensitive manner without the need
28
- # to use case-insensitive comparisons.
29
- def normalize_data
30
- if data
31
- self.data = data.downcase
32
- end
33
- end
34
-
35
27
  def data_format
36
28
  unless DATA_REGEXP.match(data)
37
29
  errors.add(:data, 'is not in Postgres MD5 Hash format')
@@ -74,6 +74,7 @@ class Metasploit::Credential::Private < ApplicationRecord
74
74
  Metasploit::Credential::Password
75
75
  Metasploit::Credential::SSHKey
76
76
  Metasploit::Credential::KrbEncKey
77
+ Metasploit::Credential::Pkcs12
77
78
  }
78
79
 
79
80
  #
@@ -57,6 +57,7 @@ en:
57
57
  metasploit/credential/ntlm_hash: "NTLM hash"
58
58
  metasploit/credential/ssh_key: "SSH key"
59
59
  metasploit/credential/krb_enc_key: 'Krb enc key'
60
+ metasploit/credential/pkcs12: 'Pkcs12 (pfx)'
60
61
  errors:
61
62
  models:
62
63
  metasploit/credential/core:
@@ -83,10 +84,14 @@ en:
83
84
  attributes:
84
85
  data:
85
86
  format: "is not in the KrbEncKey data format of 'msf_krbenckey:<ENCTYPE>:<KEY>:<SALT>', where the key and salt are in hexadecimal characters"
87
+ metasploit/credential/pkcs12:
88
+ attributes:
89
+ data:
90
+ format: "is not a Base64 encoded pkcs12 file without a password"
86
91
  metasploit/credential/ssh_key:
87
92
  attributes:
88
93
  data:
89
- encrypted: "is encrypted, but Metasploit::Credential::SSHKey only supports unencrypred private keys."
94
+ encrypted: "is encrypted, but Metasploit::Credential::SSHKey only supports unencrypted private keys."
90
95
  not_private: "is not a private key."
91
96
  errors:
92
97
  messages:
@@ -0,0 +1,32 @@
1
+ class CreateIndexOnPrivateDataAndTypeForPkcs12 < ActiveRecord::Migration[6.1]
2
+ def up
3
+ # Drop the existing index created by 20161107153145_recreate_index_on_private_data_and_type.rb, and recreate it
4
+ # with Metasploit::Credential::Pkcs12 ignored
5
+ remove_index :metasploit_credential_privates, [:type, :data], if_exists: true
6
+ change_table :metasploit_credential_privates do |t|
7
+ t.index [:type, :data],
8
+ unique: true,
9
+ where: "NOT (type = 'Metasploit::Credential::SSHKey' or type = 'Metasploit::Credential::Pkcs12')"
10
+ end
11
+
12
+ # Create a new index similar to 20161107203710_create_index_on_private_data_and_type_for_ssh_key.rb
13
+ sql = <<~EOF
14
+ CREATE UNIQUE INDEX IF NOT EXISTS "index_metasploit_credential_privates_on_type_and_data_pkcs12" ON
15
+ "metasploit_credential_privates" ("type", decode(md5(data), 'hex'))
16
+ WHERE type in ('Metasploit::Credential::Pkcs12')
17
+ EOF
18
+ execute(sql)
19
+ end
20
+
21
+ def down
22
+ # Restore the original metasploit_credential_privates index from /Users/adfoster/Documents/code/metasploit-credential/db/migrate/20161107153145_recreate_index_on_private_data_and_type.rb
23
+ # XXX: this would crash if there are any Pkcs12 entries present, so for the simplicity of avoiding a data migration we keep the pkcs12 type ommitted from the index
24
+ remove_index :metasploit_credential_privates, [:type, :data], if_exists: true
25
+ change_table :metasploit_credential_privates do |t|
26
+ t.index [:type, :data],
27
+ unique: true,
28
+ where: "NOT (type = 'Metasploit::Credential::SSHKey' or type = 'Metasploit::Credential::Pkcs12')"
29
+ end
30
+ remove_index :metasploit_credential_privates, name: :index_metasploit_credential_privates_on_type_and_data_pkcs12, if_exists: true
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ class Metasploit::Credential::CaseInsensitiveSerializer
2
+ def self.load(value)
3
+ value
4
+ end
5
+
6
+ def self.dump(value)
7
+ value.downcase
8
+ end
9
+ end
@@ -479,6 +479,8 @@ module Metasploit::Credential::Creation
479
479
  private_object = Metasploit::Credential::Password.where(data: private_data).first_or_create
480
480
  when :ssh_key
481
481
  private_object = Metasploit::Credential::SSHKey.where(data: private_data).first_or_create
482
+ when :pkcs12
483
+ private_object = Metasploit::Credential::Pkcs12.where(data: private_data).first_or_create
482
484
  when :krb_enc_key
483
485
  private_object = Metasploit::Credential::KrbEncKey.where(data: private_data).first_or_create
484
486
  when :ntlm_hash
@@ -116,7 +116,6 @@ class Metasploit::Credential::Importer::Core
116
116
  private_class = row['private_type'].present? ? row['private_type'].constantize : ''
117
117
  private_data = row['private_data'].present? ? row['private_data'] : ''
118
118
 
119
-
120
119
  if realms[realm_value].nil?
121
120
  realms[realm_value] = Metasploit::Credential::Realm.where(key: realm_key, value: realm_value).first_or_create
122
121
  end
@@ -210,7 +209,7 @@ class Metasploit::Credential::Importer::Core
210
209
  if private_data.strip == BLANK_TOKEN
211
210
  private_object_for_row = Metasploit::Credential::BlankPassword.first_or_create
212
211
  else
213
- private_object_for_row = @private_credential_type.constantize.where(data: row['private_data']).first_or_create
212
+ private_object_for_row = @private_credential_type.constantize.where(data: private_data).first_or_create
214
213
  end
215
214
 
216
215
  # need to check private_object_for_row.valid? to raise a user facing message if any cred had invalid private
@@ -3,7 +3,7 @@
3
3
  module Metasploit
4
4
  module Credential
5
5
  # VERSION is managed by GemRelease
6
- VERSION = '6.0.3'
6
+ VERSION = '6.0.5'
7
7
 
8
8
  # @return [String]
9
9
  #
@@ -28,6 +28,7 @@ module Metasploit
28
28
 
29
29
  autoload :BlankPassword
30
30
  autoload :BlankUsername
31
+ autoload :CaseInsensitiveSerializer
31
32
  autoload :Core
32
33
  autoload :CoreValidations
33
34
  autoload :Creation
@@ -43,6 +44,7 @@ module Metasploit
43
44
  autoload :Origin
44
45
  autoload :Password
45
46
  autoload :PasswordHash
47
+ autoload :Pkcs12
46
48
  autoload :PostgresMD5
47
49
  autoload :Private
48
50
  autoload :Public
@@ -1,6 +1,6 @@
1
1
  development: &pgsql
2
2
  adapter: postgresql
3
- database: metasploit-credential_development1
3
+ database: metasploit-credential_development2
4
4
  username: msf
5
5
  password: pass123
6
6
  host: localhost
@@ -10,4 +10,4 @@ development: &pgsql
10
10
  min_messages: warning
11
11
  test:
12
12
  <<: *pgsql
13
- database: metasploit-credential_test1
13
+ database: metasploit-credential_test2
@@ -0,0 +1,37 @@
1
+ FactoryBot.define do
2
+ factory :metasploit_credential_pkcs12,
3
+ class: Metasploit::Credential::Pkcs12 do
4
+ transient do
5
+ # key size tuned for speed. DO NOT use for production, it is below current recommended key size of 2048
6
+ key_size { 1024 }
7
+ # signing algorithm for the pkcs12 cert
8
+ signing_algorithm { 'SHA256' }
9
+ # the cert subject
10
+ subject { '/C=BE/O=Test/OU=Test/CN=Test' }
11
+ # the cert issuer
12
+ issuer { '/C=BE/O=Test/OU=Test/CN=Test' }
13
+ end
14
+
15
+ data {
16
+ password = ''
17
+ pkcs12_name = ''
18
+
19
+ private_key = OpenSSL::PKey::RSA.new(key_size)
20
+ public_key = private_key.public_key
21
+
22
+ cert = OpenSSL::X509::Certificate.new
23
+ cert.subject = OpenSSL::X509::Name.parse(subject)
24
+ cert.issuer = OpenSSL::X509::Name.parse(issuer)
25
+ cert.not_before = Time.now
26
+ cert.not_after = Time.now + 365 * 24 * 60 * 60
27
+ cert.public_key = public_key
28
+ cert.serial = 0x0
29
+ cert.version = 2
30
+ cert.sign(private_key, OpenSSL::Digest.new(signing_algorithm))
31
+
32
+ pkcs12 = OpenSSL::PKCS12.create(password, pkcs12_name, private_key, cert)
33
+ pkcs12_base64 = Base64.strict_encode64(pkcs12.to_der)
34
+ pkcs12_base64
35
+ }
36
+ end
37
+ end
@@ -134,20 +134,30 @@ RSpec.describe Metasploit::Credential::Creation do
134
134
  nonreplayable_hash: "Metasploit::Credential::NonreplayableHash",
135
135
  ntlm_hash: "Metasploit::Credential::NTLMHash",
136
136
  postgres_md5: "Metasploit::Credential::PostgresMD5",
137
- ssh_key: "Metasploit::Credential::SSHKey"
137
+ ssh_key: "Metasploit::Credential::SSHKey",
138
+ krb_enc_key: "Metasploit::Credential::KrbEncKey",
139
+ pkcs12: "Metasploit::Credential::Pkcs12"
138
140
  }.each_pair do |private_type, public_class|
139
141
  context "Origin[manual], Public[Username], Private[#{private_type}]" do
140
142
  let(:ssh_key) {
141
143
  key_class = OpenSSL::PKey.const_get(:RSA)
142
144
  key_class.generate(512).to_s
143
145
  }
146
+ let(:krb_enc_key) {
147
+ FactoryBot.build(:metasploit_credential_krb_enc_key).data
148
+ }
149
+ let(:pkcs12) {
150
+ FactoryBot.build(:metasploit_credential_pkcs12).data
151
+ }
144
152
  let(:private_data) { {
145
153
  password: 'password',
146
154
  blank_password: '',
147
155
  nonreplayable_hash: '435ba65d2e46d35bc656086694868d1ab2c0f9fd',
148
156
  ntlm_hash: 'aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0',
149
157
  postgres_md5: 'md5ac4bbe016b808c3c0b816981f240dcae',
150
- ssh_key: ssh_key
158
+ ssh_key: ssh_key,
159
+ krb_enc_key: krb_enc_key,
160
+ pkcs12: pkcs12
151
161
  }}
152
162
  let(:credential_data) {{
153
163
  workspace_id: workspace.id,
@@ -168,6 +178,37 @@ RSpec.describe Metasploit::Credential::Creation do
168
178
  end
169
179
  end
170
180
  end
181
+ context 'deletion and creation' do
182
+ let(:private_data) { 'md5ac4bbe016b808c3c0b816981f240dcae' }
183
+ let(:private_data_upcase) { private_data.upcase }
184
+ let(:credential_data) {{
185
+ workspace_id: workspace.id,
186
+ user_id: user.id,
187
+ origin_type: :manual,
188
+ username: 'admin',
189
+ private_data: private_data,
190
+ private_type: :postgres_md5
191
+ }}
192
+ it 'creates a private cred' do
193
+ expect{ test_object.create_credential(credential_data) }.to change{ Metasploit::Credential::PostgresMD5.count }.by(1)
194
+ end
195
+ let(:credential_data_upcase) {{
196
+ workspace_id: workspace.id,
197
+ user_id: user.id,
198
+ origin_type: :manual,
199
+ username: 'admin',
200
+ private_data: private_data_upcase,
201
+ private_type: :postgres_md5
202
+ }}
203
+ it 'allows for the recreation of core with case insensitive private credentials set to different case' do
204
+ expect{ test_object.create_credential(credential_data) }.to change{ Metasploit::Credential::PostgresMD5.count }.by(1)
205
+ expect{ Metasploit::Credential::Core.first.destroy }.to change{ Metasploit::Credential::Core.count }.by(-1)
206
+ expect( Metasploit::Credential::PostgresMD5.count ).to eq(1)
207
+ expect( Metasploit::Credential::Core.count ).to eq(0)
208
+ expect{ test_object.create_credential(credential_data_upcase) }.to change{ Metasploit::Credential::Core.count }.by(1)
209
+ expect( Metasploit::Credential::PostgresMD5.count ).to eq(1)
210
+ end
211
+ end
171
212
  end
172
213
 
173
214
  context '#create_credential_and_login' do
@@ -821,6 +862,16 @@ RSpec.describe Metasploit::Credential::Creation do
821
862
  expect{ test_object.create_credential_private(opts) }.to change{ Metasploit::Credential::KrbEncKey.count }.by(1)
822
863
  end
823
864
  end
865
+
866
+ context 'when :private_type is pkcs12' do
867
+ it 'creates a Metasploit::Credential::Pkcs12' do
868
+ opts = {
869
+ private_data: FactoryBot.build(:metasploit_credential_pkcs12).data,
870
+ private_type: :pkcs12
871
+ }
872
+ expect{ test_object.create_credential_private(opts) }.to change{ Metasploit::Credential::Pkcs12.count }.by(1)
873
+ end
874
+ end
824
875
  end
825
876
 
826
877
  context '#create_credential_core' do
@@ -148,4 +148,30 @@ RSpec.describe Metasploit::Credential::KrbEncKey, type: :model do
148
148
  end
149
149
  end
150
150
  end
151
+
152
+ context 'serialization' do
153
+ context '#first_or_create' do
154
+ let(:data) { 'msf_krbenckey:23:e22e04519aa757d12f1219c4f31252f4:' }
155
+ let(:upcase_data) {data.upcase}
156
+
157
+ context 'creates a new instance that stores case-insensitive value' do
158
+ it 'creates case insensitive data' do
159
+ expect{ Metasploit::Credential::KrbEncKey.where(data: data).first_or_create }.to change{Metasploit::Credential::KrbEncKey.count}.by(1)
160
+ expect{ Metasploit::Credential::KrbEncKey.where(data: upcase_data).first_or_create }.not_to change{Metasploit::Credential::KrbEncKey.count}
161
+ end
162
+ end
163
+
164
+ context 'finds an existing case insensitive match' do
165
+ let(:krb_enc_key) do
166
+ FactoryBot.build(
167
+ :metasploit_credential_krb_enc_key,
168
+ data: upcase_data
169
+ )
170
+ end
171
+ it 'successfully looks up credential in case insensitive way' do
172
+ expect( krb_enc_key.data ).to eq(data)
173
+ end
174
+ end
175
+ end
176
+ end
151
177
  end
@@ -397,5 +397,29 @@ RSpec.describe Metasploit::Credential::NTLMHash, type: :model do
397
397
  end
398
398
  end
399
399
 
400
+ context 'serialization' do
401
+ context '#first_or_create' do
402
+ let(:data) { 'aad3b435b51404eeaad3b435b51404ee:4dc0249ad90ab626362050195893c788' }
403
+ let(:upcase_data) {data.upcase}
404
+
405
+ context 'creates a new instance that stores case-insensitive value' do
406
+ it 'creates case insensitive data' do
407
+ expect{ Metasploit::Credential::NTLMHash.where(data: data).first_or_create }.to change{Metasploit::Credential::NTLMHash.count}.by(1)
408
+ expect{ Metasploit::Credential::NTLMHash.where(data: upcase_data).first_or_create }.not_to change{Metasploit::Credential::NTLMHash.count}
409
+ end
410
+ end
400
411
 
412
+ context 'finds an existing case insensitive match' do
413
+ let(:ntlm_hash) do
414
+ FactoryBot.build(
415
+ :metasploit_credential_ntlm_hash,
416
+ data: upcase_data
417
+ )
418
+ end
419
+ it 'successfully looks up credential in case insensitive way' do
420
+ expect( ntlm_hash.data ).to eq(data)
421
+ end
422
+ end
423
+ end
424
+ end
401
425
  end
@@ -0,0 +1,116 @@
1
+ RSpec.describe Metasploit::Credential::Pkcs12, type: :model do
2
+ it_should_behave_like 'Metasploit::Concern.run'
3
+
4
+ context 'factories' do
5
+ context 'metasploit_credential_pkcs12' do
6
+ subject(:metasploit_credential_pkcs12) do
7
+ FactoryBot.build(:metasploit_credential_pkcs12)
8
+ end
9
+
10
+ it { is_expected.to be_valid }
11
+ end
12
+ end
13
+
14
+ context 'validations' do
15
+ it { is_expected.to validate_presence_of :data }
16
+
17
+ context 'on #data' do
18
+ subject(:data_errors) do
19
+ pkcs12.errors[:data]
20
+ end
21
+
22
+ let(:pkcs12) do
23
+ FactoryBot.build(:metasploit_credential_pkcs12)
24
+ end
25
+
26
+ context '#readable' do
27
+ context 'with #data' do
28
+ context 'with error' do
29
+ #
30
+ # Shared Examples
31
+ #
32
+
33
+ shared_examples_for 'exception' do
34
+ it 'includes error class' do
35
+ exception_class_name = exception.class.to_s
36
+ expect(
37
+ data_errors.any? { |error|
38
+ error.include? exception_class_name
39
+ }
40
+ ).to be true
41
+ end
42
+
43
+ it 'includes error message' do
44
+ exception_message = exception.to_s
45
+
46
+ expect(
47
+ data_errors.any? { |error|
48
+ error.include? exception_message
49
+ }
50
+ ).to be true
51
+ end
52
+ end
53
+
54
+ #
55
+ # Callbacks
56
+ #
57
+
58
+ before(:example) do
59
+ expect(pkcs12).to receive(:openssl_pkcs12).and_raise(exception)
60
+
61
+ pkcs12.valid?
62
+ end
63
+
64
+ context 'with ArgumentError' do
65
+ let(:exception) do
66
+ ArgumentError.new("Bad Argument")
67
+ end
68
+
69
+ it_should_behave_like 'exception'
70
+ end
71
+
72
+ context 'with OpenSSL::PKCS12::PKCS12Error' do
73
+ let(:exception) do
74
+ OpenSSL::PKCS12::PKCS12Error.new('mac verify failure')
75
+ end
76
+
77
+ it_should_behave_like 'exception'
78
+ end
79
+ end
80
+
81
+ context 'without error' do
82
+ before(:example) do
83
+ pkcs12.valid?
84
+ end
85
+
86
+ it { is_expected.to be_empty }
87
+ end
88
+ end
89
+
90
+ context 'without #data' do
91
+ let(:error) do
92
+ I18n.translate!('errors.messages.blank')
93
+ end
94
+
95
+ #
96
+ # Callbacks
97
+ #
98
+
99
+ before(:example) do
100
+ pkcs12.data = nil
101
+
102
+ pkcs12.valid?
103
+ end
104
+
105
+ it { is_expected.to include(error) }
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ describe 'human name' do
112
+ it 'properly determines the model\'s human name' do
113
+ expect(described_class.model_name.human).to eq('Pkcs12 (pfx)')
114
+ end
115
+ end
116
+ end
@@ -119,4 +119,30 @@ RSpec.describe Metasploit::Credential::PostgresMD5, type: :model do
119
119
  end
120
120
  end
121
121
 
122
+ context 'serialization' do
123
+ context '#first_or_create' do
124
+ let(:data) { "md5#{SecureRandom.hex(16)}" }
125
+ let(:upcase_data) {data.upcase}
126
+
127
+ context 'creates a new instance that stores case-insensitive value' do
128
+ it 'creates case insensitive data' do
129
+ expect{ Metasploit::Credential::PostgresMD5.where(data: data).first_or_create }.to change{Metasploit::Credential::PostgresMD5.count}.by(1)
130
+ expect{ Metasploit::Credential::PostgresMD5.where(data: upcase_data).first_or_create }.not_to change{Metasploit::Credential::PostgresMD5.count}
131
+ end
132
+ end
133
+
134
+ context 'finds an existing case insensitive match' do
135
+ let(:postgres_md5) do
136
+ FactoryBot.build(
137
+ :metasploit_credential_postgres_md5,
138
+ data: upcase_data
139
+ )
140
+ end
141
+
142
+ it 'successfully looks up credential in case insensitive way' do
143
+ expect( postgres_md5.data ).to eq(data)
144
+ end
145
+ end
146
+ end
147
+ end
122
148
  end
@@ -135,6 +135,7 @@ RSpec.describe Metasploit::Credential::Private, type: :model do
135
135
  Metasploit::Credential::Password
136
136
  Metasploit::Credential::SSHKey
137
137
  Metasploit::Credential::KrbEncKey
138
+ Metasploit::Credential::Pkcs12
138
139
  }
139
140
  end
140
141
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metasploit-credential
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.3
4
+ version: 6.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Metasploit Hackers
@@ -93,7 +93,7 @@ cert_chain:
93
93
  EknWpNgVhohbot1lfVAMmIhdtOVaRVcQQixWPwprDj/ydB8ryDMDosIMcw+fkoXU
94
94
  9GJsSaSRRYQ9UUkVL27b64okU8D48m8=
95
95
  -----END CERTIFICATE-----
96
- date: 2023-04-06 00:00:00.000000000 Z
96
+ date: 2023-05-19 00:00:00.000000000 Z
97
97
  dependencies:
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: metasploit-concern
@@ -255,6 +255,7 @@ files:
255
255
  - app/models/metasploit/credential/origin/session.rb
256
256
  - app/models/metasploit/credential/password.rb
257
257
  - app/models/metasploit/credential/password_hash.rb
258
+ - app/models/metasploit/credential/pkcs12.rb
258
259
  - app/models/metasploit/credential/postgres_md5.rb
259
260
  - app/models/metasploit/credential/private.rb
260
261
  - app/models/metasploit/credential/public.rb
@@ -288,7 +289,9 @@ files:
288
289
  - db/migrate/20150106201450_old_creds_to_new_creds2.rb
289
290
  - db/migrate/20161107153145_recreate_index_on_private_data_and_type.rb
290
291
  - db/migrate/20161107203710_create_index_on_private_data_and_type_for_ssh_key.rb
292
+ - db/migrate/20221209005658_create_index_on_private_data_and_type_for_pkcs12.rb
291
293
  - lib/metasploit/credential.rb
294
+ - lib/metasploit/credential/case_insensitive_serializer.rb
292
295
  - lib/metasploit/credential/core_validations.rb
293
296
  - lib/metasploit/credential/creation.rb
294
297
  - lib/metasploit/credential/engine.rb
@@ -366,6 +369,7 @@ files:
366
369
  - spec/factories/metasploit/credential/origin/sessions.rb
367
370
  - spec/factories/metasploit/credential/password_hashes.rb
368
371
  - spec/factories/metasploit/credential/passwords.rb
372
+ - spec/factories/metasploit/credential/pkcs12.rb
369
373
  - spec/factories/metasploit/credential/postgres_md5.rb
370
374
  - spec/factories/metasploit/credential/privates.rb
371
375
  - spec/factories/metasploit/credential/publics.rb
@@ -401,6 +405,7 @@ files:
401
405
  - spec/models/metasploit/credential/origin/session_spec.rb
402
406
  - spec/models/metasploit/credential/password_hash_spec.rb
403
407
  - spec/models/metasploit/credential/password_spec.rb
408
+ - spec/models/metasploit/credential/pkcs12_spec.rb
404
409
  - spec/models/metasploit/credential/postgres_md5_spec.rb
405
410
  - spec/models/metasploit/credential/private_spec.rb
406
411
  - spec/models/metasploit/credential/public_spec.rb
@@ -497,6 +502,7 @@ test_files:
497
502
  - spec/factories/metasploit/credential/origin/sessions.rb
498
503
  - spec/factories/metasploit/credential/password_hashes.rb
499
504
  - spec/factories/metasploit/credential/passwords.rb
505
+ - spec/factories/metasploit/credential/pkcs12.rb
500
506
  - spec/factories/metasploit/credential/postgres_md5.rb
501
507
  - spec/factories/metasploit/credential/privates.rb
502
508
  - spec/factories/metasploit/credential/publics.rb
@@ -532,6 +538,7 @@ test_files:
532
538
  - spec/models/metasploit/credential/origin/session_spec.rb
533
539
  - spec/models/metasploit/credential/password_hash_spec.rb
534
540
  - spec/models/metasploit/credential/password_spec.rb
541
+ - spec/models/metasploit/credential/pkcs12_spec.rb
535
542
  - spec/models/metasploit/credential/postgres_md5_spec.rb
536
543
  - spec/models/metasploit/credential/private_spec.rb
537
544
  - spec/models/metasploit/credential/public_spec.rb
metadata.gz.sig CHANGED
Binary file