At ZeroDay.Cloud 2025, Xint Code discovered a 20-year-old heap buffer overflow in PostgreSQL's pgcrypto extension.

CVE-2026-2005 is a heap buffer overflow in PostgreSQL's pgcrypto extension that allows remote code execution inside the PostgreSQL server process. The vulnerable code has been present since pgcrypto was first contributed in 2005, more than 20 years ago. The bug was discovered by Xint Code, a fully autonomous AI-powered security analysis tool. A reliable RCE exploit was demonstrated live at ZeroDay.Cloud 2025 (London, Dec 10-11, 2025), and disclosed in collaboration with the Wiz Research Team. The patch was committed upstream on Feb 8, 2026 and shipped on Feb 12, 2026 across all supported major versions (18.2, 17.8, 16.12, 15.16, 14.21).
Now that patches are available, this post details the root cause, walks through the exploit process, and provides remediation guidance.
We'd like to thank the PostgreSQL security team for their responsive handling of this disclosure.
If you run PostgreSQL in production, treat this as critical. Many real-world attack chains start with post-auth database access (stolen credentials, SQL injection, lateral movement) and end with full infrastructure compromise.
Scope and exposure
The vulnerability lies in pgp_pub_decrypt_bytea(), the OpenPGP public-key decryption path inside pgcrypto. Every supported major version of PostgreSQL is affected, and the vulnerable code path is reached with just two statements:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
SELECT pgp_pub_decrypt_bytea($1::bytea, $2::bytea);
pgcrypto is a trusted extension: any role with CREATE privilege on a database can install it without superuser access. In practice, application roles are routinely granted this level of privilege, so the barrier between authenticated SQL and the exploitable code path is very low.
How exposed is the ecosystem?
Wiz's analysis shows that 80% of cloud environments use PostgreSQL. Moreover, 45% of cloud environments directly expose PostgreSQL to the internet.
Risk tiers
Critical risk: internet-exposed PostgreSQL with permissive roles. If your PostgreSQL instance is reachable from the internet and application roles have CREATE privilege, an attacker who obtains credentials can gain code execution on the database host. Managed or shared PostgreSQL environments where the database itself serves as a security boundary are at elevated risk: a user with valid credentials can exploit this bug to escape their tenant and move laterally within the managed environment.
High risk: internal instances reachable via lateral movement. Even when PostgreSQL is not directly internet-facing, attackers who gain a foothold elsewhere routinely pivot to database hosts. Application database credentials appear in configuration files, CI/CD secrets, and environment variables, and are often easier to obtain than RCE. SQL injection is another attack vector: a single injectable parameter gives authenticated SQL execution sufficient to exploit CVE-2026-2005.
Attack flow and impact
An attacker first needs authenticated SQL execution on the target database. In practice this means stolen application credentials, a SQL injection in the web tier, or lateral movement from an already-compromised host. From there, the attacker loads pgcrypto (no superuser required) and calls pgp_pub_decrypt_bytea() with a crafted PGP message that triggers the heap buffer overflow.
This overflow can be leveraged to achieve OS command execution in three steps: information disclosure, arbitrary write, and command execution.
- Information disclosure: The attacker leverages the overflow to leak PIE base and heap addresses.
- Arbitrary Write: The attacker leverages the overflow to write arbitrary values in the server's memory, which is used to grant the current connection superuser privileges.
- Command Execution: With superuser privileges, PostgreSQL natively allows arbitrary command execution.
At that point the attacker has the privileges of the database daemon: access to every database on the instance, secrets in tables or connection strings, and a network position that can reach adjacent services.

Technical deep dive
CVE-2026-2005 is a heap buffer overflow in pgp_parse_pubenc_sesskey() (contrib/pgcrypto/pgp-pubdec.c).
The vulnerable logic
When parsing a PGP public-key encrypted session key packet, the code decrypts the RSA/ElGamal payload and derives a session key length from the decrypted message as msglen - 3. It then copies that many bytes into ctx->sess_key, a buffer bounded by PGP_MAX_KEY (256 / 8 = 32 bytes), with no bounds check:
ctx->sess_key_len = msglen - 3;
memcpy(ctx->sess_key, msg + 1, ctx->sess_key_len);
msglen is determined by the RSA or ElGamal modulus size minus PKCS#1 v1.5 padding, so an attacker who controls the PGP message can push the session key length into the hundreds of bytes, far beyond the 32-byte destination.
Why this is exploitable
Two properties make this overflow straightforward to exploit.
First, the heap layout is predictable and contains data ripe for corruption. Inside decrypt_internal, the PGP_Context ctx (which contains sess_key) is allocated immediately before two MBuf structs, one for input (src) and one for output (dst). MBuf structs contain information about a memory buffer, including pointers to the beginning, current cursor, and end. During the overflow, the heap layout is [ctx][src][dst]. The overflow runs directly from sess_key through src into dst, potentially corrupting both. The palloc chunk headers between allocations are deterministic and can be preserved in the overflow payload, so the corrupted buffers still free cleanly.
Second, PostgreSQL's process model further increases exploitability. PostgreSQL forks a new backend for each connection, and the fork inherits the parent's address space. ASLR layout is identical across connections to the same postmaster, so any address leaked on one connection is valid on the next.

Together, these give a three-stage exploit:
Stage 1 — Information leak. The attacker triggers the overflow with a payload that partially overwrites dst->data with two null bytes, pointing it to a lower heap address. When decrypt_internal returns, mbuf_steal_data(dst) returns everything between the corrupted dst->data and the original dst->data_end, a window of heap memory containing PIE text pointers and heap addresses. From this single call, the attacker extracts the PIE base and a heap reference, enough to compute the address of any global or heap object.
Stage 2 — Arbitrary write. The attacker opens a new connection (same memory layout) and triggers the overflow again. This time the payload crafts all four dst fields (data, data_end, read_pos, buf_end) to point at a target address. A subsequent PGP_PKT_SYMENCRYPTED_DATA packet in the same PGP message writes attacker-controlled plaintext through the corrupted dst, giving an arbitrary write to any known address.
Stage 3 — Privilege escalation to RCE. The attacker targets CurrentUserId, a global in PostgreSQL's .data section that holds the OID of the currently authenticated role. Setting it to 10 (BOOTSTRAP_SUPERUSERID) grants superuser privileges for the remainder of the connection. With superuser, the attacker executes COPY (SELECT ...) TO PROGRAM '{command}' for arbitrary OS command execution as the PostgreSQL system user.
Remediation
Patch now
A patch was committed upstream on Feb 8, 2026 and released across all major versions of PostgreSQL on Feb 12, 2026: PostgreSQL 18.2, 17.8, 16.12, 15.16, and 14.21.
Upgrade to the patched minor release for your major version. PostgreSQL minor upgrades are designed to be low-risk; staying on an older minor release carries more risk than upgrading.
Cloud managed PostgreSQL services may roll out patched engine versions on their own schedules. Check your provider's bulletin.
If you can't patch yet
Reduce exploitability and blast radius while you schedule patching:
- Tighten network access: ensure PostgreSQL is not directly internet-exposed; limit connectivity to known application subnets and trusted admin networks.
- Credential hygiene: rotate broadly-distributed database credentials (apps, CI/CD, secrets stores); prefer per-service identities and short-lived credentials.
How Wiz can help
Wiz customers can use the pre-built query and advisory in the Wiz Threat Center to surface vulnerable instances in their environment. Wiz also helps prioritize publicly exposed instances of PostgreSQL and identify any instances configured with weak authentication or allowing SQL injection.
Responsible disclosure timeline
- Dec 10-11, 2025: Vulnerability discovered and demonstrated at ZeroDay.Cloud 2025 by Team Xint Code.
- Feb 8, 2026: Fix committed upstream.
- Feb 12, 2026: Fix released across all supported major versions (18.2, 17.8, 16.12, 15.16, 14.21).
- Feb 12, 2026: CVE-2026-2005 published.
Closing thoughts
CVE-2026-2005 is a pattern we see often: a memory corruption bug in a widely trusted component, reachable through authentication paths that attackers commonly obtain in real-world attacks. This vulnerability went unnoticed for 20 years until it was found by Xint Code, an autonomous AI code analysis tool built for deep code analysis and vulnerability discovery.