
Proof-of-concept exploit per CVE-2026-6471, che dimostra l'escalation dei privilegi in PostgreSQL tramite dlopen del logical decoding per ottenere l'esecuzione di codice arbitrario e una backdoor da superutente.
La decodifica logica di PostgreSQL consente a un ruolo non superuser che detiene il
privilegio REPLICATION di creare uno slot di replica logica e scegliere il
plugin di output. Nelle versioni interessate non esiste alcun controllo di autorizzazione su tale
scelta, quindi il server esegue dlopen() su qualunque cosa punti il nome del plugin. Dare il nome
a un percorso di libreria esegue il codice di quella libreria all'interno del backend postgres, come
account del sistema operativo che esegue il server. Si tratta di esecuzione di codice arbitrario
come utente OS del server di database, che per un database che contiene i
dati dell'applicazione equivale di fatto alla compromissione del server.
Corretto in PostgreSQL 18.6, 17.11, 16.15, 15.19 e 14.24 tramite la
allowlist output_plugin_libraries (default pgoutput, test_decoding).
Qualunque cosa non presente in tale elenco ora genera un errore library "X" may not be used as an output plugin prima di qualsiasi dlopen.
Esegui gli stessi passaggi su entrambe le build e cambia solo la versione di PostgreSQL. Un account a bassi privilegi (LOGIN + REPLICATION, non superuser, nessun accesso OS) fa ciò che farebbe un normale subscriber di decodifica logica, poi chiede al server di caricare una libreria arbitraria come plugin di output. Sulla build vulnerabile quella libreria viene eseguita come utente OS di postgres e pianta un ruolo backdoor superuser persistente, quindi un account solo-replica termina l'esecuzione con un login superuser funzionante.
cve-2026-6471-postgres-logical-decoding-dlopen.txt è una soluzione Exploitmatic
(.txt): dati più asserzioni, nessun codice. Il runtime la riproduce contro
la macchina. La soluzione consegna pwn.so al momento dell'esecuzione (base64 nel file),
guida l'SQL come ruolo di replica a bassi privilegi, poi accede come
ruolo backdoor superuser piantato dal payload.pwn.c è il sorgente della libreria payload e pwn.so la sua build: un singolo
costruttore ELF che viene eseguito come utente OS di postgres, si connette tramite la
socket locale come superuser del database (peer/trust) e crea il ruolo
persistente cve6471_backdoor LOGIN SUPERUSER PASSWORD 'BackdoorPass1'. Viene eseguito
solo se il server carica effettivamente la libreria, cioè solo su una build
vulnerabile. Compila con gcc -Os -shared -fPIC -o pwn.so pwn.c su una libc Linux
corrispondente all'immagine del server.Prerequisiti: hai accesso a docker.
La cartella lab/ contiene la replica esatta usata per verificare questo PoC. Compila ed
esegui le due macchine (vulnerabile = postgres 16.14, patchata = postgres 16.15):
docker build -t pg-6471-vuln -f lab/Dockerfile.vuln lab
docker build -t pg-6471-fixed -f lab/Dockerfile.fixed lab
docker run -d --name pg-6471-vuln -p 15432:5432 -e POSTGRES_PASSWORD=lab-super-pw pg-6471-vuln
docker run -d --name pg-6471-fixed -p 15433:5432 -e POSTGRES_PASSWORD=lab-super-pw pg-6471-fixed
Entrambe le macchine eseguono l'immagine postgres ufficiale stock con wal_level=logical.
Lo script di init lab/01-repro.sh crea il ruolo a bassi privilegi che la
soluzione usa, repro_rep LOGIN REPLICATION PASSWORD 'repropass', che non è
superuser.
Poi riproduci la soluzione:
exploitmatic run cve-2026-6471-postgres-logical-decoding-dlopen.txt 127.0.0.1
Punta alla macchina patchata con un override di variabile, senza modificare file:
exploitmatic run cve-2026-6471-postgres-logical-decoding-dlopen.txt 127.0.0.1 \
--var ctr=pg-6471-fixed
ctr è il container docker, pw è la password del ruolo di replica.
| target | risultato |
|---|---|
| postgres 16.14 (vulnerabile), wal_level=logical | 4/4 verificato, login backdoor superuser funzionante |
| postgres 16.15 (patchato), wal_level=logical | 3/4 non verificato, nessuna backdoor |
Solo per test autorizzati e ricerca, su sistemi di tua proprietà o per cui hai il permesso di testare. Questo PoC dimostra un'escalation di privilegi post-autenticazione: richiede un account di database a bassi privilegi esistente con l'attributo REPLICATION, oltre a un modo per posizionare codice controllato dall'attaccante dove l'utente OS di postgres possa caricarlo. Non è un attacco remoto non autenticato. Il codice caricato raggiunge il superuser del database perché l'account OS che possiede il server può connettersi tramite la socket locale come superuser (peer/trust), che è la pratica standard di distribuzione di PostgreSQL.
| passaggio | vulnerabile 16.14 | patchato 16.15 |
|---|
| ricognizione (il ruolo è di replica, non superuser) | superato | superato |
| baseline (slot logico con pgoutput integrato) | superato | superato |
| trigger (plugin dello slot = percorso libreria attaccante) | il server lo carica | rifiutato prima del caricamento |
| takeover (login come backdoor superuser piantato) | superuser | nessun ruolo del genere |