Skip to content

Conversation

jheysel-r7
Copy link
Contributor

@jheysel-r7 jheysel-r7 commented Sep 23, 2025

Originally when the RUN_REGISTRY_CHECKS datastore option was added to the ldap_esc_vulnerable_cert_finder module, the module would only check the registry of the Domain Controller defined in the rhost value. This provides incorrect results in cases where the Certificate Authority is installed on a server separate from the Domain Controller.

If the two systems are separated, the Domain Controller will hold:

  • Certificate templates
  • StrongCertificateBindingEnforcement registry value
  • CertificateMappingMethods registry value

And the Certificate Authority will hold:

  • DisableExtensionList registry value
  • EditFlags registry value

If the CA is installed on the DC then all the info needed resides on the DC. To solve the issue when they are separate the module now requires the user to define the datastore option CA ( just like the icpr_cert module) when RUN_REGISTRY_CHECKS is set to true. The module reaches out to the DC via LDAP and a new adds_get_ca_servers method which returns a list of containing the name and dnshostname of all the CA servers connected to the DC. Then the dnshostname of all the CA's are resolved and then using the same credentials, a WMI connection is established and the registry keys are pulled from each CA.

Verification

Installed on the same system:

  • Snapshot your DC
  • Setup a CA on the same machine as a DC.
  • Start msfconsole
  • use auxiliary/gather/ldap_esc_vulnerable_cert_finder
  • set RUN_REGISTRY_CHECKS true
  • set CA msflab-DC-CA
  • Verify all the registry keys are collected as expected

Installed separately:

  • Revert the snapshot
  • Setup a separate Windows Server
  • Join that server to the domain
  • Setup an Enterprise Certificate Authority on the server (Stand Alone CA's need not be tested as they don't use certificate templates)
  • Start msfconsole
  • use auxiliary/gather/ldap_esc_vulnerable_cert_finder
  • set RUN_REGISTRY_CHECKS true
  • set CA msflab-CA-CA
  • Verify all the registry keys are collected as expected

Example output when testing a CA installed on a machine separate from the DC:

[+] Found CA: kerberos-CA-CA (CA.kerberos.issue)
[+] Registry values:
[+]   certificate_mapping_methods=4
[+]   strong_certificate_binding_enforcement=0
[+]   disable_extension_list={1.3.6.1.4.1.311.25.2}
[+]   edit_flags=1376590

@msutovsky-r7 msutovsky-r7 self-assigned this Sep 29, 2025

@registry_values
rescue StandardError => e
vprint_warning("Failed to query registry values: #{e.message}")
Copy link
Contributor

Choose a reason for hiding this comment

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

While testing, I noticed that for some reason, whenever there's failure anywhere in this function, it will return the error message and the following code might loudly (for example, when DNS query fails):

[-] Auxiliary failed: TypeError no implicit conversion of Symbol into Integer
[-] Call stack:
[-]   /home/ms/git/metasploit-framework/modules/auxiliary/gather/ldap_esc_vulnerable_cert_finder.rb:1082:in `[]'
[-]   /home/ms/git/metasploit-framework/modules/auxiliary/gather/ldap_esc_vulnerable_cert_finder.rb:1082:in `block in run'
[-]   /home/ms/.rvm/gems/ruby-3.2.5/gems/net-ldap-0.19.0/lib/net/ldap.rb:646:in `block in open'
[-]   /home/ms/.rvm/gems/ruby-3.2.5/gems/net-ldap-0.19.0/lib/net/ldap.rb:718:in `block in open'
[-]   /home/ms/.rvm/gems/ruby-3.2.5/gems/net-ldap-0.19.0/lib/net/ldap/instrumentation.rb:19:in `instrument'
[-]   /home/ms/.rvm/gems/ruby-3.2.5/gems/net-ldap-0.19.0/lib/net/ldap.rb:713:in `open'
[-]   /home/ms/.rvm/gems/ruby-3.2.5/gems/net-ldap-0.19.0/lib/net/ldap.rb:646:in `open'
[-]   /home/ms/git/metasploit-framework/lib/msf/core/exploit/remote/ldap.rb:147:in `ldap_open'
[-]   /home/ms/git/metasploit-framework/lib/msf/core/optional_session/ldap.rb:53:in `ldap_connect'
[-]   /home/ms/git/metasploit-framework/modules/auxiliary/gather/ldap_esc_vulnerable_cert_finder.rb:1048:in `run'
[*] Auxiliary module execution completed
Suggested change
vprint_warning("Failed to query registry values: #{e.message}")
vprint_warning("Failed to query registry values: #{e.message}")
return

Choose a reason for hiding this comment

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

same error i am also facing i think it might be error in modules some is missing by our side let me try it again and i will help you out here brother.

end
end

ca_entry = ca_servers.find { |ca| ca[:name].casecmp?(datastore['CA']) }
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to have possibility to query all CAs? I'm not sure how many there can be and if it makes sense, but I'm thinking if it is possible, some users might wanna have all information rather than individually select each CA.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was also wondering about this. Based on my understanding from the second paragraph of your PR description, it seems like we should be checking each CA server so we can identify when one or more may be vulnerable. I'd probably want to see the certificate template not marked with the technique if all CA servers don't have the applicable configuration. If one or more do though, marking the template as vulnerable then maybe noting the server's with the necessary misconfiguration in the notes would be helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That absolutely makes sense. Thanks for bringing that up. I think I've made the necessary changes and have documented the testing in this comment here: #20563 (comment)
Please let me know if you have any comments or suggestions

@jheysel-r7 jheysel-r7 force-pushed the fix/mod/ldap_esc/ca_reg_values branch from a076a04 to 74d229e Compare October 3, 2025 02:14
@jheysel-r7
Copy link
Contributor Author

jheysel-r7 commented Oct 3, 2025

ldap_esc_vulnerable_cert_finder

Now each CA in the domain is queried for their CertSvc registry keys (disable_extension_list and edit_flags) which determine if the CA will issue certificates vulnerable to ESC16.

The other two registry keys that affect template exploitability (StrongCertificateBindingEnforcement, CertificateMappingMethods) only reside on the Domain Controller.

The certificate template named: ESC9_16_Parent_CA is only issued by the parent CA kerberos-CA-CA which doesn't contain the edit flag necessary to be vulnerable to ECS16:

[+] Template: ESC9_16_Parent_CA
[*]   Distinguished Name: CN=ESC9_16_Parent_CA,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=kerberos,DC=issue
[*]   Manager Approval: Disabled
[*]   Required Signatures: 0
[+]   Vulnerable to: ESC4, ESC10
[*]   Permissions: READ, WRITE, ENROLL
[*]   Notes:
[*]     * ESC4: The account: Administrator has edit permissions over the template ESC9_16_Parent_CA.
[*]     * ESC10: The account: Administrator has edit permission over the accounts: Administrator, msfuser, krbtgt, user1, user2, user3 which have enrollment rights for this template. Registry values: StrongCertificateBindingEnforcement=2, CertificateMappingMethods=4.
[*]   Certificate Template Write-Enabled SIDs:
[*]   Certificate Template Enrollment SIDs:
[*]     * S-1-5-21-2324486357-3075865580-3606784161-512 (Domain Admins)
[*]     * S-1-5-21-2324486357-3075865580-3606784161-513 (Domain Users)
[*]     * S-1-5-21-2324486357-3075865580-3606784161-519 (Enterprise Admins)
[*]     * S-1-5-21-2324486357-3075865580-3606784161-1603 (user2)
[+]   Issuing CA: kerberos-CA-CA (ca.kerberos.issue)
[*]     Enrollment SIDs:
[*]       * S-1-5-11 (Authenticated Users)
[*]       * S-1-5-21-2324486357-3075865580-3606784161-519 (Enterprise Admins)

The certificate template name ESC9_16_Subordinate_CA is only issued by the subordinate CA: kerberos-CA2-CA which does have the edit flag necessary to be vulnerable to ESC16:

[+] Template: ESC9_16_Subordinate_CA
[*]   Distinguished Name: CN=ESC9_16_Subordinate_CA,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=kerberos,DC=issue
[*]   Manager Approval: Disabled
[*]   Required Signatures: 0
[+]   Vulnerable to: ESC4, ESC10, ESC16
[*]   Permissions: READ, WRITE, ENROLL
[*]   Notes:
[*]     * ESC4: The account: Administrator has edit permissions over the template ESC9_16_Subordinate_CA.
[*]     * ESC10: The account: Administrator has edit permission over the accounts: Administrator, msfuser, krbtgt, user1, user2, user3 which have enrollment rights for this template. Registry values: StrongCertificateBindingEnforcement=2, CertificateMappingMethods=4.
[*]     * ESC16: Template is vulnerable due to the active policy EditFlags having: EDITF_ATTRIBUTESUBJECTALTNAME2 set (which is essentially ESC6) on the Certificate Authority: kerberos-CA2-CA. Also the CA having 1.3.6.1.4.1.311.25.2 defined in it's disabled extension list
[*]   Certificate Template Write-Enabled SIDs:
[*]   Certificate Template Enrollment SIDs:
[*]     * S-1-5-21-2324486357-3075865580-3606784161-512 (Domain Admins)
[*]     * S-1-5-21-2324486357-3075865580-3606784161-513 (Domain Users)
[*]     * S-1-5-21-2324486357-3075865580-3606784161-519 (Enterprise Admins)
[*]     * S-1-5-21-2324486357-3075865580-3606784161-1603 (user2)
[+]   Issuing CA: kerberos-CA2-CA (ca2.kerberos.issue)
[*]     Enrollment SIDs:
[*]       * S-1-5-11 (Authenticated Users)
[*]       * S-1-5-21-2324486357-3075865580-3606784161-519 (Enterprise Admins)

The notes section indicates which CA will issue the misconfigured template.
* ESC16: Template is vulnerable due to the active policy EditFlags having: EDITF_ATTRIBUTESUBJECTALTNAME2 set (which is essentially ESC6) on the Certificate Authority: kerberos-CA2-CA. Also the CA having 1.3.6.1.4.1.311.25.2 defined in it's disabled extension list

esc_update_ldap_object

The esc_update_ldap_object now also properly accounts for the possibility of their being multiple separate Certificate Authorities by resolving the CA IP and updating the LDAP objects on the domain controller before requesting the certificate from the CA.

msf auxiliary(admin/dcerpc/esc_update_ldap_object) > run
[*] Running module against 172.16.199.200
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Current value of user2's userPrincipalName: user2
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to Administrator...
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
[+] The operation completed successfully!
[*] 172.16.199.200:445 - Discovering base DN automatically
[+] 172.16.199.200:445 - The requested certificate was issued.
[*] 172.16.199.200:445 - Certificate Policies:
[*] 172.16.199.200:445 - Certificate UPN: Administrator
[*] 172.16.199.200:445 - Certificate stored at: /home/msfuser/.msf4/loot/20251002171832_default_172.16.199.203_windows.ad.cs_468977.pfx
[*] 172.16.199.200:445 - Reverting ldap object
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Current value of user2's userPrincipalName: Administrator
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to user2...
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to user2
[+] The operation completed successfully!
[*] Auxiliary module execution completed

Note the @print_prefix variable is slightly misleading in the lines surrounding:

[+] 172.16.199.200:445 - The requested certificate was issued.

It should read:

[+] 172.16.199.203:445 - The requested certificate was issued.

As it's contacting the CA not the DC for the cert. I think I might change that.

Ensuring everything works when DC and CA on same server:

ldap_vulnerable_cert_finder:

[+] Found CA: kerberos-DC2-CA (dc2.kerberos.issue)
[+] certificate_mapping_methods: 0
[+] strong_certificate_binding_enforcement: 0
[+] kerberos-DC2-CA: {:disable_extension_list=>"{1.3.6.1.4.1.311.25.2}", :edit_flags=>4522318}

Note currently the Scenario 1 and 2 for esc16 are separated by and elsif so we only report 1 in the event both are true to avoid having duplicate ESC16 entries in the notes. That could be fixed.

[+] Template: ESC9-Template
[*]   Distinguished Name: CN=ESC9-Template,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=kerberos,DC=issue
[*]   Manager Approval: Disabled
[*]   Required Signatures: 0
[+]   Vulnerable to: ESC4, ESC9, ESC16
[*]   Permissions: FULL CONTROL
[*]   Notes:
[*]     * ESC4: The account: Administrator has edit permissions over the template ESC9-Template.
[*]     * ESC9: The account: Administrator has edit permission over the accounts: user1, user3, user2 which have enrollment rights for this template. Registry value: StrongCertificateBindingEnforcement=0.
[*]     * ESC16: Template is vulnerable due StrongCertificateBindingEnforcement = 0 and the Certificate Authority: kerberos-DC2-CA having 1.3.6.1.4.1.311.25.2 defined in it's disabled extension list

esc_update_ldap_object working with DC and CA on the same server:

msf auxiliary(admin/dcerpc/esc_update_ldap_object) > run
[*] Running module against 172.16.199.200
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Current value of user3's userPrincipalName: [email protected]
[*] Attempting to update userPrincipalName for CN=user3,CN=Users,DC=kerberos,DC=issue to Administrator...
[+] Successfully updated CN=user3,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
[+] The operation completed successfully!
[*] 172.16.199.200:445 - Discovering base DN automatically
[+] 172.16.199.200:445 - The requested certificate was issued.
[*] 172.16.199.200:445 - Certificate Policies:
[*] 172.16.199.200:445 - Certificate UPN: Administrator
[*] 172.16.199.200:445 - Certificate stored at: /home/msfuser/.msf4/loot/20251002174048_default_172.16.199.200_windows.ad.cs_368130.pfx
[*] 172.16.199.200:445 - Reverting ldap object
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
[*] Current value of user3's userPrincipalName: Administrator
[*] Attempting to update userPrincipalName for CN=user3,CN=Users,DC=kerberos,DC=issue to [email protected]...
[+] Successfully updated CN=user3,CN=Users,DC=kerberos,DC=issue's userPrincipalName to [email protected]
[+] The operation completed successfully!
[*] Auxiliary module execution completed

@github-project-automation github-project-automation bot moved this from Todo to In Progress in Metasploit Kanban Oct 3, 2025
@msutovsky-r7 msutovsky-r7 merged commit 1491ede into rapid7:master Oct 3, 2025
50 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Metasploit Kanban Oct 3, 2025
@msutovsky-r7
Copy link
Contributor

Release Notes

The ldap_esc_vulnerable_cert_finder now checks the CAs and DC, when running registry check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug rn-fix release notes fix
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

5 participants