Connect with us

NEWS

PostgreSQL’s pgcrypto Decrypt Path Now Ends in a Shell

A public PoC for CVE-2026-2005 turns PostgreSQL’s trusted pgcrypto PGP parser into a superuser shell through COPY FROM PROGRAM, a built-in host command.

Published

on

A public proof-of-concept exploit for CVE-2026-2005 turns PostgreSQL’s pgcrypto decrypt path into operating-system command execution as the database user. The overflow sits in OpenPGP session-key parsing that has shipped since 2005. Security researcher Varik Matevosyan, posting as var77, put a working GitHub chain on the internet that raises the session to superuser and then runs a host command.

The PostgreSQL Global Development Group already shipped fixes on February 12, 2026. The PoC does not invent a new bug. It shows how a logged-in role, a trusted crypto extension, and a first-party COPY feature finish the job together.

Public Proof of Concept for a 20-Year Overflow

The CVE-2026-2005 security advisory is blunt. A heap buffer overflow in pgcrypto lets a ciphertext provider run code as the operating-system user that runs the database. The CVE record scoring 8.8 uses CVSS 3.1 vector AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. Privileges required are Low, not None. Someone still has to log in, steal an application role, or land SQL injection first.

Team Xint Code found the bug with the Xint Code analysis tool at ZeroDay.Cloud 2025 in London on December 10 and 11, 2025, and showed a live exploit there with the Wiz Research Team. The project credited that team when the CVE went public. Matevosyan’s repo walks the same path in Python, using psycopg2, pwntools, and pycryptodome, and it is built against one vulnerable postgres binary.

THE DISCLOSURE CALENDAR

  1. December 10-11, 2025: Team Xint Code demonstrates the overflow at ZeroDay.Cloud 2025 in London.
  2. February 8, 2026: The upstream bounds-check fix commit lands in pgp-pubdec.c.
  3. February 12, 2026: PostgreSQL 18.2, 17.8, 16.12, 15.16, and 14.21 ship, and CVE-2026-2005 is published.
  4. May 4, 2026: The finders publish the full technical write-up.
  5. July 23, 2026: A second public repo frames the same overflow as a lab SQL PoC.

That calendar is why a “20-year-old RCE” headline and a “patches have been out since February” briefing can both be true. The code was old. The working public recipes arrived after the minors.

pgcrypto Installs Without Superuser Rights

The official trusted pgcrypto extension documentation says the module can be installed by non-superusers who have CREATE privilege on the current database. That is the design, not a misconfig. Hashing, password crypt, PGP, and random bytes all ride in the same extension, so teams load it for bcrypt or gen_random_uuid() and inherit the OpenPGP parser too.

The finders’ reach test is two statements. Create the extension if it is missing, then call pgp_pub_decrypt_bytea() with attacker-controlled bytea. No superuser grant is required to get onto that path. Application roles often already have CREATE on the app database, and SQL injection is enough to issue both calls.

WHAT AN ATTACKER NEEDS ON THE WIRE

  • A login: CVSS models this as a low-privilege network attacker, typically stolen app credentials or an injectable query.
  • CREATE on a database: that right is enough to install pgcrypto when it is not loaded yet.
  • EXECUTE on decrypt: the public-key function pgp_pub_decrypt_bytea() is the documented entry to the overflow.
  • A crafted PGP message: the session-key packet is the input that overruns the fixed buffer.

Most shops that only call digest() or crypt() never touch that decrypt function. They still have to care if any role can create the extension and then call it, because the parser does not check who “should” be doing PGP.

The Session Key Buffer Holds Only 32 Bytes

The overflow is in pgp_parse_pubenc_sesskey() in contrib/pgcrypto/pgp-pubdec.c. After RSA or ElGamal unwrap of a public-key encrypted session key, the code takes a length from the decrypted message and copies that many bytes into ctx->sess_key. That buffer is bounded at PGP_MAX_KEY, which is 32 bytes (256 bits / 8). There was no check against that cap.

PGP_MAX_KEY

  • The cap: 32 bytes, the maximum session key the PGP context was built to hold.
  • The miss: length came from the decrypted packet, so a long RSA payload could push the copy into the hundreds of bytes.
  • The neighbor: the PGP context sits on the heap beside MBuf input and output structs whose pointers become the leak and write primitives.

OpenPGP public-key and symmetric functions were added in PostgreSQL 8.1 in 2005. pgcrypto itself is older; hashing landed in 7.1. The 20-year clock on this CVE is the PGP parser, not the first digest() commit. PostgreSQL 8.1 was meant to let the database speak a slice of RFC 4880. It also imported C buffer math that nobody fuzzed like a network daemon.

The finders’ heap overflow write-up is the clean public map of what comes next, without needing a second leak of offsets. A first call corrupts an MBuf so decrypt returns a window of heap that contains PIE pointers. A later call on a new connection, which inherits the same postmaster ASLR layout, turns that into an arbitrary write. The write does not have to smash a function pointer. It only has to change who the backend thinks you are.

Why COPY FROM PROGRAM Ends in a Shell

PostgreSQL keeps the current role in a global called CurrentUserId. The public PoCs overwrite it with 10, the bootstrap superuser OID. After that, the session is a superuser for the rest of the connection. Superuser is allowed to run COPY… PROGRAM, which starts a host command as the postgres operating-system user.

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.

Team Xint Code, ZeroDay.Cloud write-up

That last step is not a second vulnerability. COPY FROM PROGRAM and COPY TO PROGRAM are documented superuser tools. The overflow’s job is to steal the right that already owns them. Once the backend is running attacker code, every database on the instance, every secret in a table, and every network hop the postgres account can reach is in play.

Managed and shared hosts feel this more than a locked-down single-tenant box. The database process is the security boundary. A valid login plus pgcrypto becomes a way off that boundary and onto the host.

February’s Minors Fixed CVE-2026-2005 Across Five Versions

On February 12, 2026 the project released PostgreSQL 18.2, 17.8, 16.12, 15.16, and 14.21. The announce mail said that drop fixed five security vulnerabilities and more than 65 other bugs. CVE-2026-2005 is one of those five. The same builds also close CVE-2026-2006, a missing multibyte-length check in text handling that the project also scored 8.8, reported by Paul Gerste and Moritz Sanft of Team Bugz Bunnies at the same London event. CVE-2026-2004, an intarray issue reported by Daniel Firer, rode along too.

PostgreSQL Versions Patched for CVE-2026-2005

Every supported major from 14 through 18 was affected until the February 12 minors. If a host is still on 18.1, 17.7, or any older minor in those lines, it is on the unpatched side of this table.

PATCHED MINORS FOR CVE-2026-2005

Major line Vulnerable before Fixed in Fix published
18 18.2 18.2 February 12, 2026
17 17.8 17.8 February 12, 2026
16 16.12 16.12 February 12, 2026
15 15.16 15.16 February 12, 2026
14 14.21 14.21 February 12, 2026

Cloud engines apply those minors on their own clocks. The finders told operators to check the provider bulletin rather than assume a managed cluster moved on February 12. If a patch window is still closed, the write-up’s stop-gaps are ugly and simple: keep Postgres off the open internet, shrink who can connect, and rotate the application passwords that are sitting in CI and env files.

Matching Binaries Still Limit Mass Exploitation

Matevosyan’s README is honest about the lab. The exploit matches leaked pointers to ELF symbol offsets from a postgres binary built at commit 4b324845. A different compile, a different minor, or a distro package with other flags will move those offsets and the vote on PIE base will fail. Debug builds with assertions can abort before the freelist write. That is a real speed bump for a worm that wants to spray the internet.

It is a much smaller bump for someone who already has SQL on a known image. Forked backends share the postmaster’s ASLR, so a leak on one connection is good on the next. Operators who pin a vendor AMI, a container tag, or an internal package already handed the attacker the binary identity the first PoC wants.

A second GitHub tree circulated in July 2026 and described the overflow as remote code execution from a single SQL query against a lab container. Pentest feeds saved it and moved on; there was no public argument in the thread, only a bookmark. That is the shift the February advisory did not have to live with. The bug is no longer a conference demo. It is a recipe other people can clone, even if many clones still die on a mismatched build.

Wiz Clocked Postgres in 80 Percent of Clouds

Wiz’s scan, quoted in the finders’ post, put PostgreSQL in 80 percent of cloud environments and found 45 percent of those environments exposing PostgreSQL directly to the internet. Those are two different cuts. The first is how common the engine is. The second is how often the port is reachable without a private network in front of it.

EXPOSURE SNAPSHOT FROM THE DISCLOSURE

  • 80 percent: share of cloud environments in the Wiz cut that run PostgreSQL.
  • 45 percent: share of those environments that expose PostgreSQL to the internet.
  • 8.8: CVSS 3.1 base score, High, with Low privileges required.
  • CREATE: the documented right that lets a non-superuser load pgcrypto.

Internet-exposed Postgres with loose roles is the ugly case: stolen credentials become host code. Internal-only instances are still in the high pile, because app passwords leak from config maps and because one injectable parameter is authenticated SQL. The overflow does not need a pre-auth packet to port 5432. It needs a session that can call decrypt.

pgcrypto was not done with the project in February. On August 13, 2026, CVE-2026-14663 landed in 18.6, 17.11, 16.15, 15.19, and 14.24. That one is a 6.5, not a shell: disabled OpenSSL ciphers could store and return cleartext. It is a reminder that the same contrib module still carries OpenPGP packet code next to the hashes most people actually wanted.

Frequently Asked Questions

Which PostgreSQL versions are affected by CVE-2026-2005?

The advisory covers supported majors 14 through 18 and lists no patched builds for 13 and older, which the current documentation already marks as unsupported. A 13 cluster can still be running the old parser, but it will not get 14.21-style community minors; the upgrade path is a major move or a vendor extended-support build.

Do you need the pgcrypto extension loaded for CVE-2026-2005?

Yes. The overflow is in the OpenPGP public-key decrypt path behind pgp_pub_decrypt_bytea(), not in digest(), hmac(), or crypt(). A database that never created the extension, and whose roles cannot create it, is off this CVE’s path even if the postgres binary is an unpatched minor.

What does ciphertext provider mean in the PostgreSQL advisory?

It means whoever can feed ciphertext into pgcrypto’s decrypt functions. CVSS encodes that as a logged-in, low-privilege role (PR:L), which includes an application user whose query is injectable. It is not an unauthenticated worm that only needs a reachable 5432 port and no password.

How is CVE-2026-2006 different from CVE-2026-2005?

CVE-2026-2006 is missing validation of multibyte character length in text manipulation, also scored 8.8, and it shipped in the same February 12, 2026 minors. Paul Gerste and Moritz Sanft reported it from the London event on December 11, 2025, one day after the pgcrypto overflow demo, as a separate RCE with a different root cause.

Hosts still on the February-vulnerable minors now face public PoCs, a documented CREATE shortcut onto pgcrypto, and a superuser command that was always supposed to start a program. The August cleartext fix did not change that math.

Harry is the editor of Oton Technology, an independent site he owns and edits, covering the part of technology that people actually have to act on. After ten years in journalism, first reporting and then editing, he works from primary material by habit: the advisory rather than the write up of it, the filing rather than the press release, the changelog rather than the launch video. Every figure in an article carries its source and its date, and where a number comes from a vendor or an analyst model rather than a count, he says so plainly instead of letting it stand as established fact. What he leaves out is anything he could not verify himself, which on a beat full of unnamed supply chain claims removes a great deal. That standard applies across all the sections the site publishes for an international audience, from artificial intelligence and security to phones, computers, gaming, crypto and the software businesses depend on. He corrects errors in the open and labels them, because a site that hides its mistakes is asking readers to trust the rest on nothing.

Click to comment

Leave a Reply

Your email address will not be published. Required fields are marked *

Trending