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 +4 -4
- checksums.yaml.gz.sig +2 -2
- data/app/models/metasploit/credential/krb_enc_key.rb +2 -10
- data/app/models/metasploit/credential/ntlm_hash.rb +5 -11
- data/app/models/metasploit/credential/pkcs12.rb +84 -0
- data/app/models/metasploit/credential/postgres_md5.rb +2 -10
- data/app/models/metasploit/credential/private.rb +1 -0
- data/config/locales/en.yml +6 -1
- data/db/migrate/20221209005658_create_index_on_private_data_and_type_for_pkcs12.rb +32 -0
- data/lib/metasploit/credential/case_insensitive_serializer.rb +9 -0
- data/lib/metasploit/credential/creation.rb +2 -0
- data/lib/metasploit/credential/importer/core.rb +1 -2
- data/lib/metasploit/credential/version.rb +1 -1
- data/lib/metasploit/credential.rb +2 -0
- data/spec/dummy/config/database.yml +2 -2
- data/spec/factories/metasploit/credential/pkcs12.rb +37 -0
- data/spec/lib/metasploit/credential/creation_spec.rb +53 -2
- data/spec/models/metasploit/credential/krb_enc_key_spec.rb +26 -0
- data/spec/models/metasploit/credential/ntlm_hash_spec.rb +24 -0
- data/spec/models/metasploit/credential/pkcs12_spec.rb +116 -0
- data/spec/models/metasploit/credential/postgres_md5_spec.rb +26 -0
- data/spec/models/metasploit/credential/private_spec.rb +1 -0
- data.tar.gz.sig +0 -0
- metadata +9 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c890cd98fc73ff6cbd909ded7207646f534477a4308f60f1e7dc09231cd29d88
|
4
|
+
data.tar.gz: 62fd27bcb61c852fb705cb89a0848771f6c4ae64b6a3c76202228ea6d2037969
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06bf39bbda84fc40c4f25085a39e4f27211a50246879fbe3b50f034c502a9b6fcf7cebbe9292912a4bfc42f8804837a45e8cbc332253b5f3fc5b122983f6b23e
|
7
|
+
data.tar.gz: d361ca8222849ab597accea494fef812339b971537bcd9cdbc3259eb19271a57f31cd1773c8338282211bf43569ee507888eb09f7326799b9ec6d66140bc50cd
|
checksums.yaml.gz.sig
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
w
|
2
|
-
|
1
|
+
�,-�Ds����4'w��nB�rI�}m��c�z.�?�{?D�ls�BG�� >ה)�N�:����u��#[3�[�������ޚ{��0aƺ%�|����#�P~?��P�'F��#,N;��$��.�U���<�8:�w��t��{-
|
2
|
+
�r4A��o�z��<��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
|
-
|
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
|
-
#
|
58
|
+
# Serializers
|
59
59
|
#
|
60
60
|
|
61
|
-
|
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
|
-
|
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')
|
data/config/locales/en.yml
CHANGED
@@ -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
|
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
|
@@ -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:
|
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
|
@@ -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-
|
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-
|
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
|
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.
|
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-
|
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
|