Home   Blogs

Union SQLi -- A PortSwigger Exercise

Posted

Hello, and welcome to my first actual writeup on here!

Today I will be doing a quick walkthrough through a PortSwigger Web Security Academy exercise. This is a Practitioner-level lab, with the objective of successfully executing a UNION SQLi attack that will display the type and version of this site's database.

Some context: the UNION operator is generally used in SQL to return similar results from two different areas of a database, such as from two different tables. Executing a statement like 'SELECT a,b FROM users UNION SELECT c,d FROM accounts' will return the values of columns a and b for all users, along with the values of columns c and d for all accounts. The column datatypes for each SELECT statement must be similar (i.e., a should be similar to c and b should be similar to d), and each statement must select the same number of columns.

Therefore, to successfully execute a UNION SQLi attack, we have to inject a few different statements first in order to determine the number and types of columns in the original, benign query. After that, we can use the UNION operator to execute additional statements. I like UNION SQLi because it requires a little more sleuthing than a simple tautology-based SQL injection.

In this lab, we are aiming to execute the SQL statement 'SELECT banner FROM v$version'. We know we need to execute this statement because the lab instructions hinted that this is an Oracle database. Opportunities for user input are quite limited. The only way to modify the page is to refine the types of products shown by category. You can observe and modify your requests using Burp (community edition is sufficient). Here is what a regular HTTP request looks like when you refine products by the category 'Accessories':

'Accessories'

Based on the description of the lab, and the lack of viable user input elsewhere, we know that this must be the source of input for SQL statements. Maybe the initial statement that gets executed is something like 'SELECT * FROM products WHERE category='Accessories'', but we can't be certain at this point.

The next step for us is to figure out the number and type of columns being executed by the original statement. Here is one payload that allows us to do this:

'+ORDER+BY+1--

As stated in this helpful article, you can send repeated requests with this particular payload. This orders results by different columns, specifying which column to order by with its index. Starting with ' ORDER BY 1--, we can increment our column index until there is an error, which signifies an index out of bounds and means that we have exceeded the amount of columns in the original query. In this case, we got an error with the payload ' ORDER BY 3--, so there are only two columns.

After that, we can inject this payload:

'+UNION+SELECT+'a',NULL+FROM+v$version--

Note that because this is Oracle, our SELECT statements always have to have a FROM operator.

Since it executed successfully, we now know that the first column is at least similar to some type of string type. This is good news for us because our desired statement ('SELECT banner FROM v$version') selects a column of datatype VARCHAR2(80). So we now know that the original statement selects two columns, and the first column is some kind of varchar/string type situation. How do we modify our desired statement to fit these constraints?

The answer is simple: we just have to select an additional NULL column from v$version. Therefore our final injected payload is:

' UNION SELECT banner, NULL FROM v$version

Great news! We have gotten the site to display its database information, which could help us if we wanted to execute additional malicious statements. There are a few key takeaways from this lab:

Thanks for reading! For my next writeup, I am thinking about discussing Web LLM (large language model) attacks, discussing security in the context of this extremely trendy, controversial and misunderstood field.