A 12-year-old security vulnerability in PostgreSQL has been patched, closing a critical attack vector that lets low-privileged replication accounts execute arbitrary code on the underlying operating system.
Tracked as CVE-2026-6471 (CVSS 7.2) and dubbed “PostGREShell” by security researchers at Cyera, the flaw bypasses PostgreSQL’s internal library loading restrictions. An attacker with the REPLICATION attribute can escalate to database superuser, establish deep persistence, and potentially pivot to full host compromise.
The vulnerability sits in the logical decoding architecture introduced in PostgreSQL 9.4 (2014), so virtually every modern deployment using Change Data Capture (CDC), logical replication, or advanced backup solutions has been exposed.
This guide covers the technical mechanics of the exploit, the upstream fix, and a step-by-step remediation playbook for database administrators (DBAs) and DevOps engineers.
The anatomy of CVE-2026-6471: bypassing the LOAD restriction
Understanding the severity of PostGREShell requires understanding how PostgreSQL handles logical decoding and dynamic library loading.
Logical decoding extracts persistent changes to a database’s tables from its Write-Ahead Logs (WAL). This requires an “output plugin” (a shared library, such as .so on Linux or .dll on Windows) to translate the WAL data into a readable format.
Under normal circumstances, PostgreSQL enforces strict security boundaries. If a non-superuser tries to load a library with the standard LOAD command, the database restricts them to a specific, administrator-controlled directory ($libdir/plugins).
The flaw lies in the replication protocol. When a user creates a replication slot with the CREATE_REPLICATION_SLOT command, the provided plugin name is passed directly to the underlying library loader function. The replication path never invokes the standard LOAD directory restrictions.
The replication protocol’s parser is also highly permissive. It accepts almost any character inside a double-quoted plugin name, including path separators (/, \) and directory traversal sequences (../). That lets an attacker supply a full filesystem path or a network path, bypassing PostgreSQL’s native sandboxing entirely.
OS-specific exploitation vectors
How an attacker turns this into remote code execution (RCE) depends on the host operating system:
📬 Stay Ahead of Cyber Threats
Get the latest cybersecurity news, critical vulnerabilities, threat intelligence, tutorials, and exclusive giveaways delivered straight to your inbox. No spam. Unsubscribe anytime.
Subscribe to the Newsletter →Windows (SMB exploitation): This is the most dangerous vector. An attacker can supply a Universal Naming Convention (UNC) path pointing to a malicious DLL hosted on an attacker-controlled Server Message Block (SMB) share. The PostgreSQL service, running as the postgres (or Network Service) user, resolves the network path, fetches the DLL over port 445, and loads it into memory. No local file write access is required on the target server.
Linux and macOS (NFS exploitation): This path is slightly more complex. It requires the target server to have Network File System (NFS) automounting enabled (via autofs), so the attacker can mount a remote malicious share and load the .so file. If NFS is disabled, the attacker first needs a separate vulnerability or misconfiguration to write a malicious payload to the server’s local disk before triggering the logical decoding flaw.
Once the malicious library loads, the code runs inside the database backend process, with whatever permissions the OS user running PostgreSQL has.

The PostGREShell exploit chain
Security researchers Vladimir Tokarev and Yu Kunping, publishing through Cyera Research, showed how this bypass turns into a full compromise.
Once the malicious output plugin loads via the replication slot, the attacker’s code runs as the postgres OS user. From there:
- Catalog manipulation: The custom plugin modifies the PostgreSQL system catalogs directly (specifically
pg_authid), elevating the attacker’s low-privilegedREPLICATIONaccount to a full database superuser. - Persistence: With superuser access, the attacker can modify
pg_hba.confto allow remote connections from any IP, create hidden database roles, or write cron jobs and systemd services to survive database restarts. - Host pivot: Because the code runs as the
postgresOS user, the attacker can read database configuration files (which often hold plaintext passwords for other services), access local SSH keys, or chain a local privilege escalation bug to reachrootorSYSTEM.
Note: PostgreSQL scored “Privileges Required” as “High,” since the attack needs the REPLICATION attribute. But that attribute is routinely handed to service accounts for backup tools (like pgBackRest), standby replicas, and CDC pipelines (like Debezium), which makes the real-world attack surface much larger than the CVSS score suggests.
The fix: output_plugin_libraries and the CDC disruption
The PostgreSQL Global Development Group (PGDG) shipped the fix on August 13, 2026. Written by core contributor Jacob Champion, the patch adds a new configuration parameter: output_plugin_libraries.
This parameter is a strict whitelist. It controls exactly which libraries can be loaded as logical decoding output plugins. By default it’s set to:
output_plugin_libraries = 'pgoutput, test_decoding'
If a replication user tries to create a slot with a plugin that isn’t on this list, the operation gets rejected and the server logs an ERROR: library "..." may not be used as an output plugin.
The breaking change for DevOps and DBAs
The fix neutralizes CVE-2026-6471, but it also breaks things. Any environment relying on third-party output plugins will fail on the next update.
Tools that use wal2json, decoderbufs, or custom proprietary plugins for Change Data Capture will suddenly fail to initialize replication slots. Champion’s commit message explains why: applying the standard LOAD restrictions retroactively would have forced all third-party plugins into the $libdir/plugins directory, which the project judged too disruptive. The whitelist was chosen instead.
A bug in the fix: the pg_createsubscriber edge case
As of September 4, 2026, there’s a known issue with the pg_createsubscriber utility. When converting a physical standby to a logical standby, the tool creates replication slots using pgoutput but doesn’t check the new output_plugin_libraries setting. As a result, pg_createsubscriber --dry-run will succeed, but the actual conversion fails if pgoutput isn’t explicitly listed in the whitelist. A fix for this secondary issue is under review on the pgsql-hackers mailing list.
Step-by-step remediation guide for database administrators
Here’s how to secure your infrastructure without breaking your data pipelines.
Step 1: audit existing replication slots
Before applying any patches, find out which output plugins are active in your environment. Run this as a superuser:
sql
SELECT DISTINCT plugin FROM pg_replication_slots WHERE plugin IS NOT NULL;
Note: This only shows plugins that have already been used to create a slot. Check your application code, Debezium configurations, and backup scripts for plugin references too.
Step 2: apply the security patches
Update PostgreSQL to these patched releases or later:
- PostgreSQL 18: 18.6
- PostgreSQL 17: 17.11
- PostgreSQL 16: 16.15
- PostgreSQL 15: 15.19
- PostgreSQL 14: 14.24 (PG 14 reaches EOL on November 12, 2026 — plan your major version upgrade now.)
Warning for Linux package users: Ubuntu’s USN-8653-1 advisory omitted the configuration requirement and only advised a service restart. Debian’s advisory explicitly calls out wal2json and decoderbufs. Don’t trust OS-level package update scripts blindly; verify the postgresql.conf changes yourself. Managed services like Amazon RDS have already applied the fix and handled the configuration automatically.
Step 3: configure the whitelist
Open postgresql.conf and add your required third-party plugins:
ini
# postgresql.confoutput_plugin_libraries = 'pgoutput, test_decoding, wal2json, decoderbufs'
Step 4: reload the configuration
Unlike many core PostgreSQL parameters, output_plugin_libraries doesn’t require a full restart:
sql
-- Execute inside psqlSELECT pg_reload_conf();
Or from the shell:
bash
pg_ctl reload -D /path/to/your/data/directory
Step 5: adjust pg_upgrade procedures
If you’re planning a major version migration (say, PG 16 to PG 17), configure output_plugin_libraries on the new cluster before running pg_upgrade --check. If the new cluster’s whitelist doesn’t permit the plugins used by the old cluster’s replication slots, the pre-upgrade check fails.
Pre-patch mitigations and defense-in-depth
If your change-management process needs weeks to approve a database patch, or you’re running an end-of-life PostgreSQL version (13 or older), put these compensating controls in place immediately:
- Enforce least privilege. The
REPLICATIONattribute is often granted lazily to application users. Auditpg_authidand strip the attribute from any account that doesn’t explicitly need it for physical/logical replication or base backups. - Filter network egress. Block outbound SMB (TCP 445) and NFS (TCP/UDP 2049) traffic at the firewall for your database servers. Database servers rarely need to initiate outbound connections to file shares, and blocking this neutralizes the Windows SMB vector completely.
- Tighten
pg_hba.confrules. Restrict replication connections (host replication) to the IP addresses of known standby servers and CDC connectors. - Disable
autofs. On Linux and macOS database hosts, turn off NFS automounting if your backup architecture doesn’t strictly need it.
Bottom line
CVE-2026-6471 sat undiscovered for 12 years in one of the most rigorously maintained open-source databases around, which says something about how hard it is to keep backward compatibility and security enforcement both intact at once.
output_plugin_libraries closes the hole, but it also breaks CDC pipelines that depend on third-party plugins. DBAs need to audit their environments, patch their clusters, and update their configuration management to avoid outages after patching.
As of publication, CVE-2026-6471 hasn’t been added to CISA’s Known Exploited Vulnerabilities (KEV) catalog, and no public proof-of-concept exploit code has surfaced in the wild. Given how detailed the Cyera Research write-up is, that’s likely to change. Patch now.
Frequently asked questions (FAQ)
Does this vulnerability allow remote code execution without authentication? No. Exploitation requires valid credentials for a PostgreSQL account that has been explicitly granted the REPLICATION attribute, plus network access to the database port (default 5432).
Will updating PostgreSQL break my Debezium or logical replication setup? Yes, if you don’t update postgresql.conf. The patch restricts output plugins to a default whitelist (pgoutput, test_decoding). If you use Debezium with wal2json or decoderbufs, add those plugin names to output_plugin_libraries before or immediately after patching.
Is PostgreSQL 13 affected by CVE-2026-6471? Yes, the flaw exists in all versions since 9.4. But PostgreSQL 13 reached end-of-life in November 2025, and the PostgreSQL Global Development Group doesn’t patch EOL branches. You’ll need to upgrade to a supported version (14 through 18) to get the fix.
Does this affect Amazon RDS or Azure Database for PostgreSQL? Managed cloud providers have already applied the underlying binary patches. If you manage your own parameter groups in AWS RDS or Azure, make sure your custom parameter groups include output_plugin_libraries if you use custom logical decoding plugins.









