El compte que algú va demanar per tiquet amb la frase «només ha de llegir el WAL» podia carregar qualsevol fitxer del sistema i executar-ne el codi com l'usuari que executa la base de dades. No és una lectura nostra: és la descripció oficial del CVE-2026-6471, publicada pel mateix projecte PostgreSQL el 13 d'agost. Va estar així dotze anys.
Dissenyem i operem infraestructura i plataformes d'aplicacions per a clients, i una base de dades amb la seva rèplica i algun consumidor de canvis penjant és una peça ben corrent en aquests muntatges. Per això aquest CVE ens interessa menys pel forat —que es tanca actualitzant— i més per dues coses que queden després del pedaç: què es pensava la gent que estava concedint quan concedia aquest permís, i què es trenca el dia que l'actualitzes sense llegir la nota de versió.
Què podia fer exactament un compte amb REPLICATION
La descripció del projecte és de les que no necessiten traducció interpretativa: «Missing authorization in PostgreSQL logical decoding allows a non-superuser holding REPLICATION privilege to dlopen any file visible to the operating system account running the server, via the choice of logical decoding plugin. This in turn runs arbitrary code as that account.»
El mecanisme cap en dues frases. Quan un client crea un slot de descodificació lògica, indica amb quin plugin vol que se li serveixin els canvis; aquest nom és un text que tria el client. Fins al pedaç, aquest text arribava al carregador dinàmic del sistema sense que ningú comprovés si el fitxer que apuntava tenia res a veure amb PostgreSQL. Cal que el servidor tingui wal_level = logical —la replicació física no carrega plugins— i cal l'atribut REPLICATION al rol. Res més.
La investigació de Cyera, que és qui signa l'anàlisi llarga de la fallada sota el nom PostGREShell, hi afegeix el detall incòmode: el fitxer no ha de ser necessàriament a la màquina. A Windows descriuen rutes UNC —n'hi ha prou amb arribar al 445 de l'atacant— i a la resta acoten el terreny: sistemes de la família RHEL amb autofs i macOS anteriors a Catalina, on l'automuntatge d'NFS ve posat de sèrie. I un cop el codi corre com l'usuari del sistema, descriuen el camí fins a superusuari de la base tocant catàlegs directament. Aquest tram és seu i així el citem: nosaltres no l'hem reproduït.
El manual portava dotze anys avisant; el tiquet deia una altra cosa
La documentació de CREATE ROLE no s'hi amaga: «A role having the REPLICATION attribute is a very highly privileged role, and should only be used on roles actually used for replication.» És allà, a la pàgina que consulta qualsevol que creï el compte. I tot i així, l'atribut es concedeix cada dia a organitzacions serioses sense que ningú parpellegi.
La raó és la de sempre, i no té res a veure amb la mandra: el permís es demana pel que fa i es concedeix pel que es diu. Qui el demana escriu «he de muntar la captura de canvis cap al data warehouse» o «l'eina de còpia ha de llegir el WAL». Qui l'aprova llegeix «replicació» i entén «llegeix, no escriu». Tots dos tenen raó en la seva frase i tots dos s'equivoquen sobre el que acaba de passar, perquè l'atribut no descriu una operació, descriu un canal: connectar en mode replicació i crear slots. El que fessin els slots era, fins a l'agost, una altra conversa que no va tenir ningú.
És el mateix patró que explicàvem a el cas del Gitea on el permís d'escriptura el donava el formulari de registre: ningú no va decidir concedir allò, es va concedir sol perquè el nom de la casella sonava inofensiu.
El 7,2 no és el número que decideix
El CVE puntua 7,2, i el vector explica per què no arriba més amunt: PR:H, privilegis requerits alts. La mètrica està aplicant al peu de la lletra el que diu el manual —REPLICATION és un rol molt privilegiat—, així que sobre el paper l'atacant ja havia de ser algú de confiança. A la pràctica, aquest atribut viu en comptes de servei: eines de còpia, servidors en espera, pipelines de captura de canvis i sistemes de monitoratge que llegeixen el WAL, que és la llista que enumera Cyera. La puntuació descriu la porta amb precisió; el que no pot saber és quantes claus has repartit tu.
I ara la part que no exagerarem, perquè tenim el costum de mirar abans d'arrossegar ningú a les presses. A 4 de setembre, The Hacker News va comprovar dues coses i les va publicar totes dues: aquest CVE no era al catàleg de vulnerabilitats explotades de CISA, i no hi havia codi de prova públic en repositoris. Cyera, per la seva banda, diu haver trobat 114 plugins maliciosos de PostgreSQL circulant —troians, miners i reverse shells— en una cerca per VirusTotal, i és honesta amb el que això significa: no en vincula cap a l'explotació d'aquest CVE. Convé subratllar-ho, perquè el titular es presta al salt: que existeixin llibreries malicioses per a PostgreSQL demostra que hi ha gent escrivint-les i que el mecanisme de càrrega és un objectiu conegut des d'abans. És una altra afirmació, i és la que hi ha. El mateix passava amb els dos CVE d'Artifactory de la setmana passada, on el que va entrar abans al catàleg d'explotats va ser el de nota mitjana, no el crític.
El pedaç mou la frontera de lloc
La correcció no treu capacitats al rol: afegeix un paràmetre de servidor. Es diu output_plugin_libraries i la documentació el defineix com la llista de llibreries instal·lades a dynamic_library_path «that are also trusted for use as logical output plugins by replication clients». Qualsevol petició que n'anomeni una altra es rebutja, i la documentació hi afegeix una frase que convé llegir a poc a poc perquè inclou els superusuaris: «All users are subject to this restriction.»
Val la pena dir què significa això en termes de govern, perquè és la part que no surt a cap titular. Abans, qui podia fer carregar codi al servidor ho decidia un atribut del rol, i els rols els crea i modifica força gent. Ara ho decideix una llista que només toquen els superusuaris: al codi font, el paràmetre està declarat PGC_SUSET i amb la marca GUC_SUPERUSER_ONLY, de manera que ni tan sols es pot llegir amb un compte normal. A la pràctica viu a postgresql.conf, però encara hi ha camí per SQL: un superusuari pot fer ALTER SYSTEM SET output_plugin_libraries i recarregar sense baixar a la màquina. Hi ha moltes menys mans allà, i aquesta és la millora real. El que no canvia és que el control s'ha mudat a un lloc on, si la configuració de la base de dades no està versionada ni la revisa ningú, tampoc no mira ningú.
I aquí és on el pedaç de seguretat et para la replicació
El valor per defecte de la llista són dos noms: pgoutput i test_decoding, els dos plugins que venen amb PostgreSQL. Les notes de la 18.6 ho diuen amb totes les lletres i amb l'avís inclòs: «Installations that rely on other output plugins must add them after updating the server». Traduït a conseqüències: si la teva captura de canvis va per wal2json o per decoderbufs, en reiniciar amb la versió nova la descodificació lògica es nega a funcionar fins que algú editi la configuració.
El projecte wal2json ja ho documenta al seu README, amb la línia que cal escriure —output_plugin_libraries = 'pgoutput, test_decoding, wal2json'— i amb l'error que veuràs si no l'escrius: library "wal2json" may not be used as an output plugin. La bona notícia operativa és que el paràmetre admet recàrrega de configuració: pg_ctl reload o SELECT pg_reload_conf(), sense aturar el servidor un altre cop. I el missatge és dels bons: la documentació mostra que al log del servidor surt amb ERROR, DETAIL —amb el valor actual de la llista— i un HINT que et diu literalment que afegeixis la llibreria i recarreguis. El problema no és que el sistema no ho digui; és que ho diu al log d'un servidor que a aquella hora no mira ningú.
Sobre decoderbufs convé ser precisos, perquè és el que més desplegaments es pot endur per davant: la documentació del connector de PostgreSQL de Debezium llista decoderbufs i pgoutput com a opcions de plugin.name i dona decoderbufs com a valor per defecte. Qui el va canviar a pgoutput a mà no se n'assabenta de res. Qui el va deixar com venia, sí. Hi ha un segon lloc on salta, i aquest mossega en migracions: pg_upgrade --check falla si el clúster nou no permet els plugins dels slots del vell, quan es migra des de la 17 o posterior. És una fallada ben intencionada —t'avisa abans— però arriba en el moment de menys gràcia possible, amb la finestra oberta i el rellotge corrent.
Tot això viatja dins d'una actualització menor de seguretat, del tipus que moltes organitzacions apliquen en automàtic pel canal de la seva distribució. És la mateixa manera de trencar producció que vam explicar a l'agost amb el pedaç de .NET que es va endur la impressió d'aplicacions WPF, amb una diferència a favor de PostgreSQL que és de justícia reconèixer: aquí el projecte ho documenta, ho explica i et deixa la sortida escrita en una línia. Que a més aquella sèrie passés de la 18.4 a la 18.6 perquè la 18.5 es va retirar per una regressió —ho diu el mateix anunci— és el resum involuntari de tot plegat: les versions menors no són cap tràmit, ni tan sols per a qui les publica.
D'aquella mateixa tanda del 13 d'agost —28 CVE de cop— ja vam escriure sobre un altre que es comportava igual de callat: pgcrypto desant text pla a la columna que et pensaves xifrada. Dues fallades de la mateixa publicació, amb la mateixa propietat: ni l'INSERT fallava aleshores ni el servidor crida ara.
Quatre consultes que contesten si això et toca
Es fan en cinc minuts per instància i contesten les dues preguntes de l'article: a qui l'hi vas donar i què se t'aturarà.
- SELECT rolname FROM pg_roles WHERE rolreplication; — qui té l'atribut avui. A aquesta llista suma-hi mentalment els superusuaris, que el porten implícit.
- SHOW wal_level; — si no posa logical, la descodificació lògica no hi és en joc en aquesta instància i aquesta fallada concreta no t'arriba.
- SELECT slot_name, plugin, active FROM pg_replication_slots; — la columna plugin et diu quins noms hauràs de posar a la llista blanca. Aquesta consulta abans d'actualitzar és la diferència entre una finestra avorrida i una trucada; de fet, la mateixa documentació inclou una versió d'aquesta consulta per construir la llista a partir dels slots persistents.
- SHOW output_plugin_libraries; — després d'actualitzar, per veure què permet el servidor de debò. Compte amb qui la llança: el paràmetre està marcat com de només superusuari, així que amb el compte de l'aplicació no en veuràs el valor sinó un error; cal superusuari o pertànyer a pg_read_all_settings. I fora de l'SQL: les línies replication del teu pg_hba.conf, que és on Cyera recomana limitar aquestes connexions a adreces de confiança.
Queda una pregunta que no contesta cap consulta, i és la que de debò defineix la teva superfície després del pedaç: qui pot escriure a postgresql.conf i al directori de llibreries de la màquina. Si la resposta és «no ho sé» o «qualsevol amb accés al servidor», la llista blanca és una porta amb la clau posada per dins.
L'ordre en què ho faríem
Primer l'inventari de slots i plugins de cada instància, incloses les de preproducció —que són les que ningú no mira i les primeres que s'actualitzen soles—. Després, en el mateix canvi que puja la versió, la línia d'output_plugin_libraries amb el que ja fas servir; no en un segon canvi «quan veiem que falla». Després, treure l'atribut al que no replica: ALTER ROLE … NOREPLICATION, que és reversible en una línia, aplicat als comptes que van sortir de la primera consulta i que ningú no sap explicar. I finalment l'alerta, que és la part que gairebé mai no hi és.
Insistim en l'alerta perquè el cost de no assabentar-se'n no és només que les dades no arribin a destí. Un slot que existeix i no avança reté WAL al disc del servidor, i allà el rellotge corre sol: primer és una gràfica que puja, després és una partició plena, i una partició plena en una base de dades no és un incident de replicació, és una aturada. Hi ha un fre per a això, max_slot_wal_keep_size, i convé saber que ve de fàbrica a -1: sense límit. Posar-hi un valor canvia la manera de fallar —el slot queda inservible en comptes d'omplir-te el disc— i aquesta és una decisió que es pren abans, no a les tres de la matinada. L'alerta útil no és «el servei està amunt», és el retard del slot creixent. Nosaltres monitorem amb Zabbix i la regla que seguim és la mateixa des de fa anys: alertes que importen, no soroll, i aquesta és de les que importen perquè avisa d'una aturada amb hores de marge. Si tens algú de guàrdia 24×7, aquesta mètrica ha de ser a la seva pantalla; si no el tens, ha de ser al correu d'algú que miri el diumenge. I a la 18 hi ha un tercer paràmetre que convé conèixer abans de necessitar-lo, idle_replication_slot_timeout, que arriba desactivat.
I una regla de conducta que surt gratis: el dia que un proveïdor afegeix un paràmetre nou amb valor per defecte restrictiu, aquella versió no s'aprova sense llegir la nota sencera. Un paràmetre nou amb defecte restrictiu és, per definició, un canvi de comportament disfressat de correcció de seguretat. Val per a PostgreSQL i val per a tota la resta que corre a la plataforma de dades i aplicacions de la qual depèn el teu negoci.
El que queda quan el pedaç ja està posat
El forat es tanca actualitzant i no hi ha més misteri. El que no es tanca actualitzant és l'hàbit que el va fer interessant: concedir atributs pel nom que tenen al formulari en comptes de pel que permeten fer. Dotze anys després, la resposta correcta a «qui té REPLICATION?» continua sent, a massa cases, «els de sempre, suposo». Zero Trust no és comprar un producte: comença sent capaç de contestar aquesta pregunta amb una llista, i continua perquè la llista sigui curta.
Saps qui té REPLICATION a les teves bases de dades?
Revisem permisos de comptes de servei, finestres d'actualització i alertes de replicació com a part de la feina, amb enfocament Zero Trust: gens de confiança implícita pel nom del rol. No som resellers de ningú, així que la recomanació de treure un permís —o de deixar-lo on és— no ens beneficia d'una manera o d'una altra.
Parlar amb everyWANNota de fonts
La descripció del CVE-2026-6471, la puntuació 7,2 amb vector AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H i les versions afectades i corregides (18.6, 17.11, 16.15, 15.19 i 14.24, publicades el 13 d'agost de 2026) són de la pàgina oficial del projecte a postgresql.org/support/security. El crèdit de la troballa és el que publiquen les notes de la 18.6: «The PostgreSQL Project thanks Vladimir Tokarev and Yu Kunpeng for reporting this problem». La citació sobre l'atribut REPLICATION és de la documentació de CREATE ROLE; la definició d'output_plugin_libraries, el seu valor per defecte, el bloc d'ERROR/DETAIL/HINT del log i la consulta per construir la llista surten de la documentació de configuració de replicació de PostgreSQL 18, igual que max_slot_wal_keep_size i idle_replication_slot_timeout. Que el paràmetre sigui PGC_SUSET amb la marca GUC_SUPERUSER_ONLY ho hem llegit a src/backend/utils/misc/guc_tables.c de la branca 18 del repositori de PostgreSQL, no a la documentació. La retirada de la 18.5 per una regressió és a l'anunci oficial del 13 d'agost. El text sobre la llista blanca, l'exemple de configuració i l'avís de pg_upgrade --check són de les notes de la versió 18.6. L'error library "wal2json" may not be used as an output plugin i la línia d'exemple amb wal2json són del README del mateix projecte wal2json. Els valors de plugin.name del connector de PostgreSQL de Debezium són de la seva documentació. El nom PostGREShell, el detall de rutes UNC i automuntatge NFS, la xifra de 114 plugins maliciosos a VirusTotal, l'enumeració d'eines que solen portar l'atribut i la recomanació sobre pg_hba.conf són de la investigació publicada per Cyera, que no vincula aquests plugins a aquest CVE; el subratllat d'aquesta distinció és nostre. La comprovació que el CVE no figurava al catàleg KEV de CISA ni hi havia prova de concepte pública a 4 de setembre de 2026 és de The Hacker News. Els dotze anys es compten des de la introducció de la descodificació lògica a PostgreSQL 9.4, publicada el desembre de 2014, així que arrodonim a l'alça per uns mesos. Les consultes SQL són estàndard i qualsevol pot executar-les contra la seva instància per comprovar el que diem.