Trying some SQL injection attacks in practice, in my opinion, is the best way to understand that.
Just searching online, I found that setting up a laboratory can be very long and tricky, especially for a beginner, and it often has the consequence of causing the student to give up.
What I did to make the process simpler, is to build a terribly coded web application in python named SDWA (Sitting Duck Vulnerable App).
It’s just in an embryonic phase, but we can use it to learn:
- SQL injection
- Command injection
I have already talked about the XSS part in this article, so it’s time to focus on SQL injection and put our knowledge into practice.
If you have no idea what it is, I suggest you take your time and read this introduction.
Here is the list of all the articles about SQL injection for quick navigation:
In-Band SQL injection
- SQL Injection: What You Need to Know
- Learn SQL injection in practice by hacking vulnerable application!
- How To Hack With SQL Injection Attacks! DVWA low security
- Hack With SQL Injection Attacks! DVWA medium security
- Hack With SQL Injection Attacks! DVWA high security
Blind SQL injection
- Blind SQL injection: How To Hack DVWA With Python (Low Security)
- Blind SQL Injection: How To Hack DVWA With Python (Medium Security)
- Blind SQL Injection: How To Hack DVWA With Python (High Security)
As I said in the introduction, I tried to make the installation process as painless as possible, anyway, it requires some simple steps:
- Having an available virtual machine (this is optional but highly recommended), I suggest Kali Linux, If you don’t have one, try to follow this tutorial. Another option could be to download a prebuilt Virtualbox image from here.
- Clone the repository from GitHub at this link by typing the following command from the terminal into your chosen directory:
git clone https://github.com/StackZeroSec/sdwa
- Install all the dependencies with this command:
pip install -r requirements.txt
- Create the database:
- Run the application by typing this on the terminal:
At this point, open your browser (Firefox in case you followed this tutorial) and type this address in the search URL bar:
If you did everything correctly, you should see this minimal screen:
It means that the application is working and we are ready to understand in practice how a real SQL injection works.
So let’s get to the heart of the topic!
The whole application is extremely simple and has the only purpose to let a better understanding of how SQLi works. But this first phase is ridiculously easy: no more than a light warmup.
After clicking “Try it” from the homepage, the link takes us to a login page.
It’s easy to guess that, without any hint about username and password, the goal is to get inside using an exploit.
The first thing we are going to try is to insert a single quote in one of two fields, and the result is an internal error. It can be a hint of the presence of the SQLi vulnerability.
At this point, we can try what we have learned in the previous article about SQL injection and put it into practice.
So, we can imagine that the server is running a query similar to this one:
SELECT * FROM USERTABLE WHERE username='your_user_input' AND password='your_password_input'
Obviously, we don’t know anything about the database, but we can imagine that if the query gives a result, we are in, so let’s try to make it always true.
The idea is to manipulate our input in order to have something like this:
SELECT * FROM USERTABLE WHERE username='random_input' OR 1=1 -- 'AND password='random_password'
In this case, a double dash indicates that what follows is a comment, anyway the syntax depends on the DBMS, and in case of an error, we should try to guess what we are targeting.
So let’s try to log in by typing this (the password is just a random char):
And it works! The result is the following page:
As the alert says, we are inside as normal users, but what we’re going to do in the next paragraph is to exploit a new vulnerability and get inside as administrators!
SQL injection in practice: how to use the UNION clause
In this phase we can read an output, so seems easy to gather information about the database.
At this point appear to be clear that our goal is to access as an administrator.
Just at a glance, we can guess that the username and the password are in a table other than the product table. So what we need to know is:
- The products table’s number of columns (The union can be done just between the same number of columns)
- The DBMS (MySql, SQLite, PostgreSQL, Oracle, Microsoft…)
- The name of the table containing the username and the password
- The name of the users’ table fields
How many columns are involved?
In order to accomplish the first task we have mainly two techniques:
- ORDER BY: We can try a series of ORDER BY until an error occurs, and it is the signal that this index is out of range:
' ORDER BY 1-- ' ORDER BY 2-- ' ORDER BY 3-- ...
- UNION SELECT: This other technique involves sending a UNION SELECT with an increasing number of NULL values (even a constant value like the number 1 is ok). In this way, we don’t get an error only when the number of “NULL” values is equal to the number of the table’s columns
' UNION SELECT NULL-- ' UNION SELECT NULL,NULL-- ' UNION SELECT NULL,NULL,NULL-- ...
Just by trying the “ORDER BY” method, we get an error even with index 1, so that’s probably not the right way.
Since the “UNION SELECT” technique seems to be more promising, let’s try that one
This technique did not disappoint our expectations and the number of columns is four, we can proceed with the next step.
What is the target DBMS?
What can make our life easier is knowing what database we are targeting.
There are many ways to do that, in this case, we are taking advantage of the different syntax for the query that returns the version.
Here is a list of the different syntaxes:
SELECT version FROM v$instance
Knowing that the table containing products has four columns, we can craft the query in order to guess what is the DBMS.
The input should be something like this:
' UNION SELECT 1, 1, 1, version_request --
The only query that doesn’t return an error is the SQLite one, so we have another valuable piece of information.
' UNION SELECT 1, 1, 1, sqlite_version() --
Getting the tables’ name
At this point of our SQL injection tour, we need to know the name of the tables, but before exploiting the application, I’ll show you how to do that with a query in SQLite.
SELECT name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%';
So in the next step, we want to show, the result in our table.
We can do that, as we know well by now, with the UNION SELECT. This will be our input:
' UNION SELECT 1, 1, 1,name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%'--
The query above will let the application prompt all the tables belonging to the application, and this will be the result.
The table we are interested in is the one with the name “users”, our investigation needs just one more step and we can get further into our process to get administrator credentials.
Getting the columns’ fields
This is the last info we need before passing it to the real attack, let’s quickly refresh the way to get the column names in SQLite3:
SELECT name FROM pragma_table_info('table_name');
By now you should easily understand how to construct our input which will be:
' UNION SELECT 1, 1, 1, name FROM pragma_table_info('users') --
That’s all, now by clicking the “Search” button, this will be the result:
SQL injection to get username and password
This is the stage where we want to get the username and password of the administrator.
As you can imagine, the work is not so much different from what we have done until now.
Let’s try our UNION SELECT query that will show us even what the field “admin” represents.
The input we need to insert in order to run our exploit is the following:
' UNION SELECT 1, username, password,admin FROM users --
The output will be:
The password looks like an md5 output, but it’s too short and even the “flask_admi” username seems to be truncated after 10 chars. So probably there is a function that doesn’t allow you to see the full password.
You will find a lot of similar problems in your career, so try to be creative. In this case, I chose to use the function
substr( string, start, length) of sqlite3 and split the password of flask_admin (I guessed that the application has truncated the last character).
There are the queries that I’ll try to run, I don’t know a priori the right order so instead of wasting time trying to infer that, I’ll try to run four separate queries and take note of the results:
' UNION SELECT 1, username, substr(password, 0, 10), admin FROM users -- ' UNION SELECT 1, username, substr(password, 10, 10), admin FROM users -- ' UNION SELECT 1, username, substr(password, 20, 10), admin FROM users -- ' UNION SELECT 1, username, substr(password, 30, 3), admin FROM users --
Here is one of the results:
By running the four queries and concatenating the results we have:
We have the hashed password, but we need to get to the last step and crack the administrator’s password.
Cracking the password
Hash is not an invertible function, anyway, you can find online a lot of precomputed lookup tables that can crack in seconds a weak password.
One of my favourites is CrackStation, so click on the link, put the hashed password you found into the text input, solve the captcha and crack the hash!
The password is found, so at this point, you just need to log out and try to log in as an administrator with these credentials:
- Username: flask_admin
- Password: password@123
Et voilà, you are logged in as administrator!
I hope you had fun and you learned something by exploiting this app. If you liked my work, follow me on Medium and visit my blog.
I want to produce more content like this and If you appreciate my work, please support me by following all my socials!