Skip to content

Commit 1fc9516

Browse files
authored
Merge pull request rapid7#19736 from cdelafuente-r7/enh/pkcs12/add_metadata
Report CA, ADCS Template and Password along with Pkcs12 in the database
2 parents 0f4c73b + 41f25a9 commit 1fc9516

File tree

7 files changed

+163
-44
lines changed

7 files changed

+163
-44
lines changed

.github/workflows/command_shell_acceptance.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ jobs:
132132
run: git config --system core.longpaths true
133133

134134
- name: Setup Ruby
135+
run: git config --system core.longpaths true
135136
env:
136137
BUNDLE_FORCE_RUBY_PLATFORM: true
137138
uses: ruby/setup-ruby@v1

.github/workflows/shared_meterpreter_acceptance.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ jobs:
196196
run: git config --system core.longpaths true
197197

198198
- name: Setup Ruby
199+
run: git config --system core.longpaths true
199200
env:
200201
BUNDLE_FORCE_RUBY_PLATFORM: true
201202
# Required for macos13 pg gem compilation

Gemfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ GEM
293293
activesupport (~> 7.0)
294294
railties (~> 7.0)
295295
zeitwerk
296-
metasploit-credential (6.0.12)
296+
metasploit-credential (6.0.14)
297297
metasploit-concern
298298
metasploit-model
299299
metasploit_data_models (>= 5.0.0)
@@ -303,12 +303,12 @@ GEM
303303
rex-socket
304304
rubyntlm
305305
rubyzip
306-
metasploit-model (5.0.2)
306+
metasploit-model (5.0.3)
307307
activemodel (~> 7.0)
308308
activesupport (~> 7.0)
309309
railties (~> 7.0)
310310
metasploit-payloads (2.0.189)
311-
metasploit_data_models (6.0.6)
311+
metasploit_data_models (6.0.9)
312312
activerecord (~> 7.0)
313313
activesupport (~> 7.0)
314314
arel-helpers

db/schema.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[7.0].define(version: 2022_12_09_005658) do
13+
ActiveRecord::Schema[7.0].define(version: 2025_02_04_172657) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "plpgsql"
1616

@@ -314,6 +314,7 @@
314314
t.datetime "created_at", precision: nil, null: false
315315
t.datetime "updated_at", precision: nil, null: false
316316
t.string "jtr_format"
317+
t.jsonb "metadata", default: {}, null: false
317318
t.index "type, decode(md5(data), 'hex'::text)", name: "index_metasploit_credential_privates_on_type_and_data_pkcs12", unique: true, where: "((type)::text = 'Metasploit::Credential::Pkcs12'::text)"
318319
t.index "type, decode(md5(data), 'hex'::text)", name: "index_metasploit_credential_privates_on_type_and_data_sshkey", unique: true, where: "((type)::text = 'Metasploit::Credential::SSHKey'::text)"
319320
t.index ["type", "data"], name: "index_metasploit_credential_privates_on_type_and_data", unique: true, where: "(NOT (((type)::text = 'Metasploit::Credential::SSHKey'::text) OR ((type)::text = 'Metasploit::Credential::Pkcs12'::text)))"

lib/msf/core/exploit/remote/ms_icpr.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,8 @@ def do_request_cert(icpr, opts)
251251
workspace_id: myworkspace_id,
252252
username: upn || datastore['SMBUser'],
253253
private_type: :pkcs12,
254-
# pkcs12 is a binary format, but for persisting we Base64 encode it
255254
private_data: Base64.strict_encode64(pkcs12.to_der),
255+
private_metadata: { adcs_ca: datastore['CA'], adcs_template: cert_template },
256256
origin_type: :service,
257257
module_fullname: fullname
258258
}

lib/msf/ui/console/command_dispatcher/creds.rb

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,19 @@ def cmd_creds_help
100100
print_line "Usage - Adding credentials:"
101101
print_line " creds add uses the following named parameters."
102102
{
103-
user: 'Public, usually a username',
104-
password: 'Private, private_type Password.',
105-
ntlm: 'Private, private_type NTLM Hash.',
106-
postgres: 'Private, private_type postgres MD5',
107-
pkcs12: 'Private, private_type pkcs12 archive file, must be a file path.',
108-
'ssh-key' => 'Private, private_type SSH key, must be a file path.',
109-
hash: 'Private, private_type Nonreplayable hash',
110-
jtr: 'Private, private_type John the Ripper hash type.',
111-
realm: 'Realm, ',
112-
'realm-type'=>"Realm, realm_type (#{Metasploit::Model::Realm::Key::SHORT_NAMES.keys.join(' ')}), defaults to domain."
103+
user: 'Public, usually a username',
104+
password: 'Private, private_type Password.',
105+
ntlm: 'Private, private_type NTLM Hash.',
106+
postgres: 'Private, private_type postgres MD5',
107+
pkcs12: 'Private, private_type pkcs12 archive file, must be a file path.',
108+
'ssh-key' => 'Private, private_type SSH key, must be a file path.',
109+
hash: 'Private, private_type Nonreplayable hash',
110+
jtr: 'Private, private_type John the Ripper hash type.',
111+
realm: 'Realm, ',
112+
'realm-type' => "Realm, realm_type (#{Metasploit::Model::Realm::Key::SHORT_NAMES.keys.join(' ')}), defaults to domain.",
113+
'adcs-ca' => 'CA, Certificate Authority that issued the pkcs12 certificate',
114+
'adcs-template' => 'ADCS Template, template used to issue the pkcs12 certificate',
115+
'pkcs12-password' => 'The password to decrypt the Pkcs12, defaults to an empty password'
113116
}.each_pair do |keyword, description|
114117
print_line " #{keyword.to_s.ljust 10}: #{description}"
115118
end
@@ -206,7 +209,7 @@ def creds_add(*args)
206209
end
207210

208211
begin
209-
params.assert_valid_keys('user','password','realm','realm-type','ntlm','ssh-key','hash','address','port','protocol', 'service-name', 'jtr', 'pkcs12', 'postgres')
212+
params.assert_valid_keys('user','password','realm','realm-type','ntlm','ssh-key','hash','address','port','protocol', 'service-name', 'jtr', 'pkcs12', 'postgres', 'adcs-ca', 'adcs-template', 'pkcs12-password')
210213
rescue ArgumentError => e
211214
print_error(e.message)
212215
end
@@ -276,6 +279,10 @@ def creds_add(*args)
276279
end
277280
data[:private_type] = :pkcs12
278281
data[:private_data] = pkcs12_data
282+
data[:private_metadata] = {}
283+
data[:private_metadata][:adcs_ca] = params['adcs-ca'] if params['adcs-ca']
284+
data[:private_metadata][:adcs_template] = params['adcs-template'] if params['adcs-template']
285+
data[:private_metadata][:pkcs12_password] = params['pkcs12-password'] if params['pkcs12-password']
279286
end
280287

281288
if params.key? 'hash'
@@ -305,7 +312,7 @@ def creds_add(*args)
305312
framework.db.create_credential(data)
306313
end
307314
rescue ActiveRecord::RecordInvalid => e
308-
print_error("Failed to add #{data['private_type']}: #{e}")
315+
print_error("Failed to add #{data[:private_type]}: #{e}")
309316
end
310317
end
311318

@@ -414,11 +421,13 @@ def creds_search(*args)
414421
when 'password'
415422
Metasploit::Credential::Password
416423
when 'hash'
417-
Metasploit::Credential::PasswordHash
424+
Metasploit::Credential::NonreplayableHash
418425
when 'ntlm'
419426
Metasploit::Credential::NTLMHash
420427
when 'KrbEncKey'.downcase
421428
Metasploit::Credential::KrbEncKey
429+
when 'pkcs12'
430+
Metasploit::Credential::Pkcs12
422431
when *Metasploit::Credential::NonreplayableHash::VALID_JTR_FORMATS
423432
opts[:jtr_format] = ptype
424433
Metasploit::Credential::NonreplayableHash

spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb

Lines changed: 133 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -212,32 +212,48 @@
212212
realm: nil,
213213
workspace: framework.db.workspace)
214214
end
215+
let!(:pkcs12_subject) { '/C=FR/O=MyOrg/OU=MyUnit/CN=SubjectTestName' }
216+
let!(:pkcs12_issuer) { '/C=US/O=MyIssuer/OU=MyIssuerUnit/CN=IssuerTestName' }
217+
let!(:pkcs12_ca) { 'testCA' }
218+
let!(:pkcs12_adcs_template) { 'TestTemplate' }
219+
let!(:pkcs12_core) do
220+
priv = FactoryBot.create(:metasploit_credential_pkcs12_with_ca_and_adcs_template,
221+
subject: pkcs12_subject,
222+
issuer: pkcs12_issuer,
223+
adcs_ca: pkcs12_ca,
224+
adcs_template: pkcs12_adcs_template)
225+
FactoryBot.create(:metasploit_credential_core,
226+
origin: FactoryBot.create(:metasploit_credential_origin_import),
227+
private: priv,
228+
public: nil,
229+
realm: nil,
230+
workspace: framework.db.workspace)
231+
end
215232

216-
# # Somehow this is hitting a unique constraint on Cores with the same
217-
# # Public, even though it has a different Private. Skip for now
218-
# let!(:ntlm_core) do
219-
# priv = FactoryBot.create(:metasploit_credential_ntlm_hash, data: ntlm_hash)
220-
# FactoryBot.create(:metasploit_credential_core,
221-
# origin: FactoryBot.create(:metasploit_credential_origin_import),
222-
# private: priv,
223-
# public: pub,
224-
# realm: nil,
225-
# workspace: framework.db.workspace)
226-
# end
227-
# let!(:nonreplayable_core) do
228-
# priv = FactoryBot.create(:metasploit_credential_nonreplayable_hash, data: 'asdf')
229-
# FactoryBot.create(:metasploit_credential_core,
230-
# origin: FactoryBot.create(:metasploit_credential_origin_import),
231-
# private: priv,
232-
# public: pub,
233-
# realm: nil,
234-
# workspace: framework.db.workspace)
235-
# end
233+
let!(:ntlm_core) do
234+
priv = FactoryBot.create(:metasploit_credential_ntlm_hash, data: ntlm_hash)
235+
FactoryBot.create(:metasploit_credential_core,
236+
origin: FactoryBot.create(:metasploit_credential_origin_import),
237+
private: priv,
238+
public: pub,
239+
realm: nil,
240+
workspace: framework.db.workspace)
241+
end
242+
let!(:nonreplayable_core) do
243+
priv = FactoryBot.create(:metasploit_credential_nonreplayable_hash, data: 'asdf')
244+
FactoryBot.create(:metasploit_credential_core,
245+
origin: FactoryBot.create(:metasploit_credential_origin_import),
246+
private: priv,
247+
public: pub,
248+
realm: nil,
249+
workspace: framework.db.workspace)
250+
end
236251

237252
after(:example) do
238-
# ntlm_core.destroy
253+
ntlm_core.destroy
239254
password_core.destroy
240-
# nonreplayable_core.destroy
255+
nonreplayable_core.destroy
256+
pkcs12_core.destroy
241257
end
242258

243259
context 'password' do
@@ -283,16 +299,48 @@
283299

284300
context 'ntlm' do
285301
it 'should show just the ntlm' do
286-
skip 'Weird uniqueness constraint on Core (workspace_id, public_id)'
287302

288303
creds.cmd_creds('-t', 'ntlm')
289304
expect(@output.join("\n")).to match_table <<~TABLE
290305
Credentials
291306
===========
292307
293-
host origin service public private realm private_type JtR Format cracked_password
294-
---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
295-
thisuser 1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c NTLM hash
308+
host origin service public private realm private_type JtR Format cracked_password
309+
---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
310+
thisuser 1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c NTLM hash
311+
312+
TABLE
313+
end
314+
end
315+
316+
context 'nonreplayable' do
317+
it 'should show just the ntlm' do
318+
319+
creds.cmd_creds('-t', 'hash')
320+
expect(@output.join("\n")).to match_table <<~TABLE
321+
Credentials
322+
===========
323+
324+
host origin service public private realm private_type JtR Format cracked_password
325+
---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
326+
thisuser asdf Nonreplayable hash
327+
328+
TABLE
329+
end
330+
end
331+
332+
context 'pkcs12' do
333+
it 'should show just the pkcs12' do
334+
private_str = "subject:#{pkcs12_subject},issuer:#{pkcs12_issuer},ADCS CA:#{pkcs12_ca},ADCS template:#{pkcs12_adcs_template}"
335+
private_str = "#{private_str[0,76]} (TRUNCATED)"
336+
creds.cmd_creds('-t', 'pkcs12')
337+
expect(@output.join("\n")).to match_table <<~TABLE
338+
Credentials
339+
===========
340+
341+
host origin service public private realm private_type JtR Format cracked_password
342+
---- ------ ------- ------ ------- ----- ------------ ---------- ----------------
343+
#{private_str} Pkcs12 (pfx)
296344
297345
TABLE
298346
end
@@ -479,6 +527,65 @@
479527
}.to_not change { Metasploit::Credential::Core.count }
480528
end
481529
end
530+
context 'pkcs12' do
531+
let(:priv) { FactoryBot.build(:metasploit_credential_pkcs12) }
532+
before(:each) do
533+
@file = Tempfile.new('mypkcs12.pfx')
534+
@file.write(Base64.strict_decode64(priv.data))
535+
@file.close
536+
end
537+
it 'creates a core if one does not exist' do
538+
expect {
539+
creds.cmd_creds('add', "pkcs12:#{@file.path}")
540+
}.to change { Metasploit::Credential::Core.count }.by 1
541+
end
542+
it 'does not create a core if it already exists' do
543+
FactoryBot.create(:metasploit_credential_core,
544+
origin: FactoryBot.create(:metasploit_credential_origin_import),
545+
private: priv,
546+
public: nil,
547+
realm: nil,
548+
workspace: framework.db.workspace)
549+
expect {
550+
creds.cmd_creds('add', "pkcs12:#{@file.path}")
551+
}.to_not change { Metasploit::Credential::Core.count }
552+
end
553+
554+
context 'with a password' do
555+
let(:pkcs12_password) { 'mypass' }
556+
let(:priv) {
557+
FactoryBot.build(:metasploit_credential_pkcs12,
558+
pkcs12_password: pkcs12_password,
559+
metadata: { pkcs12_password: pkcs12_password }
560+
)
561+
}
562+
563+
it 'creates a core if the password is correct' do
564+
expect {
565+
creds.cmd_creds('add', "pkcs12:#{@file.path}", "pkcs12-password:#{pkcs12_password}")
566+
}.to change { Metasploit::Credential::Core.count }.by 1
567+
end
568+
569+
it 'does not creates a core if the password is incorrect' do
570+
expect {
571+
creds.cmd_creds('add', "pkcs12:#{@file.path}", "pkcs12-password:wrongpass")
572+
}.to_not change { Metasploit::Credential::Core.count }
573+
end
574+
end
575+
576+
context 'with metadata other than password' do
577+
let(:adcs_ca) { 'myca' }
578+
let(:adcs_template) { 'mytemplate' }
579+
580+
it 'creates a core if the password is correct' do
581+
expect {
582+
creds.cmd_creds('add', "pkcs12:#{@file.path}", "adcs-ca:#{adcs_ca}", "adcs-template:#{adcs_template}")
583+
}.to change { Metasploit::Credential::Core.count }.by 1
584+
expect(Metasploit::Credential::Pkcs12.first.adcs_ca).to eq(adcs_ca)
585+
expect(Metasploit::Credential::Pkcs12.first.adcs_template).to eq(adcs_template)
586+
end
587+
end
588+
end
482589
end
483590
context 'realm-types' do
484591
Metasploit::Model::Realm::Key::SHORT_NAMES.each do |short_name, long_name|

0 commit comments

Comments
 (0)