Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pkcs12 private data type #169

Merged
merged 2 commits into from
Apr 11, 2023

Conversation

adfoster-r7
Copy link
Contributor

@adfoster-r7 adfoster-r7 commented Dec 8, 2022

Adding support to metasploit-credential to persist pkcs12 files

Generating example pfx file:

openssl genpkey -algorithm RSA -out key.pem
openssl req -new -x509 -key key.pem -out cert.pem
openssl pkcs12 -export -in cert.pem -inkey key.pem -out user.pfx

# Verify
openssl pkcs12 -info -in user.pfx

Or ruby with ruby ruby example.rb > user.pfx

require 'openssl'
require 'base64'

key_size = 1024
signing_algorithm = 'SHA256'
subject = '/C=BE/O=Test/OU=Test/CN=Test'
issuer = '/C=BE/O=Test/OU=Test/CN=Test'

password = ''
pkcs12_name = ''

private_key = OpenSSL::PKey::RSA.new(key_size)
public_key = private_key.public_key

cert = OpenSSL::X509::Certificate.new
cert.subject = OpenSSL::X509::Name.parse(subject)
cert.issuer = OpenSSL::X509::Name.parse(issuer)
cert.not_before = Time.now
cert.not_after = Time.now + 365 * 24 * 60 * 60
cert.public_key = public_key
cert.serial = 0x0
cert.version = 2
cert.sign(private_key, OpenSSL::Digest.new(signing_algorithm))

pkcs12 = OpenSSL::PKCS12.create(password, pkcs12_name, private_key, cert)
# pkcs12_base64 = Base64.strict_encode64(pkcs12.to_der)
puts pkcs12

Importing:

msf6 exploit(multi/handler) > creds add user:alice pkcs12:./user.pfx

Viewing:

msf6 exploit(multi/handler) > creds
Credentials
===========

host  origin  service  public  private                                     realm  private_type  JtR Format
----  ------  -------  ------  -------                                     -----  ------------  ----------
                       alice   subject:/ST=a/L=a/O=a,issuer:/ST=a/L=a/O=a         Pkcs12 (pfx)  

Exporting, note that the output is b64

msf6 exploit(multi/handler) > creds -v                             
Credentials                                                        
===========                                                        
                                                                   
host  origin  service  public  private                                   realm  private_type  JtR Format
----  ------  -------  ------  -------                                   -----  ------------  ----------
                       alice   MIIJCQIBAzCCCM8GCSqGS...OAgIIAA==         Pkcs12 (pfx)  

Verifying icpr module:

msf6 auxiliary(admin/dcerpc/icpr_cert) > rerun smbuser=Administrator smbpass=p4$$w0rd rhosts=192.168.123.13 ca=adf3-DC3-CA cert_template=ESC1-Test smbdomain=ADF3.LOCAL alt_upn=Administrator@adf3.local
[*] Reloading module...
[*] Running module against 192.168.123.13

[*] 192.168.123.13:445 - Requesting a certificate...
[+] 192.168.123.13:445 - The requested certificate was issued.
[*] 192.168.123.13:445 - Certificate UPN: Administrator@adf3.local

@adfoster-r7 adfoster-r7 force-pushed the add-pkcs12-private-data-type branch 2 times, most recently from 6905ffd to 19611a9 Compare December 12, 2022 17:11
def up
# Drop the existing index created by 20161107153145_recreate_index_on_private_data_and_type.rb, and recreate it
# with Metasploit::Credential::Pkcs12 ignored
remove_index :metasploit_credential_privates, [:type, :data], if_exists: true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @jmartin-r7 - I Believe we sync'd up on this and we were happy with this approach

Is this a PR that we could merge in ahead of time before going live with 6.3 to derisk things? Or do you think it would be safe enough to merge together with the Kerberos effort

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to do a scaling test but should be reasonable to land early.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With ~25k entries the migration takes no time at all:

-- remove_index(:metasploit_credential_privates, [:type, :data], {:if_exists=>true})
   -> 0.0080s
-- change_table(:metasploit_credential_privates)
   -> 0.5110s
-- remove_index(:metasploit_credential_privates, {:name=>:index_metasploit_credential_privates_on_type_and_data_pkcs12, :if_exists=>true})
   -> 0.0043s

That seems good to me to go ahead with, unless the creds number isn't high enough and we want a second verification with a larger number of creds

Count:

=> select count(*) from metasploit_credential_privates;
24665

Groupings:

=> select type, count(*) from metasploit_credential_privates group by type;

type                              | count
--                                | --
Metasploit::Credential::NTLMHash  | 12341
Metasploit::Credential::KrbEncKey | 6168
Metasploit::Credential::SSHKey    | 6155
Metasploit::Credential::Password  | 1

Created with a quick rc file:

<ruby>
def report_creds(
  user, hash, type: :ntlm_hash, jtr_format: '', realm_key: nil, realm_value: nil,
  rhost: nil, service_name: 'smb', rport: '445', myworkspace_id: nil, module_fullname: nil
)
  rhost ||= "192.168.#{rand(5..240)}.#{rand(5..240)}"
  service_data = {
    address: rhost,
    port: rport,
    service_name: service_name,
    protocol: 'tcp',
    workspace_id: myworkspace_id
  }
  credential_data = {
    module_fullname: module_fullname,
    origin_type: :service,
    private_data: hash,
    private_type: type,
    jtr_format: jtr_format,
    username: user
  }.merge(service_data)
  credential_data[:realm_key] = realm_key if realm_key
  credential_data[:realm_value] = realm_value if realm_value

  cl = framework.db.create_credential_and_login(credential_data)
  cl.respond_to?(:core_id) ? cl.core_id : nil
end

require 'securerandom'

myworkspace_id = framework.db.default_workspace.id
module_fullname = 'exploit/multi/http/gitlab_file_read_rce'

def rand_crypto(char_len)
    SecureRandom.hex(char_len)
end

(1..2500).each do |i|
    $stderr.puts "#{i}"
    report_creds(
        "user_#{i}_without_realm",
        "aad3b435b51404eeaad3b435b51404ee:#{rand_crypto(16)}",
        type: :ntlm_hash, module_fullname: module_fullname, myworkspace_id: myworkspace_id
    )
    report_creds(
        "user#{i}_with_realm", "aad3b435b51404eeaad3b435b51404ee:#{rand_crypto(16)}",
        type: :ntlm_hash, module_fullname: module_fullname, myworkspace_id: myworkspace_id,
        realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN, realm_value: 'example.local'
    )
    krb_key = {
        enctype: Rex::Proto::Kerberos::Crypto::Encryption::AES256,
        salt: "DEMO.LOCALuser_#{i}_with_krbkey".b,
        key: rand_crypto(64)
    }
    report_creds(
        "user_#{i}_with_realm", Metasploit::Credential::KrbEncKey.build_data(**krb_key),
        type: :krb_enc_key, module_fullname: module_fullname, myworkspace_id: myworkspace_id,
        realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN, realm_value: 'demo.local'
    )

   ssh_key = OpenSSL::PKey::RSA.generate(1024).to_s
   report_creds(
       "user_#{i}_with_realm", ssh_key,
       type: :ssh_key, module_fullname: module_fullname, myworkspace_id: myworkspace_id,
       realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN, realm_value: 'demo.local',
       rport: 22,
       service_name: 'ssh'
   )
end
</ruby>

… entries are already present when attempting a rollback
@adfoster-r7 adfoster-r7 marked this pull request as ready for review April 11, 2023 10:46
@adfoster-r7 adfoster-r7 merged commit a00c341 into rapid7:master Apr 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants