Skip to content

Commit d66e93a

Browse files
authored
Merge pull request rapid7#20658 from jheysel-r7/feat/mod/cert_details_update
Add Updates to LDAP ESC Vulnerable Cert Finder
2 parents c77578d + 0e2af23 commit d66e93a

File tree

4 files changed

+193
-32
lines changed

4 files changed

+193
-32
lines changed

docs/metasploit-framework.wiki/ad-certificates/Attacking-AD-CS-ESC-Vulnerabilities.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1580,7 +1580,7 @@ msf6 auxiliary(admin/kerberos/get_ticket) > get_hash rhost=172.16.199.200 cert_f
15801580
[*] Auxiliary module execution completed
15811581
```
15821582

1583-
#### ESC16 Scenario 2
1583+
## ESC16 Scenario 2
15841584
If domain controllers are in Full Enforcement mode (`StrongCertificateBindingEnforcement` == 2), ESC16 alone would normally
15851585
prevent authentication using certificates that lack the required SID extension. However, if the CA is also vulnerable
15861586
to ESC6, which is defined as: `EDITF_ATTRIBUTESUBJECTALTNAME2` flag is set under it's `EditFlags` registry key, located here:

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

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -420,14 +420,39 @@ def build_on_behalf_of(csr:, on_behalf_of:, cert:, key:, algorithm: 'SHA256')
420420
# @param [OpenSSL::X509::Certificate] cert
421421
# @return [Array<Rex::Proto::CryptoAsn1::ObjectId>] The policy OIDs if any were found.
422422
def get_cert_policy_oids(cert)
423-
ext = cert.extensions.find { |e| e.oid == 'ms-app-policies' }
424-
return [] unless ext
423+
all_oids = []
425424

426-
cert_policies = Rex::Proto::CryptoAsn1::X509::CertificatePolicies.parse(ext.value_der)
427-
cert_policies.value.map do |policy_info|
428-
oid_string = policy_info[:policyIdentifier].value
429-
Rex::Proto::CryptoAsn1::OIDs.value(oid_string) || Rex::Proto::CryptoAsn1::ObjectId.new(oid_string)
425+
# ms-app-policies (CertificatePolicies) - existing handling
426+
if (ext = cert.extensions.find { |e| e.oid == 'ms-app-policies' })
427+
begin
428+
cert_policies = Rex::Proto::CryptoAsn1::X509::CertificatePolicies.parse(ext.value_der)
429+
cert_policies.value.each do |policy_info|
430+
oid_string = policy_info[:policyIdentifier].value
431+
all_oids << (Rex::Proto::CryptoAsn1::OIDs.value(oid_string) || Rex::Proto::CryptoAsn1::ObjectId.new(oid_string))
432+
end
433+
rescue StandardError => e
434+
vprint_error("Failed to parse ms-app-policies from certificate with subject:\"#{cert.subject.to_s}\" and issuer:\"#{cert.issuer.to_s}\". #{e.class}: #{e.message}")
435+
end
430436
end
437+
438+
# extendedKeyUsage - SEQUENCE OF OBJECT IDENTIFIER
439+
if (eku_ext = cert.extensions.find { |e| e.oid == 'extendedKeyUsage' })
440+
begin
441+
asn1 = OpenSSL::ASN1.decode(eku_ext.value_der)
442+
# asn1 should be a Sequence whose children are OBJECT IDENTIFIER nodes
443+
if asn1.is_a?(OpenSSL::ASN1::Sequence)
444+
asn1.value.each do |node|
445+
next unless node.is_a?(OpenSSL::ASN1::ObjectId)
446+
oid_string = node.value
447+
all_oids << (Rex::Proto::CryptoAsn1::OIDs.value(oid_string) || Rex::Proto::CryptoAsn1::ObjectId.new(oid_string))
448+
end
449+
end
450+
rescue StandardError => e
451+
vprint_error("Failed to parse extendedKeyUsage from certificate with subject:\"#{cert.subject.to_s}\" and issuer:\"#{cert.issuer.to_s}\". #{e.class}: #{e.message}")
452+
end
453+
end
454+
455+
all_oids
431456
end
432457

433458

modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ def initialize(info = {})
6767
OptEnum.new('UPDATE_LDAP_OBJECT', [ true, 'Either userPrincipalName or dNSHostName, Updates the necessary object of a specific user before requesting the cert.', 'userPrincipalName', %w[userPrincipalName dNSHostName] ]),
6868
OptString.new('UPDATE_LDAP_OBJECT_VALUE', [ true, 'The account name you wish to impersonate', 'Administrator']),
6969
OptString.new('TARGET_USERNAME', [true, 'The username of the target LDAP object (the victim account).'], aliases: ['SMBUser']),
70-
OptString.new('TARGET_PASSWORD', [false, 'The password of the target LDAP object (the victim account). If left blank, Shadow Credentials will be used to authenticate as the TARGET_USERNAME'], aliases: ['SMBPass'])
70+
OptString.new('TARGET_PASSWORD', [false, 'The password of the target LDAP object (the victim account). If left blank, Shadow Credentials will be used to authenticate as the TARGET_USERNAME'], aliases: ['SMBPass']),
71+
OptString.new('CertificateAuthorityRhost', [false, 'The IP Address of the CA. The module will attempt to resolve this via DNS if this is not set'])
7172
])
7273

7374
register_advanced_options(
@@ -216,7 +217,7 @@ def action_request_cert
216217
@device_id, cert_path = call_shadow_credentials_module('add')
217218
smbpass = automate_get_hash(cert_path, datastore['TARGET_USERNAME'], datastore['LDAPDomain'], datastore['RHOSTS'])
218219
end
219-
ca_ip = resolve_ca_ip
220+
ca_ip = datastore['CertificateAuthorityRhost'].present? ? datastore['CertificateAuthorityRhost'] : resolve_ca_ip
220221
with_ipc_tree do |opts|
221222
datastore['SMBUser'] = datastore['TARGET_USERNAME']
222223
datastore['SMBPass'] = smbpass

0 commit comments

Comments
 (0)