El 13 de agosto el proyecto PostgreSQL publicó una tanda de actualizaciones que cerraba 28 vulnerabilidades. Varias tenían un CVSS de 8,8; la que ha acabado en los titulares casi tres semanas después puntúa 7,2. Es CVE-2026-6471, bautizada PostGREShell por sus descubridores, y llevaba en el código desde que la versión 9.4 estrenó la decodificación lógica en 2014.
Lo que la distingue del resto de la tanda es la premisa que rompe: una cuenta con el atributo REPLICATION, el que se concede a copias de seguridad, réplicas y conectores de datos, podía elegir qué biblioteca cargaba el servidor y, con ello, ejecutar código con la identidad del sistema operativo bajo la que corre PostgreSQL.
La cadena completa la describió Cyera Research el 1 de septiembre: de la carga de código a superusuario permanente y de ahí a una persistencia con tres mecanismos que se cubren entre sí. A fecha de 4 de septiembre no constaba explotación, no había código de prueba público y el fallo seguía fuera del catálogo KEV de CISA, según comprobó The Hacker News. Esa ventana es la que interesa aprovechar.
En Hard2bit la cuenta de replicación de PostgreSQL entró hace tiempo en el inventario de identidades no humanas que revisamos con los clientes, porque casi siempre la creó alguien que ya no está y la usan herramientas que ya no se recuerdan. Este fallo convierte esa revisión en algo urgente.
Qué se puede explotar y en qué condiciones
El aviso del proyecto es escueto: la decodificación lógica no comprobaba permisos. Un usuario con REPLICATION, sin ser superusuario, elige el módulo de decodificación y con ello obliga al servidor a cargar con dlopen() cualquier fichero que vea la cuenta del sistema operativo que ejecuta PostgreSQL. Afecta a todas las versiones anteriores a 18.6, 17.11, 16.15, 15.19 y 14.24.
Hacen falta dos condiciones simultáneas. La primera es una cuenta con el atributo REPLICATION, sin necesidad de que sea superusuario ni administrador. La segunda es que el servidor funcione con wal_level = logical, el ajuste que registra los cambios como eventos legibles por herramientas externas y que hoy es la base de la captura de cambios (CDC), de Debezium, de las migraciones a la nube y de la analítica en tiempo real.
Un programa cliente que quiere leer esos cambios crea un slot de replicación lógica, un punto de lectura persistente sobre el flujo de cambios, e indica qué módulo de salida (output plugin) debe formatear el flujo. Ese nombre viajaba directo al cargador de bibliotecas.
Cómo llega la biblioteca maliciosa hasta el cargador depende de la plataforma. En Windows basta con acceso de red: el nombre del módulo puede ser una ruta UNC y el sistema la resuelve por SMB desde un servidor del atacante, sin escribir nada en el disco de la víctima. En Linux y macOS ocurre algo parecido solo si el montaje automático de NFS está activo. En el resto de despliegues, que son la mayoría de los Linux, Docker y Kubernetes en producción, el atacante necesita haber dejado antes un fichero en el disco del servidor por otra vía.
Esa diferencia explica por qué las fuentes no coinciden en el privilegio necesario. Cyera habla de una cuenta de bajo privilegio; el proyecto PostgreSQL puntuó el fallo con privilegios requeridos altos, y SUSE reprodujo esa valoración. Las dos lecturas son defendibles. Para el modelo de permisos de PostgreSQL, REPLICATION es un atributo privilegiado. Para la organización que lo concede a una herramienta de copia de seguridad o a un conector de Kafka, es una credencial de servicio más, guardada donde se guardan las demás.
Cronología: del informe de febrero a la publicación de septiembre
- 21 de febrero de 2026. Cyera envía al equipo de seguridad de PostgreSQL el análisis de causa raíz, las versiones afectadas y una prueba de concepto para Windows, según su cronología pública. El equipo confirma el fallo seis días después.
- 13 de agosto. Salen 18.6, 17.11, 16.15, 15.19 y 14.24 con la corrección incluida entre las 28 vulnerabilidades del anuncio de la versión. El texto no la destaca sobre las demás. Los créditos del hallazgo son para Vladimir Tokarev y Yu Kunpeng.
- 20 de agosto. Ubuntu publica USN-8653-1 para 22.04, 24.04 y 26.04 LTS. Como observa The Hacker News, el aviso pide reiniciar PostgreSQL tras actualizar y no menciona el nuevo parámetro de configuración.
- 1 de septiembre. Cyera publica el análisis completo con el nombre PostGREShell. CSO Online y SecurityWeek lo recogen los días 3 y 4.
- 4 de septiembre. The Hacker News comprueba ese día que el fallo no está en el KEV, que no hay pruebas de concepto en repositorios públicos y que el proyecto wal2json ha actualizado su documentación para indicar cómo registrar el módulo en la nueva lista de permitidos.
Un detalle de la cronología matiza la alarma: el parche llevaba diecinueve días disponible cuando se publicó el análisis. Quien mantenga al día sus versiones menores de PostgreSQL ya estaba protegido sin saberlo. El problema es que instalar el parche puede interrumpir la replicación lógica si no se prepara.
Por qué una cuenta de replicación acaba siendo superusuario
Un módulo de salida es código compilado que PostgreSQL carga dentro del proceso del servidor y cuya función de inicialización se ejecuta de inmediato con los permisos de ese proceso. El servidor ya contaba con un control para este riesgo, check_restricted_library_name(), que obliga a los usuarios sin privilegios de superusuario a cargar módulos únicamente desde un directorio controlado por el administrador, sin rutas absolutas ni saltos de directorio. El comando SQL LOAD pasa por ese control. La ruta de replicación no lo llamaba, y el analizador del protocolo admitía casi cualquier carácter dentro del nombre del módulo, separadores de ruta incluidos.
Desde ese punto el problema deja de ser de PostgreSQL y pasa a ser del modelo de cualquier servidor que carga extensiones. Las listas de control de acceso, los permisos por rol y la seguridad a nivel de fila viven en la capa SQL. El código que se carga con dlopen() comparte el espacio de memoria del servidor y no atraviesa esa capa.
En la prueba de Cyera, el módulo escribe directamente en pg_authid, el catálogo que define quién es superusuario, y activa todos los indicadores de privilegio de la cuenta. El cambio sobrevive a los reinicios y en el catálogo es idéntico a un ALTER ROLE legítimo: quien revise solo pg_authid no verá por dónde se hizo. Esa escalada de privilegios abre después las funciones habituales del superusuario: ejecutar comandos del sistema, leer ficheros del servidor y escribir donde el usuario postgres pueda.
La persistencia descrita tiene tres patas: reescribir pg_hba.conf para admitir conexiones sin contraseña y recargar la configuración, copiarse a una ruta estable y registrarse en shared_preload_libraries para volver a cargarse en cada proceso nuevo, y reaplicar el cambio de superusuario si un administrador lo revierte. Quien encuentre una de las tres y la deshaga tiene todavía dos por descubrir.
Cyera sitúa el fallo en una familia conocida: un servidor que carga módulos por nombre sin validar el nombre. Redis con MODULE LOAD, MySQL y sus bibliotecas compartidas, MongoDB, OpenVPN y el conector JDBC de SQLite han pasado por variantes del mismo patrón. La razón por la que se repite también la apunta el informe: el código que carga extensiones suele escribirse aparte del modelo de seguridad principal, y las dos rutas no siempre se conectan.
Quién está expuesto y quién menos
La exposición depende de tres preguntas, y hay que responderlas instancia por instancia, no por parque entero.
¿Cuántas cuentas tienen REPLICATION y para qué?
Las herramientas de copia física que usan el protocolo de streaming (pg_basebackup, Barman en ese modo), cada réplica en espera, cada conector CDC (Debezium y equivalentes), cada migración con servicios de nube y no pocos agentes de monitorización llevan una, y con los años se acumulan. Una consulta a pg_roles filtrando por rolreplication devuelve la lista; el trabajo consiste en poner nombre y dueño a cada entrada y quitar el atributo a las que no lo necesitan. La entrada de Hard2bit sobre cuentas de servicio, tokens y claves de API describe el mismo ejercicio para el resto de credenciales que no pertenecen a personas.
¿Está activada la decodificación lógica?
Sin wal_level = logical no hay slot lógico y no hay carga de módulo. Muchas instancias lo tienen activado por una necesidad puntual (una migración, una prueba de CDC) que terminó hace tiempo. Si nada consume cambios lógicos, volver a replica cierra la vía sin esperar al parche, a costa de un reinicio.
¿Qué puede hacer el servidor hacia fuera?
El escenario más directo es un PostgreSQL sobre Windows que puede abrir conexiones SMB salientes hacia Internet o hacia segmentos que no controla. Le sigue un Linux con autofs capaz de resolver rutas de red que el atacante controle. Fuera de esos dos casos, el fallo vuelve a ser el problema de dos pasos ya descrito: antes hay que conseguir escribir un fichero en el disco del servidor.
Los servicios gestionados no eximen del trabajo: Amazon RDS dispone de paquetes corregidos para las cinco ramas, según The Hacker News, y aun así la cuenta de replicación que alimenta un conector CDC sigue existiendo y sigue siendo responsabilidad del cliente. Y las versiones anteriores a 14 no reciben corrección; el proyecto recuerda además que PostgreSQL 14 deja de recibir parches el 12 de noviembre de 2026, con lo que cualquier instancia en 13 o inferior necesita una migración, no una actualización menor.
Detección: qué rastro deja
Es poco probable que este fallo levante alertas en el EDR del servidor hasta que el módulo cargado haga algo visible, y en la capa SQL las modificaciones parecen legítimas. El rastro está en la replicación y en el catálogo mismo. Estos son los indicadores que merecen una regla en el SIEM o una consulta programada:
- Nombres de módulo con separadores de ruta o saltos de directorio en
pg_replication_slots. La columnaplugindebería contener únicamentepgoutput,test_decodingo el módulo de terceros que la organización haya instalado a sabiendas. - Peticiones
CREATE_REPLICATION_SLOTdesde direcciones que no corresponden a las réplicas ni a los conectores conocidos. Conlog_connectionsactivado, la conexión de replicación aparece en el registro con su origen. - Tras aplicar el parche, líneas
ERROR: library "..." may not be used as an output pluginen el registro del servidor. Según la documentación del parámetro, así avisa la nueva lista de permitidos. Puede ser un conector legítimo sin registrar o un intento de carga; en ambos casos hay que mirar. - Cambios en
pg_authidque activenrolsuperen cuentas que no lo tenían, modificaciones depg_hba.confque relajen la autenticación y entradas nuevas enshared_preload_libraries. Los tres elementos deberían estar bajo control de integridad. - Conexiones salientes del servidor de base de datos hacia los puertos 445 (SMB) y 2049 (NFS). Salvo que el servidor monte a sabiendas almacenamiento o copias por NFS o SMB, no tienen explicación operativa.
Si alguna de estas señales aparece, la prioridad es asumir que el catálogo ya no es de fiar: comparar pg_authid y pg_hba.conf con una copia de referencia íntegra, revisar qué bibliotecas hay cargadas en el proceso y rotar las credenciales que la base de datos custodia. Es el tipo de investigación que un servicio de threat hunting aborda con el catálogo y el sistema de ficheros a la vez, porque ninguno de los dos basta por separado.
Parchear sin romper la replicación
La corrección no consiste en aplicar al camino de replicación la restricción de LOAD. Jacob Champion, autor del cambio, explica en el mensaje que acompaña al commit que hacerlo habría obligado a reinstalar todos los módulos de terceros bajo el directorio de extensiones. En su lugar, el parche añade el parámetro output_plugin_libraries, una lista de módulos autorizados que por defecto solo incluye pgoutput y test_decoding, los dos que se distribuyen con PostgreSQL. Cualquier otro (wal2json, decoderbufs y los módulos desarrollados en casa) deja de cargarse tras actualizar hasta que el administrador lo añade y recarga la configuración. Las notas de la versión 18.6 lo advierten de forma expresa.
De ahí que el orden importe:
- Antes de actualizar, inventariar los módulos en uso con
SELECT DISTINCT plugin FROM pg_replication_slots WHERE plugin IS NOT NULL. La consulta solo muestra los módulos que se han usado con éxito en algún momento, así que hay que contrastarla con los equipos de datos. - Actualizar a 18.6, 17.11, 16.15, 15.19 o 14.24, o al paquete equivalente de la distribución. Los avisos de Debian y de Ubuntu no coinciden en el grado de detalle: Debian advierte de que la corrección exige cambios de configuración y cita sus paquetes de wal2json y decoderbufs; el aviso de Ubuntu, como se ha visto, no menciona el parámetro.
- Añadir cada módulo de terceros a
output_plugin_librariesy recargar conpg_reload_conf(). No hace falta reiniciar. - Si se migra desde la versión 17 o posterior con
pg_upgrade, fijar el parámetro en el clúster nuevo antes de ejecutar--check, porque la comprobación falla si la lista no admite los módulos de los slots del clúster antiguo.
Hay un fleco abierto en la lista de correo de desarrollo, según recoge The Hacker News: pg_createsubscriber crea sus slots con pgoutput sin comprobar el parámetro, de modo que una ejecución con --dry-run puede superar la prueba y la conversión real fallar después si la lista del servidor no admite ese módulo. El 4 de septiembre el parche estaba en revisión y sin integrar.
Mientras no se pueda actualizar, las medidas que reducen exposición son las que Cyera recomienda y no exigen tocar la aplicación: retirar REPLICATION de las cuentas que no lo necesitan, limitar en pg_hba.conf las entradas de replicación a direcciones concretas (nunca 0.0.0.0/0), bloquear en el cortafuegos el tráfico saliente SMB y NFS desde los servidores de base de datos y desactivar autofs donde no se use. Todo ello es endurecimiento que debería sobrevivir al parche.
Qué vigilar en las próximas semanas
Hay tres cosas que vigilar. La primera, si CVE-2026-6471 entra en el KEV o aparece una prueba de concepto pública para Linux; el programa de gestión de vulnerabilidades debería tener el fallo en seguimiento aunque hoy no esté en el KEV ni haya señales públicas de explotación, con los criterios de KEV, EPSS y SSVC como referencia para subirlo de prioridad. La segunda, los escaneos contra instancias con el puerto 5432 abierto a Internet, que siguen apareciendo en las revisiones de perímetro.
La tercera, y la más importante, el resultado del inventario de cuentas con REPLICATION: ese inventario decide si este fallo es un riesgo teórico o una puerta abierta en la organización.
La semana pasada escribimos sobre la clave de unión de Artifactory, otro caso en el que una credencial de fontanería, creada para que dos componentes se hablen, acaba concediendo privilegios de administrador. En PostgreSQL el mismo patrón llevaba doce años sin revisarse. Una cuenta que solo sirve para que dos sistemas se hablen merece la misma revisión de mínimo privilegio que una de administrador, y en la mayoría de los parques que auditamos desde Hard2bit todavía no la ha tenido.
Este artículo combina información técnica de avisos públicos del proyecto PostgreSQL con la investigación publicada por Cyera Research y con las comprobaciones de The Hacker News, CSO Online y SecurityWeek, según la información disponible a fecha de publicación. Las consultas, parámetros y medidas de configuración descritas son orientativas: cada despliegue debe validarlas en un entorno de pruebas antes de aplicarlas en producción, y la lista de módulos autorizados debe ajustarse a las herramientas de cada organización. El estado de explotación y la disponibilidad de pruebas de concepto pueden cambiar.