From d24158360dd7e566b3101ac2fe0860df36e28641 Mon Sep 17 00:00:00 2001 From: Christophe De La Fuente Date: Mon, 16 Dec 2024 12:18:40 +0100 Subject: [PATCH 1/3] Update Pkcs12-related code to report CA and ADCS Template to the database - Update the `creds` command to add Pkcs12 private credentials with metadata. - Update `ms_icpr` module to store metadata. --- lib/msf/core/exploit/remote/ms_icpr.rb | 8 +- .../ui/console/command_dispatcher/creds.rb | 34 +++-- .../console/command_dispatcher/creds_spec.rb | 124 ++++++++++++++---- 3 files changed, 125 insertions(+), 41 deletions(-) diff --git a/lib/msf/core/exploit/remote/ms_icpr.rb b/lib/msf/core/exploit/remote/ms_icpr.rb index 925baa796dd0..ae4fbcf80b3f 100644 --- a/lib/msf/core/exploit/remote/ms_icpr.rb +++ b/lib/msf/core/exploit/remote/ms_icpr.rb @@ -232,8 +232,12 @@ def do_request_cert(icpr, opts) workspace_id: myworkspace_id, username: upn || datastore['SMBUser'], private_type: :pkcs12, - # pkcs12 is a binary format, but for persisting we Base64 encode it - private_data: Base64.strict_encode64(pkcs12.to_der), + private_data: Metasploit::Credential::Pkcs12.build_data( + # pkcs12 is a binary format, but for persisting we Base64 encode it + pkcs12: Base64.strict_encode64(pkcs12.to_der), + ca: datastore['CA'], + adcs_template: cert_template + ), origin_type: :service, module_fullname: fullname } diff --git a/lib/msf/ui/console/command_dispatcher/creds.rb b/lib/msf/ui/console/command_dispatcher/creds.rb index 220365a4c70f..2a165e621c5c 100644 --- a/lib/msf/ui/console/command_dispatcher/creds.rb +++ b/lib/msf/ui/console/command_dispatcher/creds.rb @@ -100,16 +100,18 @@ def cmd_creds_help print_line "Usage - Adding credentials:" print_line " creds add uses the following named parameters." { - user: 'Public, usually a username', - password: 'Private, private_type Password.', - ntlm: 'Private, private_type NTLM Hash.', - postgres: 'Private, private_type postgres MD5', - pkcs12: 'Private, private_type pkcs12 archive file, must be a file path.', - 'ssh-key' => 'Private, private_type SSH key, must be a file path.', - hash: 'Private, private_type Nonreplayable hash', - jtr: 'Private, private_type John the Ripper hash type.', - realm: 'Realm, ', - 'realm-type'=>"Realm, realm_type (#{Metasploit::Model::Realm::Key::SHORT_NAMES.keys.join(' ')}), defaults to domain." + user: 'Public, usually a username', + password: 'Private, private_type Password.', + ntlm: 'Private, private_type NTLM Hash.', + postgres: 'Private, private_type postgres MD5', + pkcs12: 'Private, private_type pkcs12 archive file, must be a file path.', + 'ssh-key' => 'Private, private_type SSH key, must be a file path.', + hash: 'Private, private_type Nonreplayable hash', + jtr: 'Private, private_type John the Ripper hash type.', + realm: 'Realm, ', + 'realm-type' => "Realm, realm_type (#{Metasploit::Model::Realm::Key::SHORT_NAMES.keys.join(' ')}), defaults to domain.", + ca: 'CA, Certificate Authority that issued the pkcs12 certificate', + 'adcs-template' => 'ADCS Template, template used to issue the pkcs12 certificate' }.each_pair do |keyword, description| print_line " #{keyword.to_s.ljust 10}: #{description}" end @@ -206,7 +208,7 @@ def creds_add(*args) end begin - params.assert_valid_keys('user','password','realm','realm-type','ntlm','ssh-key','hash','address','port','protocol', 'service-name', 'jtr', 'pkcs12', 'postgres') + params.assert_valid_keys('user','password','realm','realm-type','ntlm','ssh-key','hash','address','port','protocol', 'service-name', 'jtr', 'pkcs12', 'postgres', 'ca', 'adcs-template') rescue ArgumentError => e print_error(e.message) end @@ -275,7 +277,11 @@ def creds_add(*args) print_error("Failed to add pkcs12 archive: #{e}") end data[:private_type] = :pkcs12 - data[:private_data] = pkcs12_data + data[:private_data] = Metasploit::Credential::Pkcs12.build_data( + pkcs12: pkcs12_data, + ca: params['ca'], + adcs_template: params['adcs-template'] + ) end if params.key? 'hash' @@ -414,11 +420,13 @@ def creds_search(*args) when 'password' Metasploit::Credential::Password when 'hash' - Metasploit::Credential::PasswordHash + Metasploit::Credential::NonreplayableHash when 'ntlm' Metasploit::Credential::NTLMHash when 'KrbEncKey'.downcase Metasploit::Credential::KrbEncKey + when 'pkcs12' + Metasploit::Credential::Pkcs12 when *Metasploit::Credential::NonreplayableHash::VALID_JTR_FORMATS opts[:jtr_format] = ptype Metasploit::Credential::NonreplayableHash diff --git a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb index 7e08644e83bf..67dcef8d3faf 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb @@ -212,32 +212,48 @@ realm: nil, workspace: framework.db.workspace) end + let!(:pkcs12_subject) { '/C=FR/O=MyOrg/OU=MyUnit/CN=SubjectTestName' } + let!(:pkcs12_issuer) { '/C=US/O=MyIssuer/OU=MyIssuerUnit/CN=IssuerTestName' } + let!(:pkcs12_ca) { 'testCA' } + let!(:pkcs12_adcs_template) { 'TestTemplate' } + let!(:pkcs12_core) do + priv = FactoryBot.create(:metasploit_credential_pkcs12_with_ca_and_adcs_template, + subject: pkcs12_subject, + issuer: pkcs12_issuer, + ca: pkcs12_ca, + adcs_template: pkcs12_adcs_template) + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: nil, + workspace: framework.db.workspace) + end - # # Somehow this is hitting a unique constraint on Cores with the same - # # Public, even though it has a different Private. Skip for now - # let!(:ntlm_core) do - # priv = FactoryBot.create(:metasploit_credential_ntlm_hash, data: ntlm_hash) - # FactoryBot.create(:metasploit_credential_core, - # origin: FactoryBot.create(:metasploit_credential_origin_import), - # private: priv, - # public: pub, - # realm: nil, - # workspace: framework.db.workspace) - # end - # let!(:nonreplayable_core) do - # priv = FactoryBot.create(:metasploit_credential_nonreplayable_hash, data: 'asdf') - # FactoryBot.create(:metasploit_credential_core, - # origin: FactoryBot.create(:metasploit_credential_origin_import), - # private: priv, - # public: pub, - # realm: nil, - # workspace: framework.db.workspace) - # end + let!(:ntlm_core) do + priv = FactoryBot.create(:metasploit_credential_ntlm_hash, data: ntlm_hash) + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), + private: priv, + public: pub, + realm: nil, + workspace: framework.db.workspace) + end + let!(:nonreplayable_core) do + priv = FactoryBot.create(:metasploit_credential_nonreplayable_hash, data: 'asdf') + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), + private: priv, + public: pub, + realm: nil, + workspace: framework.db.workspace) + end after(:example) do - # ntlm_core.destroy + ntlm_core.destroy password_core.destroy - # nonreplayable_core.destroy + nonreplayable_core.destroy + pkcs12_core.destroy end context 'password' do @@ -283,16 +299,48 @@ context 'ntlm' do it 'should show just the ntlm' do - skip 'Weird uniqueness constraint on Core (workspace_id, public_id)' creds.cmd_creds('-t', 'ntlm') expect(@output.join("\n")).to match_table <<~TABLE Credentials =========== - host origin service public private realm private_type JtR Format cracked_password - ---- ------ ------- ------ ------- ----- ------------ ---------- ---------------- - thisuser 1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c NTLM hash + host origin service public private realm private_type JtR Format cracked_password + ---- ------ ------- ------ ------- ----- ------------ ---------- ---------------- + thisuser 1443d06412d8c0e6e72c57ef50f76a05:27c433245e4763d074d30a05aae0af2c NTLM hash + + TABLE + end + end + + context 'nonreplayable' do + it 'should show just the ntlm' do + + creds.cmd_creds('-t', 'hash') + expect(@output.join("\n")).to match_table <<~TABLE + Credentials + =========== + + host origin service public private realm private_type JtR Format cracked_password + ---- ------ ------- ------ ------- ----- ------------ ---------- ---------------- + thisuser asdf Nonreplayable hash + + TABLE + end + end + + context 'pkcs12' do + it 'should show just the pkcs12' do + private_str = "subject:#{pkcs12_subject},issuer:#{pkcs12_issuer},CA:#{pkcs12_ca},ADCS_template:#{pkcs12_adcs_template}" + private_str = "#{private_str[0,76]} (TRUNCATED)" + creds.cmd_creds('-t', 'pkcs12') + expect(@output.join("\n")).to match_table <<~TABLE + Credentials + =========== + + host origin service public private realm private_type JtR Format cracked_password + ---- ------ ------- ------ ------- ----- ------------ ---------- ---------------- + #{private_str} Pkcs12 (pfx) TABLE end @@ -479,6 +527,30 @@ }.to_not change { Metasploit::Credential::Core.count } end end + context 'pkcs12' do + let(:priv) { FactoryBot.create(:metasploit_credential_pkcs12) } + before(:each) do + @file = Tempfile.new('mypkcs12.pfx') + @file.write(Base64.strict_decode64(priv.pkcs12)) + @file.close + end + it 'creates a core if one does not exist' do + expect { + creds.cmd_creds('add', "pkcs12:#{@file.path}") + }.to change { Metasploit::Credential::Core.count }.by 1 + end + it 'does not create a core if it already exists' do + FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), + private: priv, + public: nil, + realm: nil, + workspace: framework.db.workspace) + expect { + creds.cmd_creds('add', "pkcs12:#{@file.path}") + }.to_not change { Metasploit::Credential::Core.count } + end + end end context 'realm-types' do Metasploit::Model::Realm::Key::SHORT_NAMES.each do |short_name, long_name| From 96f7538d86e32960eb074c5e23ead979e85111c4 Mon Sep 17 00:00:00 2001 From: Christophe De La Fuente Date: Mon, 16 Dec 2024 15:06:35 +0100 Subject: [PATCH 2/3] Point Gemfile to the metasploit-credentials feature branch on cdelafuente-r7 repo --- Gemfile | 2 ++ Gemfile.lock | 27 +++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 83b7b2811fbd..391cff021bc8 100644 --- a/Gemfile +++ b/Gemfile @@ -3,6 +3,8 @@ source 'https://rubygems.org' # spec.add_runtime_dependency '', [] gemspec name: 'metasploit-framework' +gem 'metasploit-credential', git: 'git@github.com:cdelafuente-r7/metasploit-credential.git', branch: 'enh/MS-9710/add_pkcs12_metadata' + # separate from test as simplecov is not run on travis-ci group :coverage do # code coverage for tests diff --git a/Gemfile.lock b/Gemfile.lock index 4163cd5f8163..887243188ced 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,19 @@ +GIT + remote: git@github.com:cdelafuente-r7/metasploit-credential.git + revision: acc5a012f4bc7e7774af059e778b947cd994da1e + branch: enh/MS-9710/add_pkcs12_metadata + specs: + metasploit-credential (6.0.12) + metasploit-concern + metasploit-model + metasploit_data_models (>= 5.0.0) + net-ssh + pg + railties + rex-socket + rubyntlm + rubyzip + PATH remote: . specs: @@ -286,16 +302,6 @@ GEM activesupport (~> 7.0) railties (~> 7.0) zeitwerk - metasploit-credential (6.0.11) - metasploit-concern - metasploit-model - metasploit_data_models (>= 5.0.0) - net-ssh - pg - railties - rex-socket - rubyntlm - rubyzip metasploit-model (5.0.2) activemodel (~> 7.0) activesupport (~> 7.0) @@ -581,6 +587,7 @@ DEPENDENCIES factory_bot_rails fivemat memory_profiler + metasploit-credential! metasploit-framework! octokit pry-byebug From 4c5a3657159c9ad59246d3450cde001c5ff82b28 Mon Sep 17 00:00:00 2001 From: Christophe De La Fuente Date: Mon, 16 Dec 2024 15:49:18 +0100 Subject: [PATCH 3/3] Update Gemfile to use https --- Gemfile | 2 +- Gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 391cff021bc8..a6a9ebadd26e 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org' # spec.add_runtime_dependency '', [] gemspec name: 'metasploit-framework' -gem 'metasploit-credential', git: 'git@github.com:cdelafuente-r7/metasploit-credential.git', branch: 'enh/MS-9710/add_pkcs12_metadata' +gem 'metasploit-credential', git: 'https://github.com/cdelafuente-r7/metasploit-credential', branch: 'enh/MS-9710/add_pkcs12_metadata' # separate from test as simplecov is not run on travis-ci group :coverage do diff --git a/Gemfile.lock b/Gemfile.lock index 887243188ced..82199bed42a8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,5 +1,5 @@ GIT - remote: git@github.com:cdelafuente-r7/metasploit-credential.git + remote: https://github.com/cdelafuente-r7/metasploit-credential revision: acc5a012f4bc7e7774af059e778b947cd994da1e branch: enh/MS-9710/add_pkcs12_metadata specs: