What is SQL Injection?
SQL injection is a very well known web application security vulnerability that can have serious consequences. Exploitation often leads to theft of sensitive information such as passwords, financial data, personally identifiable information, and much more. Big headlines that showcase companies with millions of sensitive records being leaked are often the result of a SQL injection vulnerability being exploited. Malicious threat actors seek this information for a variety of reasons, but most commonly, the goal is to sell this data to other criminals on dark web marketplaces. In addition to stealing sensitive data, attackers frequently find ways to alter the database information, perform denial of service attacks, access back-end infrastructure such as the underlying server hosting the database, and use stolen credentials against other services. There are many possibilities with this vulnerability and it is essential that developers take the appropriate steps to secure their applications.
How Does SQL Injection Work?
This attack is performed by an attacker sending unauthorized SQL commands in various text input fields. When you visit a website and fill out a form, you are actually sending information to a database behind the scenes of the web application. Web applications use databases to store and retrieve information. This is considered normal communication between the application and database, but what happens when someone submits malicious code that is recognized by the database into a field on the form? I think you are starting to see the idea here. Attackers can trick the database into running their own commands.
SQL Injection in the Wild
Before diving deep into the technicalities of this exploit, take a look at some of these massive breaches that hackers were able to pull off.
- RockYou (2009): An attacker gained access to RockYou’s database server by leveraging a SQL injection vulnerability. This breach resulted in 32 million user accounts being compromised. The stolen data included usernames, passwords, and email addresses, all of which were stored in plain text, making it easy for the attacker to access and use the information. RockYou’s password list is arguably the most popular dictionary of passwords used by security researchers and is even available in Kali Linux.
- Yahoo Voices (2012): Hacker group “D33Ds Company” launched a SQL injection attack against Yahoo Voices, compromising over 450,000 user accounts and leaking their usernames, email addresses, and unencrypted passwords.
- Heartland Payment Systems (2008): In 2008, the payment processing company Heartland Payment Systems suffered a massive breach that resulted in the theft of over 130 million credit card numbers. Once the attackers gained initial access to the company’s network using SQL injection, they planted advanced packet-sniffing tools and malware to capture card data that’s being transmitted over the network.
- Freepik (2020): This SQL injection attack resulted in 8.3 million email addresses and 3.7 million hashed passwords being leaked. Most of these passwords would be very difficult to crack as they were hashed using bcrypt, but 229,000 were hashed using an MD5 hash algorithm which is not sufficient against today’s password cracking tools.
- Under Armour (2020): Attackers were able to exploit a vulnerability in the MyFitnessPal website’s API, which allowed them to inject malicious SQL code into the API’s search function. This code enabled them to bypass the website’s security measures and gain unauthorized access to the user database. The end result was the compromise of 150 million users, including usernames, email addresses, and hashed passwords.
- TalkTalk (2015): Attackers stole the personal information of over 157,000 customers, including names, addresses, dates of birth, and financial information. The attackers then demanded a ransom from TalkTalk in exchange for not publishing the stolen data. Not only are the customers privacy being impacted, but TalkTalk faced heavy criticism for its security practices and was fined £400,000 by the UK Information Commissioner’s Office (ICO) for security failings that led to the breach.
- Adobe (2013): After attackers exploited a SQL injection vulnerability, they gained access to the Adobe’s user database and were able to steal the personal information of over 38 million users, including usernames, encrypted passwords, and credit card details. The attackers also stole source code for several Adobe products, including Acrobat and ColdFusion.
Manually Exploiting SQL Injection Vulnerabilities
Manual Exploitation Table of Contents
- SQL Injection – Retrieval of Hidden Data with WHERE Clause
- SQL Injection – Login Bypass
- SQL Injection UNION Attack – Determining the Number of Columns Returned by the Query
- SQL Injection UNION Attack – Finding a Column Containing Text
- SQL Injection UNION Attack – Retrieving Data From Other Tables
- SQL Injection UNION Attack – Retrieving Multiple Values In a Single Column
- SQL Injection Attack – Querying the Database Type and Version on Oracle
- SQL Injection Attack – Querying the Database Type and Version on MySQL and Microsoft
- SQL Injection attack – Listing The Database Contents on Non-Oracle Databases
- SQL Injection Attack – Listing Database Contents on Oracle
- Blind SQL Injection With Conditional Responses
- Blind SQL Injection With Conditional Errors
- Blind SQL Injection With Time Delays
- Blind SQL Injection With Time Delays and Information Retrieval
- SQL Injection With Filter Bypass via XML Encoding
The following examples of SQL Injection exploitation are all being performed from PortSwigger’s web academy. This is a completely free resource that you can begin using today to learn about common web application vulnerabilities. Feel free to follow along with me using this platform. Additionally, I would recommend downloading BurpSuite’s community edition. This is natively installed if you are using Kali Linux and once again, is completely free.
SQL Injection – Retrieval of Hidden Data with WHERE Clause
In this demonstration you will see how an attacker may try to retrieve information from a database by using the WHERE clause in a SQL statement.
In the screenshot above you can see that refining our search to Accessories brings us to the corresponding directory and this is also visible in the URL. Like mentioned earlier, the application is communicating with the database to retrieve this information, so what does this look like in a SQL statement?
SELECT * FROM products WHERE category = 'Accessories' AND released = 1
- SELECT: The select clause is used to retrieve data from a table and when paired with a *, it is instructed to retrieve all data.
- FROM: This clause is used to specify which table in the database data is being retrieved from. So in this case we are retrieving all data with our select clause from the products table in the database.
- WHERE: This clause is used to filter data from a table based on a specific condition or set of conditions. In this case the condition is that the category must be accessories.
- AND: This is used in conjunction with the WHERE clause to further narrow down the SQL query. The released condition is equal to one which means that we are seeing only released products, and unreleased ones are hidden. The goal of the attacker here is to view unreleased products.
Now that we have a basic understanding of the SQL query that’s being performed, we can being trying various things in the application to see what happens. We took a look at the Accessories category, but what if we change the URL and don’t include a value in category?
We do not retrieve any results, but we can still select our category from the web page. Once again, the SQL query that is being performed can be seen below.
SELECT * FROM products WHERE category = " AND released = 1
What if we inject a single quote (‘) into category within the URL? We of course know this isn’t a directory on the web page, but what will the application do? Before demonstrating, it is important to mention that this is a common way to test for SQL injection vulnerabilities because it is a common character that can be used to break the syntax of a SQL statement. If an error is returned, there is a good chance that the application is vulnerable to SQL injection.
SELECT * FROM products WHERE category = '" AND released = 1
Internal Server Error – We know that injecting a single quote will break SQL syntax and it’s now very likely that the application is vulnerable since the user is able to generate this error with a broken query. It is now time to test injecting with a payload of ‘ OR 1=1–‘. In SQL, the single quote character (‘) is used to enclose string values. In the string ‘ OR 1=1–‘, the initial single quote closes any previous string value that might have been in the SQL query, and the rest of the string is interpreted as a new string value. The string OR 1=1 is a condition that always evaluates to true, since the expression 1=1 is always true. This means that any rows that are checked against this condition will be returned in the query results. The — characters indicate that everything after them is a comment and should be ignored by the SQL engine. This string can be used in a SQL injection attack to bypass authentication checks or to retrieve data from a database.
We can now see all of the existing products including the hidden ones. This exploit allowed us to see 8 products that we would not have been able to see otherwise.
SELECT * FROM products WHERE category = " OR 1=1--' AND released = 1
- OR: This is a logical operator used to combine multiple conditions in a WHERE clause. The OR operator is used when at least one of the conditions must be true for a row to be included in the query results.
- 1=1: Always true
- –: Comment out the query and ignore anything that comes afterwords
SQL Injection – Login Bypass
This example of SQL injection will be very similar to the last example. We are once again using the same payload (‘ OR 1=1–), but this time it’s to bypass login and get unauthorized access to the administrator account. This will showcase the use of an interception proxy in BurpSuite to modify a request with a SQL injection payload. Another great interception tool this can be achieved with is ZAP by OWASP. This may seem a little intimidating if you’ve never used a proxy before, so I will also showcase other methods that make this even easier afterwords.
The first step is to navigate to Proxy > Intercept in BurpSuite and then turn the Intercept on.
You will also need to set your browser’s proxy to match BurpSuite. There are several tools for this, but in this example I am using FoxyProxy.
Once the browser traffic is being intercepted by BurpSuite, you can attempt logging in with any username and password. You will not be redirected since the request is captured in your proxy. From the proxy you can see the request and add a SQL injection payload to bypass authentication.
With our interceptor on, we can capture the request and see that I have entered “chandler” in the username field and “password” in the password field. These are not valid credentials and I will not be authenticated but since I have intercepted the request, I can modify these values before it gets forwarded. Using the same logic as before, I can add ‘ OR 1=1– to my username and it will authenticate me as the first user in the users database table which is Administrator. The area where I’m adding this payload can be seen highlighted in the screenshot above. Since the query is commented out with –, a valid password does not need to be supplied. This will look like &username=chandler'+or+1=1--
.
This can also be easily achieved without a proxy by manually entering the SQL syntax into the login form. Once again, I am using chandler as the username but I will be authenticated to the web application as administrator since their is a SQL injection vulnerability present.
The above examples showcase bypassing the login without even knowing a valid username, but this can be even more simply done if you do know a valid user. Since we know that administrator is a valid username, we simply login with administrator'--
. Once again, commenting the query out with — ignores the rest of the statement and the password requirement is bypassed.
SQL Injection UNION Attack – Determining the Number of Columns Returned by the Query
UNION
is a SQL clause that is used to combine multiple statements for retrieving data in multiple tables. For UNION to work, the separated queries must contain the same number of columns and the data types in each column must be compatible between each query.
SELECT a, b FROM table1 UNION SELECT c, d FROM table2
- The UNION clause in SQL is used to combine the results of two or more SELECT statements into a single result set. The UNION operation removes duplicate rows between the various SELECT statements.
Since having the same number of columns in each query is a requirement, we can use the ORDER BY
clause to incrementally increase the number of columns until an error is returned. Once an error is returned, we will know the last successful ORDER BY is usable.
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--
The ORDER BY position number 3 is out of range of the number of items in the select list.
- The
ORDER BY
clause in SQL is used to sort the result set of aSELECT
statement based on one or more columns.
It is possible for the application to return the error in the interface. This could look like a generic error, or we may actually receive the SQL error message.
Another method for determining the number of columns is to use SELECT UNION payloads to specify a different number of null values.
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
Once again, the application may return this error message for us. One common error to see displayed is NullPointerException. NULL can be used with every data type which increases the chances of success and makes it an excellent candidate when enumerating the number of columns in a query.
With that basic explanation, it’s time to showcase this attack in action. This example will once again involve using BurpSuite’s proxy. The first step is to intercept the request on the targeted application and send the request to Repeater.
From Repeater, I added a single quote (‘) at the end of accessory and sent the request to the application. The response from the application included an internal server error which indicates that a SQL injection vulnerability is present. It is now time to inject a UNION statement.
Injecting the UNION SELECT statement with null will once again provide an internal server error. The reason for this is because there is more than one column of data in the SQL query. We are now going to repeat this process adding an additional null in each request until there is no error.
With three null values we are able to retrieve a 200 response which means that there are three columns of data in this query. To better visualize what is happening here, you can view the SQL statement that is being executed in the back-end database below.
SELECT * FROM products WHERE category = 'Clothing, shoes and accessories' UNION SELECT NULL,NULL,NULL--
SQL Injection UNION Attack – Finding a Column Containing Text
What if we wanted to identify a certain string of text within a column? Following the previous column number enumeration, we already know that there are three columns in the “Clothing, shoes and accessories” table and the next step for an attacker would be to search for interesting data by replacing null values with strings. If the response is successful, then the data is present.
To start this challenge we will follow the same steps as before by initially sending a single quote, you will receive a 500 response. However this time we can see a hint that instructs us to retrieve the string ‘h0pN6w’.
We already know there are three columns, but one of these columns contains the string we are searching for so we will replace the null statements until we identify which column has this value. The purpose of the null values previously was to just simply determine the number of columns. Replacing the first null value with the string will provide a 500 error.
Replacing the second null with the string data yields a successful response and you now know that this string is present in the column.
The SQL statement being performed on the database can be seen below.
SELECT * FROM products WHERE category = 'Clothing, shoes and accessories' UNION SELECT NULL,'h0pN6w',NULL--
SQL Injection UNION Attack – Retrieving Data From Other Tables
We have performed some great enumeration but lets take it a step further and steal some credentials. Using the UNION statement below you can see that I am searching for the usernames and passwords that are stored in the users table within the database. This payload provides a 200 response and scrolling down will showcase four sets of credentials. You can take administrator:d8530d5ihdaw8r0cey2v and authenticate into the application.
This scenario requires knowing that there are username and password columns in the users table within the database. This data could be named something completely different which will make exploitation more difficult as there would be a lot of guessing required. The SQL statement being performed on the database with this payload can be seen below.
SELECT * FROM products WHERE category = 'Clothing, shoes and accessories' UNION SELECT username,password FROM users--
SQL Injection UNION Attack – Retrieving Multiple Values In a Single Column
Knowing how to retrieve multiple values in a single column is important because what if you are able to get multiple columns identified, but they use different data types? You will need to know how to target the column that uses a specific data type and retrieve multiple values using concatenation. Following the same steps as before we will first need to confirm a SQL injection vulnerability is present and determine the number of columns using a UNION SELECT statement with null values. However this time we will also need to see which column will allow string data types.
In the screenshot above you can see that there are two columns and the second one allows the string data type. Wrapping the first null in single quotes will provide a 500 internal server error response, but wrapping the second null in single quotes will respond with a 200 response. SQL injection payloads with various string concatenation syntax can get pretty tricky so the next step is to do a Google search for “SQL Injection UNION Attack – Retrieving Multiple Values In a Single Column”.
This website provides a very useful table for the various syntax that required by different database engines. We don’t currently know what the underlying database infrastructure is so we will try each syntax until a match is found. Immediately after trying the Oracle syntax, we receive a successful 200 response and credentials can be found in the response of the request.
The SQL query being performed on the database can be seen below.
SELECT * FROM products WHERE category = 'Corporate gifts' UNION SELECT NULL,username||'-'||password FROM users--
SQL Injection Attack – Querying the Database Type and Version on Oracle
Querying the database version is an important step in SQL injection because it can provide valuable information about the database and help the attacker determine which exploits to use. Knowing the database version can reveal which SQL features and functions are supported, which can help the attacker craft more effective attacks. For example, if the database version is known to support certain functions or procedures that can be used to extract sensitive data, the attacker can focus on exploiting those features. Additionally, the database version can also provide information about the types of security measures and patches that are installed on the database, which can help the attacker identify potential vulnerabilities or weaknesses in the system.
To query the Oracle database information we can once again do a Google search for something like “query oracle version sql injection” and will find an excellent cheat sheet from pentestmonkey. This cheat sheet contains information for finding the database version information and much more.
SELECT banner FROM v$version WHERE banner LIKE ‘Oracle%’;
SELECT banner FROM v$version WHERE banner LIKE ‘TNS%’;
SELECT version FROM v$instance;
- The
LIKE
clause in SQL is used to compare a column value to a pattern using wildcard characters. It is commonly used in theWHERE
clause of aSELECT
statement to filter rows based on a specific pattern.
The first step is to determine the number of columns since this is once again a UNION statement. In Oracle, every SELECT statement must also specify the FROM clause which means we need to know an existing table name in the database. In this scenario we will specify “dual” in the FROM clause because dual is a default table that is included in Oracle.
In the screenshot above we can see that there are two columns and they both allow string data types. It is now time to inject our SQL injection payload to retrieve Oracle database version information.
The SQL statement being performed in the database can be seen below.
SELECT * FROM products WHERE category = 'Clothing, shoes and accessories' UNION SELECT banner,'NULL' FROM v$version--
SQL Injection Attack – Querying the Database Type and Version on MySQL and Microsoft
Querying the database information for MSSQL and MySQL will be the same as Oracle but we will just need to change up our syntax a little bit. After finding the number of columns we can once again use pentestmonkey to find the proper syntax to achieve database version information.
One subtle difference that you will notice with the syntax here is that a # is being used to comment out the statement instead of –.
The reason that this challenge targets both MSSQL and MySQL is because both database engines use @@verson
to retrieve the database version information. The SQL statement being performed in the back-end can be seen below.
SELECT * FROM products WHERE category = 'Clothing, shoes and accessories' UNION SELECT @@version,NULL#
SQL Injection attack – Listing The Database Contents on Non-Oracle Databases
This will be another attack using a UNION clause, so the first step is to identify how many columns are present. As shown in the screenshot below, there are two columns and they both allow string data types. Throughout this scenario you will be dumping table names, column names, and credentials from the discovered columns. The injection payloads I’m using can be found on PortSwigger’s SQL Injection cheat sheet.
The next goal is to dump all database table names with a SQL injection payload that targets information_schema.tables
. After performing this injection and listing all of the database’s tables, you will find users_jgpwzt. To find this more quickly, I did a search in the response for “user”. There is a good chance that this table is being used to store the credentials of the application’s users. For your convenience I have also included the SQL statement that is being performed in the back-end database.
SELECT * FROM products WHERE category = 'Corporate gifts' UNION SELECT table_name,NULL FROM information_schema.tables--
We can perform another injection, but this time the goal is to dump the column names from the users_jgpwzt table. After performing this we can find the username and password column names. Once again, the SQL statement can be found below the screenshot.
SELECT * FROM products WHERE category = 'Corporate gifts' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='users_jgpwzt'--
The final step will be to retrieve the usernames and passwords stored in the database. Since we know the username and password column names in our identified user table, we can perform a simple injection to achieve this. After receiving a 200 response from the application, you can perform a search for “administrator” within the response of the request and then successfully authenticate into the application. The SQL statement is once again listed below the screenshot.
SELECT * FROM products WHERE category = 'Corporate gifts' UNION SELECT username_svzdwt,password_vwamar FROM users_jgpwzt--
SQL Injection Attack – Listing Database Contents on Oracle
We will follow the same concept for this exercise and get started by first determine the number of columns and seeing if they accept string data types. Since the database engine is Oracle, we will also need to specify the FROM clause targeting the dual default table.
The next step is to dump all of the database’s table names. Upon doing this we find a match for what will likely be the users table which stores credentials to the application.
SELECT * FROM products WHERE category = 'Clothing, and accessories' UNION SELECT table_name,NULL FROM all_tables--
Now that we have a table to target in the database, we can dump all columns within that table. When doing this, you will find the username and password column names. You will only see the password column in the response of the screenshot below, but the username column can be located when scrolling down further.
SELECT * FROM products WHERE category = 'Clothing, and accessories' UNION SELECT column_name,NULL FROM all_tab_columns WHERE table_name='USERS_AJLDEC'--
Now that know the column names in our targeted table, we can dump all credentials and authenticate into the application as administrator.
SELECT * FROM products WHERE category = 'Clothing, and accessories' UNION SELECT USERNAME_LSUCDM,PASSWORD_DMRVFR FROM USERS_AJLDEC--
Blind SQL Injection With Conditional Responses
Blind SQL injection is a type of SQL injection attack where the attacker can’t see the results of their SQL queries directly. In a typical SQL injection attack, the attacker is able to see the results of their queries directly by exploiting vulnerabilities in the application’s user input validation and SQL query construction. However, in a blind SQL injection attack, the application’s response to the attacker’s queries does not reveal any information about the underlying data or database structure. This is a bit more tricky because all of the previous attacks we have performed leading up to this point, we have been able to first inject a single quote into the category parameter of the request and receive a 500 internal server error which indicates the application is vulnerable to SQL injection.
In a blind SQL injection attack, the attacker must rely on the application’s response to indirectly infer the data or structure of the database. This can be done by exploiting vulnerabilities that allow the attacker to ask “yes” or “no” questions about the data. For example, the attacker might send a SQL query that asks whether a particular value exists in the database, and the application’s response will either be “yes” or “no”. By using a series of such queries and analyzing the responses, the attacker can gradually infer the structure and content of the database.
Luckily this challenge gives us a hint that the SQL injection vulnerability will be found in the tracking cookie. In the real world you won’t know what parameters are vulnerable so you would need to fuzz everything to find possible vulnerabilities. We also know that the application will provide a “Welcome back” message our query returns any rows. If no rows are returned, then this message will not be present. We also are given information that there is a user table that has username and password columns. The end goal is to once again authenticate to the application as administrator.
The first part of our testing will be to force a true query and see how the application responds. Afterwords, we will force a false query and compare the response. If you find a difference between the responses, then a blind SQL injection vulnerability is present. The screenshot below shows that appending ' AND 1=1--
returns a true response.
Now what if we test a query that is false? Will the application still return a “Welcome back!” message? The answer is no and we can now confirm that the TrackingId
parameter is vulnerable to blind SQL injection.
These are very basic examples, but it we are able to communicate with the database engine and it’s now time to start finding true or false statements for other information that could be valuable to us. For example, this query is used to confirm that we have a users table in the database.
Since we are after the administrator account, we will want to make sure the username is actually “administrator”. This is important because it could be admin, root, sa, or something completely different. This query provides the “Welcome back!” message which means that there is an administrator username within the users table on the underlying database.
Since this is blind SQL injection, we are not going to be able to retrieve the password in the response of our request. This means we will have to get a little more creative. To initiate password enumeration against the administrator account, we will first want to find how many characters long the password is. This query is asking if the password is greater than one character. The expected response here is of course going to be true.
The idea now is to modify the query and instead of asking if the password is greater than one character, we will ask if it’s greater than two characters. Once we receive the true response, we will ask if it’s greater than three and so on. Of course this could be a tedious process to do manually so we are going to brute force this enumeration by using Intruder within BurpSuite. To do this, right-click the request and select “Send to Intruder”.
Now that it is in Intruder, we will use the sniper attack type, clear the highlighted areas, and manually highlight the number “1”. Once this number is highlighted you can click add. The idea here is that Intruder will keep testing a greater than number for us. Once we receive a different response we will be able to know how many characters long the password is.
Move over into the payloads tab. Here we will specify that the payload type is numbers and we are going to set the from field to 1 and the stop field to 50. This means that we are seeing if the password is between 1 and 50 characters in length. Additionally we want to set step to 1 since we are checking one number at a time. Once this is complete, click start attack.
Once the sniper attack is complete you will notice that request 20 presents a difference in the length column. This indicates a different response from the application. With this information we can conclude that the password is 20 characters long.
Now that we know how long the password is, we need to identify what characters are present in the password. This query is asking if the first letter of the administrator password is “a”. Since the “Welcome back!” message is not presented, we know this is false. If the administrator password did start with an “a” we would see the “Welcome back!” message. We are going to take this character identification logic and use in a brute force attack. Once again, right click the request and send it to Intruder.
Now that the request is in Intruder, set the attack type to cluster bomb. This attack works by specifying multiple payloads and automatically generating all possible combinations. In this case we are going to look for all alphanumeric characters (a-z and 0-9) against all 20 characters in the password. Each character in the password will have a different length in the response if there is a match for one of the alphanumeric characters. Once the attack type is set, clear the highlighted areas and highlight “1” and “a” and then click add. Before starting the attack follow the next steps to set up your payloads properly.
Set the first payload to numbers. This will need to be 1-20 since we need to find a match for each character in the password.
In the second payload you will need to set the password type to brute forcer. Additionally the character set will need to be set to alphanumeric characters and the minimum and maximum length will need to be set to one. Now that this is complete you can click the shiny start attack button.
Here are the results from the cluster bomb attack. I sorted by the length and you can see that the requests which I have highlighted in yellow have a different length and there are only 20 values (the number of characters in the password). View the payload 1 column to see which character number in the password it is and compare with the payload 2 column to see the actual value of the character. For example, the first character in the password would be “r” and the second character would be “z”. Once you compare each value in order, you will retrieve the administrator password.
The password here was “rzzjiw4n1whnkjlo6vb0” and it provided successful authentication into the application.
Blind SQL Injection With Conditional Errors
This scenario will be similar as we once again know that a tracking cookie will be the vulnerable parameter to target. However, tackling this with conditional responses that are based on present data (true or false) will not be possible here since the application will not respond differently. It’s not over though! The application will return a custom error message if the SQL query causes an error. This will require the same process as before, but this time working through errors. We also know that the database has a users table with username and password columns. The end goal is to authenticate as administrator. In the screenshot below you will see that a single quote provides a 500 internal server error.
As mentioned above, an invalid SQL query will return an error in the application. With that being said, a valid SQL query will not return an error. Adding two single quotes provides a 200 response.
Next we can add concatenation to further test a valid SQL query. The goal is to receive a good response and confirm that the SQL injection vulnerability is present.
You are probably now asking why we got an error with valid syntax. Well if you remember from earlier, there is different syntax for different database engines. This test shows us that we are not using MySQL, so now we can try sending this request again, but this time with Oracle syntax.
We now know that Oracle is being used and can begin constructing new queries to send to the database. We already know the table and columns we are attacking, so it’s time to determine the length of the password.
The above query probably looks fairly complicated if you’ve never seen this before. Essentially we are executing a concatenated subquery that is selecting a value based on the condition that username is equal to ‘administrator’ and the length of the password is greater than 1. The subquery is also using a case statement to generate an error message by dividing a number by zero if the condition is true. The error message is then converted to a string using the to_char() function. If the statement is false, then the application will return a valid response. Since we received 500 internal server, we know that the password is greater than one character. For your convenience, I have put the SQL syntax below so you can better wrap your head around it and use it if you’re following along in the lab. If you paste this into the vulnerable parameter on BurpSuite, you can easily URL encode the query by pressing CTRL+U on your keyboard.
' || (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' AND LENGTH(password)>1) || '
- The
CASE
clause in SQL is used to perform conditional logic within aSELECT
statement. It allows you to create conditional expressions and return different values based on the condition that is met. - The
WHEN
clause is a part of theCASE
expression that is used to define the conditional expressions and their corresponding results. - The
THEN
clause is part of theCASE
expression and is used to specify the value to return if the correspondingWHEN
clause is true. - The
TO_CHAR
function is used to convert a value of any data type to a string format. The resulting string can be formatted using various options provided by the function. - The
ELSE
clause is part of theCASE
expression and is used to specify a default value to return if none of theWHEN
clauses are true. - The
LENGTH
function is used to return the length of a string in bytes or characters, depending on the database system and the character encoding used. The function takes a single argument, which is the string expression to evaluate.
Using the same query but changing “1” to “50”, we can confirm that the password is not greater than 50 characters. To figure out how many characters are in the administrator’s password we can expedite this process using Intruder. Right click the request and click “Send to Intruder”. This will be the same process as the previous blind SQLi vulnerability, so I’ll avoid the screenshots, but you will want to select the Sniper attack and clear the positions. Next highlight the “50” and click add. You can then specify the payload type to numbers and the range from 1 to 50 with a 1 step increment. Once this is complete you will see that request 20 brings back a 200 response from the application which means that the password is 20 characters long.
Now we can perform more brute forcing to determine which letter or number is present in each of the 20 characters. Similar to the previous lab, we will use the Cluster Bomb attack in Intruder since we are targeting multiple positions with various combinations. One position will be character 1-20 while the other position will target all alphanumeric characters (a-z and 0-9). First send a request to Repeater that can then be sent to Intruder.
The above query is likely difficult to read and understand, so once again I have provided the SQL syntax below. This query is similar to the last once, but instead of testing rather a password length is true or false, we are going to see if the first character has the letter “a”.
' || (SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator' AND SUBSTR(password,1,1)='a') || '
- The
SUBSTR
function is used to extract a substring from a string. The function takes three arguments: the string expression to evaluate, the starting position of the substring, and the length of the substring to extract.
Like mentioned above, you will need to right click the request and “Send to Intruder”. From here you can select the Cluster Bomb attack, clear the positions, highlight “1” and “a”, and then click add.
Once the positions are set, you can navigate to payloads and set the first payload to numeric with a range of 1-20 and 1 step. We establish this range since we know that there are 20 characters in the password.
The final step is to set your second payload to brute forcer and use alphanumeric characters in the character set. Since we are only testing one letter or number against each character in the password, you will have “1” as the minimum and maximum length. Once complete, click start attack.
Once the attack is complete, you will be able to sort your results by the length or status and identify valid characters (payload 2) with the corresponding character number (payload 1). Once each value is sequentially compared, you will find the administrator password. You will notice in the screenshot below that I like to hug the payload columns as it makes comparing values is easier.
The password in my lab was “j38n55rufghslyc9k5up” and it provided successful authentication.
Blind SQL Injection With Time Delays
Time delayed blind SQL injection involves adding time delays to the SQL query or injecting SQL code that causes the database to perform time-consuming operations. By analyzing the application’s response time to the injected SQL queries, the attacker can infer information about the database and extract sensitive data such as usernames, passwords, and other confidential information.
This lab will once again use the tracking cookie as the vulnerable parameter. While testing our previous injection methods, you will find that the SQL query results are not returned, and if the query returns rows or an error, the application will not respond any differently. The next step is to test time based SQL injection. Using PortSwigger’s cheat sheet, we can find the time delay syntax for each database.
The database in use for this lab is PstgreSQL. Using the cheat sheet above, our injected query will look like ' || (SELECT pg_sleep(10))--;
. The || is used to concatenate an empty string with the result of the injected pg_sleep(10) function. The — sequence denotes a SQL comment which is used to comment out the remainder of the original SQL query. Once this is performed, you will see that the request takes ten seconds to receive a response. All of the other database engines syntax will provide an instant 200 response.
Blind SQL Injection With Time Delays and Information Retrieval
This lab is similar to previously accomplished ones and the end goal is sign in as administrator. We know that the application does not return SQL query results and will not respond any differently if the injected query returns rows or creates an error. The vulnerable parameter is the tracking cookie and time delayed SQL injection will be required for exploitation. The first step is to identify the type of database that is behind the application. After testing various queries, you will find that the database being used is once again, PostgreSQL.
The next step is to begin asking the application true and false questions with time delayed queries. We already know that there is a users table with username and password columns. Since the goal is to retrieve the administrator’s credentials, we will first need to enumerate the password length. This can be done by first testing a query that asks if the password is greater than one character. If true, the ten second time delay will take effect. If false, there request will send a response instantly.
The application did encounter a ten second delay as expected, which means that the administrator password is longer than one character. We could increase the length(password)>1
to 2, 3, 4, 5… and so on until we don’t get a ten second response. However, this would be a time consuming process and it can be automated with a sniper attack in Intruder by targeting the “1”.
The idea here is to send a request for every number up to 25 (which creates a true or false response based on password length). You could use a different range, but 1-25 works well because the password is longer than one characters and likely shorter than 25 characters. The step field should be one since we want to sequentially test every number in the range.
You will additionally need to create a new resource pool with the maximum concurrent requests set to one. When using the default resource pool or the maximum concurrent requests are set to a value higher than one, Burp Suite will attempt to send multiple requests simultaneously. This can cause the injected time delayed SQL queries to overlap and interfere with each other, leading to inaccurate results and potentially making it more difficult to extract data from the database.
The number of the first request that has a different response time (not ten seconds) will be the character length of the administrator’s password. To view the response time, we will need to add the response received column to our results and sort by the time.
Once you sort by the response received column, you will see that the 20th request took 139 milliseconds and all the previous requests took ten seconds. This means that the password for the administrator is 20 characters long.
Now that we know the length of the password, we need to perform further enumeration and identify what each character is. To do this we can perform a similar process. Sending a boolean time delayed SQL query will let is us know if a certain character contains a specified value. In the screenshot below, we are asking if the first character of the administrator’s password starts with “a”. The response was returned in 315 milliseconds and did not trigger the time delay, so we know that the first character of the password does not start with “a”.
We will now brute force every character from the 20 identified with alphanumeric values (a-z and 0-9). It is a good idea to lower the sleep(x) function in your query to allow a faster brute force attack. Send the request to Intruder and select the Cluster bomb attack. Highlight the first “1” and “a” and click add.
Set the first payload type to numbers. The range will be 1-20 since we know there are 20 characters in the password.
The second payload will use brute forcer with alphanumeric characters. Set the minimum and maximum length to one since we are performing sequential testing against each character with one value at a time.
As mentioned previously, we can only send one request at a time since concurrent time delayed requests can cause problems. You will need to select the previously created resource pool. This step is important to ensure accuracy, and it has a fair downside of making the attack very time consuming. This is especially true if you are using the community version of Burp Suite since they throttle Intruder’s performance. Once this step is complete, you can start the attack.
While doing these cluster bomb attacks throughout this research, I have had my tracking cookie expire multiple times while waiting for all requests to be sent. If you’ve made it this far in my post and have been following along in the labs, then there’s a decent chance that you have experienced this too. Hopefully I’m not too late, but I would recommend navigating around in the application every so often while the attack is being performed so that your session does not expire and you keep your tracking cookie. The reason you want to maintain your cookie is because the administrator password resets on each session. If the password resets, you will need to redo your cluster bomb attack (which is a very lengthy process) to get the new password and authenticate into the application as the administrator. That’s of course only if it’s important for you to receive credit for this lab on PortSwigger.
SQL Injection With Filter Bypass via XML Encoding
Some web applications may use input filters or validators to prevent users from submitting certain characters or strings that could be used to inject malicious SQL code. However, attackers can use various techniques to bypass these filters, such as encoding their payload using XML. These encoded payloads can be submitted to a vulnerable application and will be decoded by the web server before being passed to the SQL database. The database will then execute the injected SQL command, which could potentially grant the attacker unauthorized access to sensitive data or systems.
Automating SQL Injection Exploitation
SQLMap
SQLMap is an open-source tool that automates the process of detecting and exploiting SQL injection vulnerabilities in web applications. It works by sending specially crafted payloads to the targeted web application or database server and analyzing the responses to identify potential vulnerabilities. The tool is has a wide range of features and options to help users fine-tune their testing. To list a few, there is strong number of injection techniques, evasion techniques, and testing modes. SQLMap is currently the industry standard for quickly identifying and extracting sensitive data from vulnerable web applications.
This demonstration of SQLMap will showcase a couple simple use case scenarios. To learn more about this tool I would suggest entering sqlmap --help
and testing some of the various options. One of the best ways to use SQLMap is to perform automated fingerprinting and injections while simultaneously performing manual enumeration and exploitation. To fingerprint a web application from the root directory you can use the crawl
switch.
sqlmap -u http://10.10.111.37 --crawl=100 --batch
- -u: This option is used to specify the target URL or web application endpoint to be tested for SQL injection vulnerabilities.
- –crawl: This option is used to automatically discover and test additional URLs within the target web application. You will also need to specify the number of directories for SQLMap to crawl. Be careful to not break scope or overload the application
- –crawl-exclude: This option is not relevant for the example below but I wanted to include it because if you’re doing a crawl in an authenticated session, you do not want SQLMap to sign you out and terminate the fingerprinting.
- –batch: This option runs SQLMap in non-interactive mode. The tool will automatically select default options and suppress most interactive prompts, making it suitable for automation and scripting purposes.
The crawling functionality allows SQLMap to explore the application by following links and identifying new potential injection points. It can help in finding hidden or undiscovered areas of the application that might be vulnerable to SQL injection. If you are using this method in an authenticated session, I would recommend including –crawl-exclude=”logout”. If this is not done, SQLMap will hit the logout button while navigating the application. Once we find the vulnerability, we will want to move forward and see what databases are present.
sqlmap -u http://10.10.111.37:80/includes/pop-up-help-context-generator.php?pagename=/usr/share/nginx/html/mutillidae/home.php --dbs
- –dbs: This option is used to enumerate the names of available databases on a vulnerable target.
Now that we know the different existing databases, we will want to find some existing tables. To do this, you can specify the database that you are targeting in the DBMS and instruct SQLMap to return all table names.
sqlmap -u http://10.10.111.37:80/includes/pop-up-help-context-generator.php?pagename=/usr/share/nginx/html/mutillidae/home.php -D owap10 --tables
- -D: This option is used to specify the name of the target database.
- –tables: This option is used to enumerate the tables within a specified database of the target DBMS. It allows you to retrieve the names of the tables present in the selected database.
What table looks interesting to you? Maybe accounts? Well let’s switch it up this time and dump information from the credit cards table.
sqlmap -u http://10.10.111.37:80/includes/pop-up-help-context-generator.php?pagename=/usr/share/nginx/html/mutillidae/home.php -D owap10 -T credit_cards --dump
- -T: This option is used to specify the name of the target database.
- –dump: This option is used to extract and dump all data from a specific database.
As you can see SQLMap is a very powerful tool the greatly simplifies discovery and exploitation of vulnerabilities. This demonstration only scratches the surface of the tools capabilities, so I would certainly recommend performing more research and taking it to the next level.
Nmap (NSE)
The Nmap Scripting Engine (NSE) is a powerful feature of the Nmap security scanner. It allows users to extend the functionality of Nmap by writing and running scripts that automate a wide range of tasks related to network scanning, vulnerability detection, and more. The http-sql-injection
script is one of the scripts available in the Nmap Scripting Engine (NSE) that focuses on detecting SQL injection vulnerabilities in web applications. When you run the http-sql-injection
script, it sends crafted HTTP requests to the target web application, attempting to inject SQL queries into vulnerable parameters or fields. The script analyzes the responses received from the application to identify indications of SQL injection vulnerabilities. For more information on this script, go look at Nmap’s documentation.
nmap -p80 --script=http-sql-injection 10.10.111.37
- -p: This option is used to specify the port(s) to scan. Ports are endpoints on a network device that processes incoming and outgoing network communications. Port 80 is the default port for HTTP, which is the protocol used for transmitting web pages over the internet.
- –script: This option allows you to specify one or more Nmap scripts to run against the target hosts during a scan. Nmap scripts are small programs or modules written in the Nmap Scripting Engine (NSE) language, designed to extend the functionality of Nmap by providing additional features, vulnerability detection, or network exploration capabilities.
Cheat Sheets
Cheat sheets are crucial for penetration testing as they serve as concise reference materials that provide quick access to essential commands, techniques, and tools used during assessments. They enable penetration testers to navigate through various testing stages efficiently, ensuring accuracy, consistency, and productivity. By consolidating crucial information in an easily accessible format, cheat sheets help testers save time, avoid errors, and deliver reliable results within tight deadlines. They act as valuable resources that enhance the effectiveness and efficiency of penetration testing engagements.
- WebSec SQL Injection Knowledge Base: https://websec.ca/kb/sql_injection
- NetSPI SQL Injection Wiki: https://sqlwiki.netspi.com/
- Pentest Monkey SQL Injection Cheat Sheet: https://pentestmonkey.net/category/cheat-sheet/sql-injection
- Infosec SQLMap Commands: https://resources.infosecinstitute.com/topic/important-sqlmap-commands-2/
- Nmap NSE Script http-sql-injection: https://nmap.org/nsedoc/scripts/http-sql-injection.html