SQL Injection Vulnerability in WHERE Clause Allowing Retrieval of Hidden Data

4 min read Easy PortSwigger
SQL injection
Contents

On this page

Welcome to the first post . I've started grinding through PortSwigger's labs on my way to the BSCP (Burp Suite Certified Practitioner) certification, and I figured the best way to actually lock in what I learn is to write it all down and share

We're working through these labs: https://portswigger.net/web-security/all-labs

And there's no better place to begin than the classic that every web hacker cuts their teeth on: SQL injection.

So let's dive into our very first lab.

The Lab#

Here's how PortSwigger describes it:

This lab contains a SQL injection vulnerability in the product category filter. When the user selects a category, the application carries out a SQL query like the following:

SQL
SELECT * FROM products WHERE category = 'Gifts' AND released = 1

Our mission: perform a SQL injection attack that causes the application to display one or more unreleased products.

Simple enough. Let's break down what's actually going on before we start throwing payloads around.

Understanding the Target#

Take a close look at that query, because it tells us everything we need:

sql
SELECT * FROM products WHERE category = 'Gifts' AND released = 1

Two conditions are being checked here:

  1. category = 'Gifts' only show products in the category you picked.
  2. AND released = 1 only show products that have been released

That second condition is the interesting one. It means there are products sitting in the database that are not released hidden away with released = 0 and the application deliberately filters them out. Our whole job is to get those hidden products to show up anyway.

The key insight? Our category selection gets dropped straight into that query. If the app doesn't sanitize our input properly, we can break out of the intended query and rewrite the logic. That's SQL injection in a nutshell.

Poking at the Application#

When we open the lab, we're greeted with a shop and an option to refine the search by category

All, Corporate gifts, Gifts, Food & Drink, Pets, Tech gifts

1

Clicking a category let's say Pets sends us to this URL

/filter?category=Pets

2

Notice the category=Pets bit in the URL? That value is exactly what lands inside the WHERE category = '...' part of the query. This is our injection point. Right now the Pets category shows us 3 products but how many are actually in the database, hidden behind that released = 1 filter?

Test #1: Killing the Filter with a Comment#

We already know the shape of the query, so we can craft our input surgically. Let's inject into the category so the query becomes

sql
SELECT * FROM products WHERE category = 'Gifts'-- - AND released = 1
4

Here's the magic, piece by piece:

  • The single quote ' closes the string that was opened by category = '. Without it, our input would just be treated as plain text. With it, we escape out into the query itself.
  • The -- - is a SQL comment. Everything after it gets ignored by the database including AND released = 1.

Why -- - and not just --? In many databases, a -- comment needs a whitespace character right after it to be recognized. Writing -- - guarantees there's a space there, and the trailing dash keeps whatever follows harmless. In a URL you'll often see this encoded as --+-, because + represents a space.

So what the database actually runs is effectively

sql
SELECT * FROM products WHERE category = 'Gifts'

The released = 1 check is gone. And sure enough the Gifts category now reveals an extra product that wasn't there before, an unreleased one we weren't supposed to see.

That confirms it the application is vulnerable to SQL injection, and we've already dragged hidden data into the light


Test #2: Show Me Everything with OR 1=1#

Commenting out the filter is great, but let's flex a little and pull back every product in the database, released or not. For this we use the timeless payload:

SQL
' OR 1=1-- -

Dropped into the query, it reads:

sql
SELECT * FROM products WHERE category = 'Gifts' OR 1=1-- - AND released = 1
3

Let's unpack why this is so powerful:

  • ' OR 1=1 again, the ' breaks us out of the string. Then we add an OR condition: 1=1. That statement is always true, no matter what. And in SQL, when you have condition_A OR always_true, the whole WHERE clause evaluates to true for every single row in the table.
  • -- - comments out the leftover AND released = 1 so it doesn't interfere.

The result? The database happily hands over every product it has, ignoring both the category and the released filter.

And with that...

We solved the first lab