In this blog post, I'll cover the basics of SQL Injection and demonstrate some examples.
What is SQL injection?
SQL injections are amongst the most common web hacking techniques. Basically, a SQL injection is exploiting a vulnerability in a web application by injecting malicious SQL commands in an input field, allowing to bypass authentication; disclose, alter or delete data.
The following answer on StackExchange explains the concept very well:
There are three types of SQL Injection attacks:
- Error based: If the application displays database errors to the user, an attacker may learn key information such as database name, version, user names etc.
- Union based: Union based SQL injection as the name suggests abuses the union operator. The basic idea is to append the data that the attacker wants to a table that is already displayed in the page.
- Blind: Even if database errors are turned off, an attacker may learn information about the database by launching blind attacks. E.g. you could send the statement "if the first name of the username is an a, wait 10 seconds. If the application takes 10 seconds to perform the query, the username starts with an a. Needless to say this should be considered as a last resort as this bruteforce approach will take much time.
Install Damn Vulnerable Web Application (DVWA)
To test SQL injections and other common web vulnerabilities such as XSS and CSRF, we can create or own safe hacking environment by setup a vulnerable web application on purpose. You can either download a VM or host it manually on an MSQL server. I strongly suggest to go for the easiest solution this time and download the ISO and boot as a livecd in virtualbox (don't add a hard disk, just let a new VM boot from the cd) as the latter gave me a lot of headaches due to incompatibilities with latest PHP versions on Kali.
When the machine is booted, just surf to the ip adress of the VM and login with admin and password. Easy.
SQL Injection example 1: ' OR 1 = 1 --
Before diving into the tools of automated SQl injections, let's try a manual SQL injection to fully grasp the concept. In the DWVA, click SQL Injection in the menu. It looks like we have have an User ID field and a submit form.
Let's try with:
' OR 1 = 1 --. We see we received a number of user data. Let's check under the hood of the SQL function what actually happened.
"SELECT first_name, last_name FROM users WHERE user_id = '$id' "
In the above example, you see very clearly that the function is giving you the first name and the last name of the given User ID via the input field.
"SELECT first_name, last_name FROM users WHERE user_id = '' OR 1 = 1 -- '"
' OR 1 = 1 -- in the input field, we alter the function by stating it should display the first name and last name of all user rows where User ID is empty
1 = 1. Since 1=1 is always true, all user rows will be given. The
-- statement tells the SQL function to ignore whatever comes after it by commenting it out. This is useful because the function my contain additional 'checks'.
I should note that
' OR 1 = 1 -- will not always work, this depends on the specific database version that is used and how the function is exactly coded. Best advice is to try multiple variations and see what works. You can find an useful cheat sheet here.
SQL Injection example 2: display tables and column fields
In the previous example, I demonstrated how an SQL example worked and what happens exactly with the function. Of course, a real life scenario we do not know the table names and column fields and how the function is coded. There are however a few tricks to gain such information.
Identify database tables via SQL injection
To find out which tables exist in the database, you can try-out the following code:
%' and 1=0 union select null, table_name from information_schema.tables #
Resulting in the following code under the hood:
"SELECT first_name, last_name FROM users WHERE user_id = '%' and 1=0 union select null, table_name from information_schema.tables #
Let's breakdown the above SQL injection. To fully comprehend what the code does, you need to add it to the already existing function:
- %' and 1=0 By adding this piece, you make the original query fail. Basically, you tell the database to select first name and last name from the users table for every row where 1=0, which is impossible. The reason we do this is because the information we need is not in the users table, but from the information_schema database which contains information over the other databases. We intentionally fail the query to the users database so we can use the information from our query to the columns table of the information_schema database instead.
- union select null The union select operator is used to combine the results of multiple SELECT statements, in this case the SELECT is on first_name, last_name so two columns. We use union select null because we need to have the same number of columns as the data we are doing an union operator on. Since we are only interested in one column - the table_name - we use null as a placeholder so we can union the data.
- table__name from information_schema.table This part states which column we want to retrieve from the information schema table. In this case, we want to have all table names.
- # This will effectively comment out the rest what is following your SQL injection and only have SQL evaluate the query until the #.
After executing the the above SQL injection, we indeed receive all tables in the information_schema database. The users table is by example a very interesting one.
Identify columns in a certain table via SQL injection
Now we know that a table users exists, we would like to find which columns exist within that database (e.g. passwords), so we can extract that information as well.
The code below is pretty straight forward, just like in the previous example, you first fail the original query, allowing you to get all column_names from the information_schema for all table_name is equal to user and ignore everything after the SQL injection.
%' and 1=0 union select null, column_name from information_schema.columns where table_name = 'users' #”
Get the information from the tables via SQL injection
Now we have identified the user and password column in the users table, we can fully exploit the web application by retrieving both.
The code below is very simple, as the orginal query on the page is already retrieving information from the users databse, we don't need to fail it first. We also do not need add union null as we want two columns, i.e. user and password
%' UNION SELECT user, password from users #
Done, we've have extracted all usernames and hashed passwords. Next step would be to bruteforce these hashes. But that's for another blogpost.