metasploit-credential 6.0.3 → 6.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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