On May 4, ZeroDay.Cloud published the technical writeups for CVE-2026-2005 and CVE-2026-2006. Both are remote code execution bugs in pgcrypto. Both have been in the tree since pgcrypto was first contributed in 2005.
That’s the headline. It deserves a beat to land.
CVE-2026-2005 is a 32-byte heap overflow in pgp_parse_pubenc_sesskey(). The code decrypts an RSA or ElGamal session-key payload from a PGP message and copies the result into a fixed-size buffer (ctx->sess_key, bounded at 32 bytes) with no bounds check at all. Hand the function a PGP message with a larger session key and you get arbitrary writes onto the backend heap. The path to RCE inside the database process is mechanical from there. CVE-2026-2006 is an adjacent encoding-validation bug that arrives at the same destination by a slightly different door. The fixes shipped in the February minors — 18.2, 17.8, 16.12, 15.16, 14.21 — so if you are current, you are patched. If you are still on 18.0 or 17.7 or older, this is your reminder.
Two things about this disclosure are more interesting than the bugs themselves.
Who Can Reach the Vulnerable Function
The attack precondition is “a logged-in user with EXECUTE privilege on pgp_pub_decrypt_bytea().” That sounds restrictive until you remember how pgcrypto is actually deployed. RDS pre-loads it. A long list of application frameworks assume it is there. The default grant is EXECUTE ... TO PUBLIC. In practice, “any authenticated role” means “any successful SQL injection in the application tier.” The vulnerability surface is materially wider than the CVE text implies.
If you cannot patch this week — and you should patch this week — at least REVOKE EXECUTE ON FUNCTION pgp_pub_decrypt_bytea(...) FROM PUBLIC and grant it explicitly to the roles that actually need it. (You did not need it. Almost no one needs it.)
How the Bug Was Found
Both pgcrypto CVEs came out of December’s ZeroDay.Cloud event, where Team Xint Code and Team Bugz Bunnies were both running AI-assisted fuzzers against PostgreSQL. The bugs themselves are not exotic. A missing bounds check and a missing validation. A patient human auditor would have caught either one.
No human auditor did, for twenty years. A weekend of LLM-guided fuzzing did.
We are about to get a lot more of these. PostgreSQL has a defensible amount of code dating to the 1990s and early 2000s, written in a different era of memory-safety paranoia, and the cost of running automated adversarial analysis against it just dropped by something like two orders of magnitude. Some of the resulting CVEs will be in extensions nobody loves. Some will be in src/backend/. None of them will be a surprise.
The Real Question
pgcrypto lives on the default allowlist for every managed Postgres service I am aware of. It is, charitably, a museum of 1990s cryptography practices that happens to live inside a database. Symmetric block ciphers and PGP message parsing in a Postgres extension are useful in approximately three deployments worldwide, and the rest of us are paying the security tax for those three.
I’ve often referred to pgcrypto as the most popular least useful extension in contrib/. You almost always want to do the encryption application-side (although that’s a topic for another post).
If you have an extension allowlist policy, this is the moment to look at it again. pgcrypto did not earn its slot.