AD CS Web Enrollment. Relay me completely

Lately, one can often hear that attacks on ADCS have become something trivial: after the publication of an informative article Certified Pre-Owned from Specter Ops almost every pentester knows what ESC1 and ESC8 are, and seeing in Cert Publishers computers, immediately runs there. However, Web Enrollment, in addition to attacks, can chain vulnerabilities and serve as an excellent option for Initial Access. Let's figure out how to use it, using our infrastructure pentest project as an example.

Sometimes it happens that work needs to be done with a “gray box”. This happened to us when we came to the customer's office. But what pentester doesn't do work with a “black box”, even when there is a fresh UZ, right? After all the default preparations and checks, we find in Responder that some computer UZ is just asking to visit us.

Computer ultrasound detection

Computer ultrasound detection

When attempting to resolve a name via network protocols, Windows uses the following priority:

1. DNS

2. LLMNR

3.NBNS

By default, hosts send requests via DNS to find a web server where the proxy configuration file is stored. This happens via the WPAD (Web Proxy Auto Discovery protocol) protocol — it often appears in sent requests. If the DNS server responds that it doesn’t know about it, the host will go down to LLMNR and NBNS, and then Responder will lend a helping hand to each request, introducing itself as the required server, and in return will ask to pass authentication. You can make life more difficult for pentesters by creating so-called wildcard records that will satisfy all requests via DNS, and they will not go down to LLMNR and NBNS.

We caught a Net-NTLMv2 hashed value of the machine UZ password – a banal situation, let's remember it and move on.

One of the vectors for obtaining UZ is the use of a utility pre2kthe mechanism of which painted guys from TrustedSec. It allows you to request a TGT ticket for any machine account in the domain, and if it was created with the flag Assign this computer account as a pre-Windows 2000 computerthen her lowercase password will be equal to her name. We scan the scope available to us, enter the found hosts into a file and get a valid domain ID.

Obtaining a UZ in a domain

Obtaining a domain certificate

Authentication of such UZs is carried out using the Kerberos protocol. And bloodhoundand Certipy support it. We collect information about the domain and find that all domain administrator accounts are in the Protected Users group. We go to the certification center and find vulnerabilities ESC1 and ESC8. If you encounter a huge domain, I recommend using rusthoundwhich collects information about the domain much faster than bloodhound and can sometimes please with interesting finds (whoever solved our stand on the inside at OFFZONE 2024 knows what I'm talking about).

Example of the rusthound utility in operation

Example of the rusthound utility in operation

It is useful to access AD CS not only from computer AUs, but also from user AUs, since the rights to read templates may be different and the conclusions may not match. Also, if we consider the Certipy utility and similar ones, I rarely use the vulnerable flag, since:

1) you can miss important information due to the rights of the user account from which the request is made;

2) learning patterns manually is not as difficult as it seems.

For example, if you are looking for ESC1, you only need to pay attention to three fields: Client Authentication, Enrollee Supplies Subject, and Enrollment Rights. If the first two items are set to true, and the third value specifies a user or group, then exploitation is possible from their account. And if you are looking for ESC4, then look at the attributes Write Owner Principals, Write Dacl Principals, Write Property Principals, etc.

Having issued ourselves a certificate during the standard attack on ESC8 at the authentication stage, we encounter an error.

Error during authentication

Error during authentication

On the official Microsoft website in documentation we find its description.

KDC_ERR_PADATA_TYPE_NOSUPP:

Smart card logon is being attempted and the proper certificate cannot be located. This problem can happen because the wrong certification authority (CA) is being queried or the proper CA cannot be contacted in order to get Domain Controller or Domain Controller Authentication certificates for the domain controller.

It can also happen when a domain controller doesn't have a certificate installed for smart cards (Domain Controller or Domain Controller Authentication templates).

It becomes clear that PKINIT not supported on domain controller. Yes great articledescribing the entire path from receiving an error during authentication via PKINIT and further using the certificate via Schannel and LDAP via TLS. As you may have guessed, we are talking about the PassTheCert attack. Initially tool The guys from AlmondOffSec created it for implementation, but Ly4k quickly adapted its functionality for Certipy.

You can get an ldap shell from the domain controller's account, change your own password, and do DCSync. But let's do something new and get an ldap shell from the domain administrator to chain ESC1 + ESC8. This is appropriate when templates with the DomainControllers enroll group are disabled. To exploit the ESC1 vulnerability in this example, you need a computer account, which, by the way, we already have.

Let's recall the computer CIs that knocked on our Responder. Standard exploitation of ESC8 occurs when forcing authentication of the machine CI of the domain controller via relay to the Web Enrollment service of the certification center with the indication of the template we need. But we also know that to exploit ESC1, we need a computer CI, we know the name of the template, we know who is in the domain administrators group. What prevents us from “setting” any computer CI on Web Enrollment with the indication of the required certificate and issuing it with the required alternative name? That's right, nothing!

We launch ntlmrelayx, followed by Responder, and immediately catch successful authentication and issuance of the certificate we need.

Authentication from a computer-based UZ to the Web Enrollment service

Authentication from a computer-based UZ to the Web Enrollment service

Certificate issue

Certificate issue

Certipy kindly informs you at the end of the output that the certificate with the required alternative name has been successfully issued.

Successful operation of ESC8 + ESC1

Successful operation of ESC8 + ESC1

Of course, you can get ldap-shell via Certipy, but let's use the original Python version of passthecert as a sign of respect to the creator, for which Ly4k also added the functionality of parsing a certificate from the .pfx format to .crt and .key. And to solve any problems, it is recommended to be able to use several tools, since any of them may not work at the most crucial moment (tested).

Extracting the certificate and private key

Extracting the certificate and private key

Next, we get a domain administrator session and add the account provided to us for work to the privileged group. The guys from the customer's side worked hard and even wrote our full name in the description, and when we successfully executed the command, my friend and colleague Vova smiled broadly at the output on the screen.

Vova really can

Vova really can

After this project, I got the idea that if Web Enrollment is enabled when we don't have an AU, then we can take users or computer AUs from Responder (IPv6, coerce, etc.) and try to spray certificate templates directly through it. A kind of TEMPLATE SPRAY. This is appropriate when you managed to collect information about objects in the domain (relay on ldap, for example), but you can't get an AU.

Let's test this option in practice on our stand. Let's create a vulnerable certification center with Web Enrollment and a vulnerable template for ESC1. Let's call the template Netrunner.

Creating a vulnerable certificate template

Creating a vulnerable certificate template

What is Web Enrollment and what is its role?

In Microsoft documentation written:

The Certification Authority (CA) Web Enrollment role service provides a set of web pages that allow users to perform certificate tasks. For example, requesting and renewing certificates, retrieving certificate revocations lists (CRLs) and enrolling for smart card certificates.

It's all obvious: requesting and renewing certificates, registering smart card certificates, etc. That's exactly what we do!

Next, let's filter the json file with templates.

cat 20240830202029_nightcity-corp_templates.json | grep -oP '"name":\s*"\K[^@]*' > templates.txt
Getting a list of templates

Getting a list of templates

Now all that's left for us to do is write a short Python script and sit back in your chair, proudly watching as the certificates start flying in and add logic that will run ntlmrelayx.py for 15 seconds with a specific template. And then wait for authentication, make an attempt to issue a certificate, kill your process, because ntlmrelayx.py itself does not close, and after a short time start working again, but with a different template, etc.

A simple POC here:

import time
import os
import signal

def run_command(command):
    process = subprocess.Popen(command, shell=False)
    
    try:
        time.sleep(15)
    finally:
        try:
            os.kill(process.pid, 0)  
            print(f"Killing process {process.pid}")
            os.kill(process.pid, signal.SIGKILL)
        except OSError:
            print(f"Process {process.pid} does not exist or has already been terminated.")
        time.sleep(3)

def main(filename):
    with open(filename, 'r') as file:
        for line in file:
            command = [
                "ntlmrelayx.py",
                "-t", "http://WEB_ENROLLMENT_IP/certsrv/certfnsh.asp",
                "-smb2support", "--adcs", "--template", line.strip(),
                "--altname", "administrator"
            ]
            if command:  
                print(f"Executing command: {' '.join(command)}")
                run_command(command)

if __name__ == "__main__":
    main('templates.txt')

Emulate the frontal place of pentesters on departures to the customer working accounting movement in Responder and we observe how, in addition to the overwhelming majority of errors when issuing certificates, we still receive several successful conclusions with the issuance of a certificate, including with ESC1.

Example of an unsuccessful attempt to issue a certificate

Example of an unsuccessful attempt to issue a certificate

Successful certificate issue

Successful certificate issue

When analyzing the few templates that users can use to obtain certificates in the domain, it was found that there are default templates. And using them, you can get a certificate for a client if it is in the group they need. Among them are User for users and Machine for computer accounts. Both are of great value to us! They are enabled by default, and both have the True flag on Client Authentication. This means that we will be able to authenticate if we request a certificate using such a template.

It turns out that, knowing that these templates exist, we do not need to spray and do not even need information about the domain, but simply “bring” the desired client to the correct template in Web Enrollment – this is how we get a certificate. If you look at the screenshot below, where we take a domain through a template spray and the ESC1 vulnerability, the most important part is at the top. It shows how we get a valid UZ in the domain through the User template, which does not have any vulnerabilities. By default, ntlmrelayx and certipy use these templates for a relay attack:

adcsoptions.add_argument('--template', action='store', metavar="TEMPLATE", required=False, 
help='AD CS template. Defaults to Machine or User whether relayed account name ends with $. 
Relaying a DC should require specifying DomainController')
Successful domain takeover via certificate template spray

Successful domain takeover via certificate template spray

Let's check the option for computer ultrasound on the Machine template and strip one of the hosts.

Example of obtaining a certificate from a computer UZ

Example of obtaining a certificate from a computer UZ

Next, we do the same as before, only now, as with the User template, we do not claim increased privileges.

Obtaining a valid UZ in a domain

Obtaining a valid UZ in a domain

Everything worked out! And a valid computer ID is a potential Silver Ticket, etc. The advantage of the considered vector is that it is implemented by a “black box” and you do not need any information about the domain or certificates – it is enough to find the AD CS server, and then, if enabled, Web Enrollment. This will be useful if there is no ID of users or computers, but there is llmnr/nbns/mdns spoofing or another way to force the domain ID to pass authentication for an NTLM relay attack. You can also collect hosts on port 445 and run them PetitPotam without authentication. The chances of being able to shear at least one host are high, in my experience.

There is an opinion that implementing HTTPS helps, but I would not treat it as a panacea, and would recommend making Web Enrollment available from certain hosts or for a certain group of users, or disabling it. Below is a screenshot from one of the projects, proving that https is not always a solution.

Conducting an ESC8 attack over HTTPS

Conducting an ESC8 attack over HTTPS

In this article, we found out that Web Enrollment can not only issue domain controller certificates through standard ESC8 exploitation, but also chain vulnerabilities and help with initial access. Hope you learned something new! Until next time!

Victor Zvarykin

Information Security Consultant, Jet Infosystems

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *