Injection occurs when an application misinterprets user input as actual code rather than a string changing the code flow and executing it. More specifically, SQL injection occurs when user-input is inputted into the SQL query string without properly sanitizing or filtering the input.

There exists 3 types of SQL Injections:

  • In-band is where the ouput of both the intended and the new query may be printed directly on the front end. It has two types:
    • Union Based we may have to specify the exact location which we can read.
    • Error based is used when we can get the PHP or SQL errors in the front-end.
  • Blind is where we may not get the ouput printed, so we may utilize SQL logic to retrieve the output character by character. It has two types:
    • Boolean based we can use SQL conditional statements to control whether the page returns any output at all.
    • Time based we use SQL conditional statements that delay the page response if the conditional returns true using the Sleep() function.
  • Out-of-band is where we may not have direct access to the output whatsoever so we may have to direct the output to a remote location and retrieve it there.

OR Injection

In typical web authentication we want the DBMS to return a true boolean value. So we need the query to return true regardless of input. We use the OR operator to do so.

Operation precedence states that the the AND operator would be evaluated before the OR operator. That means that if there is at least one true condition in the entire query along with an OR operator the entire query will evaluate to TRUE. An example condition that always return true is '1'='1'.

Comments

Written with -- for single line comments or # (note: if inputted in the URL they have to URL encoded symbol because # is a tag). /**? for in-line comments. Comments need a space after them in order to be recognized as comments.

We will use these so that remainder of a given query is now ignored as a comment.

Union Injection

Union clause is used to combine results from multiple SELECT statements, this will allow use to dump data from all across the DBMS.

A UNION statement can only operate on statements with an equal number of columns. To bypass this we can put junk data for the remaining required columns so that the total number of columns we are UNIONing with remains the same as the original query. We must ensure that the columns being with junk data matches the columns data type otherwise the query will return an error.

Here is an example of junk data to match column count:

SELECT * from products where product_id = '1' UNION SELECT username, 2 from passwords

To exploit union injection, we must first find the number of columns selected by the server.

  • ORDER BY: if we start with order by 1 and it succeeds, we continue until we reach a number that returns an error.
  • UNION injection with a different number of columns until we successfully get the results back.

We may not always get the output of the injection, for example, not every columns will be displayed back to the user. To view which columns are actually shown we can use junk data alongside @@version .

MySQL Fingerprinting

If the webserver we see in HTTP responses is Apache or Nginx, it is a good guess that the webserver is running on Linux, so the DBMS is likely MySQL. Some queries are also helpful to identify MySQL.

PayloadWhen to UseExpected OutputWrong Output
SELECT @@versionWhen we have full query outputMySQL 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 output1Error with other DBMS
SELECT SLEEP(5)Blind/No OutputDelays page response for 5 seconds and returns 0.Will not delay response with other DBMS

INFORMATION_SCHEMA

Another entry exists. INFORMATION_SCHEMA database that contains metadata about the databases and tables present on the server. To reference a table present in another DB we use the . operator. Here is an example:

SELECT * FROM my_database.users;

SCHEMATA

The table SCHEMATA in INFORMATION_SCHEMA. It contains information about all databases on the server.

SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA;

We can also find the current database with

SELECT database()

TABLES

To get a list of all tables we can use the TABLES table in the INFORMATION_SCHEMA. The important columns to find here are the TABLE_SCHEMA and the TABLE_NAME columns.

select 1,TABLE_NAME,TABLE_SCHEMA,4 from INFORMATION_SCHEMA.TABLES

COLUMNS

We need to find the column names to dump the data we are interested in finding. This can be found in the COLUMNS table in the INFORMATION_SCHEMA.

select 1,COLUMN_NAME,TABLE_NAME,TABLE_SCHEMA from INFORMATION_SCHEMA.COLUMNS

Reading Files

SQL Injection can be leveraged to perform other operations such as reading and writing files on the server.

Writing data is strictly reserved for privileged users in modern DBMSes. The DB user must have the FILE privilege to load a file’s content into a table.

-- determine current user
SELECT USER()
SELECT CURRENT_USER()
SELECT user from mysql.user

Now that we know our user, we can start looking for what privileges we have with that user.

-- test if we have suyper admin privs
SELECT super_priv FROM mysql.user
 
-- dump other privileges
SELECT 1, grantee, privilege_type, 4 FROM information_schema.user_privileges
 
-- dump privileges for a specific user
SELECT 1, grantee, privilege_type, 4 FROM information_schema.user_privileges WHERE grantee="'root'@'localhost'

Now that we know we have enough privileges to read local systems.

-- read files
SELECT LOAD_FILE('/etc/passwd');

Writing Files

To write files we require

  • User with FILE privilege
  • MySQL global secure_file_priv not enabled.
    • This is used to determine where to read/write files from. An empty values let use read files from the entire file system. If a certain directory is set we can only read from the folder specified by the variable. NULL means we cannot read/write from any directory.
-- view the global variable
SHOW VARIABLES LIKE 'secure_file_priv';
 
-- if UNION injection is only possible
SELECT variable_name, variable_value FROM information_schema.global_variables where variable_name="secure_file_priv"
  • Write access to the location we want to write to on the back-end server.

If we confirmed our user should write files to the back-end server, to write to files we use

SELECT * from users INTO OUTFILE '/tmp/credentials';

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.