UNION-based Payloads
Whenever we’re dealing with in-band SQL injections
and the result of the query is displayed along with the application-returned value,
we should also test for UNION-based SQL injections.
The UNION keyword aids exploitation
because it enables execution of an extra SELECT statement
and provides the results in the same query,
thus concatenating two queries into one statement.
For UNION SQLi attacks to work,
the injected UNION query has to include
the same number of columns as the original query,
and the data types need to match for each column.
To demonstrate this concept,
let’s test a web application with a preconfigured SQL query:
The query fetches all the records from the customers table.
It also includes the LIKE keyword
to search any name values containing our input
that are followed by zero or any number of characters,
as specified by the percentage operator.
We can interact with the vulnerable application from our Kali machine.
We can click SEARCH to retrieve all data from the customers table.
Before crafting any attack strategy,
we need to know the exact number of columns present in the target table.
Although the output shows that four columns are present,
we should not assume based on the application layout,
as there may be extra columns.
To discover the correct number of columns, we can submit a query into the search bar:
This statement orders the results by a specific column,
meaning it will fail whenever the selected column does not exist.
We’ll run the query and increase the column value by one each time.
We’ll discover that the table has five columns,
since ordering by column six returns an error.
With this information in mind,
we can attempt our first attack by enumerating the current database name,
user, and MySQL version.
Since we want to retrieve all the data from the customers table,
we’ll use the percentage sign
followed by a single quote to close the search parameter.
Then, we begin our injected query with a UNION SELECT statement
that dumps the current database name,
the user,
and the MySQL version in the first, second, and third columns, respectively,
leaving the remaining two null.
After launching our attack,
we’ll notice that the username and the DB version are present on the last line,
but the current database name is not.
This happens because column 1 is typically reserved
for the ID field consisting of an integer data type,
meaning it cannot return the string value we are requesting
through the SELECT database() statement.
With this in mind,
let’s update our query
by shifting all the enumerating functions to the right-most place,
avoiding any type mismatches.
Since we already verified the expected output,
we can omit the percentage sign and rerun our modified query.
This time, all three values returned correctly,
including offsec as the current database name.
Let’s extend our tradecraft
and verify whether other tables are present in the current database.
We can start by enumerating the information schema
of the current database from the information_schema.columns table.
We’ll attempt to retrieve the columns table from the information_schema database
belonging to the current database.
We’ll then store the output in the second, third, and fourth columns,
leaving the first and fifth columns null.
This output verifies that the three columns contain the table name,
the column name,
and the current database, respectively.
Interestingly, we discovered a new table named users,
that contains four columns,
including one named password.
Let’s craft a new query to dump the users
We’ll again attempt to store the output of the username, password,
and description in the web application table.
Great!
Our UNION-based payload was able to fetch the usernames
and MD5 hashes of the entire users table,
including an administrative account.
These MD5 values are encrypted versions of the plain-text passwords,
which can be reversed using appropriate tools.