CVE-2026-2005 is a heap buffer overflow in the OpenPGP code path of pgcrypto. Feeding crafted ciphertext to pgp_sym_decrypt or its siblings allows arbitrary code execution as the operating system user the database is running as. The bug has been there since approximately 2005. Twenty years of pgcrypto shipping in every PostgreSQL release. Twenty years of “battle-tested” extension code. Twenty years of nobody finding it until the Wiz ZeroDay.Cloud event in December 2025 produced a working exploit and the disclosure landed last week.
This is the kind of bug that’s worth thinking about for a minute, because the comfortable lessons are wrong.
The threat model people had
The implicit threat model for pgcrypto was: the application controls what gets passed to pgp_sym_decrypt. The application is the trust boundary. If the attacker can influence what the application decrypts, that’s an application bug, not a database bug.
That’s a reasonable threat model right up until you actually look at how pgcrypto is used. The most common usage I see in the wild is column-level encryption: the application stores encrypted blobs and decrypts them on read, with the encrypted blob having come from somewhere — sometimes the application itself, sometimes a third party, sometimes a customer upload, sometimes a JWT-adjacent token format that was always going to involve attacker-controlled bytes. “The application controls the ciphertext” turns out to mean “the application accepts the ciphertext from whoever sent it.” Same difference, eventually.
The other common usage is for credential storage where the encryption key lives in a Postgres GUC or a function definition. In that case the ciphertext column is the easiest thing in the world for an attacker who got SQL injection to write to. They don’t need the key. They need a buffer overflow in the decryption path. That’s exactly what they got.
Why it lasted
Twenty years is a long time. The honest answer is that the OpenPGP code in pgcrypto is a port of a 2002-era subset of the OpenPGP spec, written in C, with a lot of explicit buffer arithmetic, and it’s not exactly anyone’s favorite code to maintain. It is exercised by a relatively small fraction of pgcrypto users — most people use digest() and crypt(), not pgp_sym_decrypt. It does not get the kind of fuzzing attention that the parser or the executor get. It’s contrib. And “contrib” in PostgreSQL has always been a slightly awkward category: it ships with the core distribution, it’s installed by default in most distros, but it doesn’t carry quite the same expectations of scrutiny as src/backend.
None of that excuses the bug. It explains the longevity.
The lesson that isn’t “audit your code more”
Auditing your code more is good advice in the same sense that flossing is good advice. It is not actually what changes outcomes.
The lesson worth taking from this is more boring: do not do crypto in the database if you can do it anywhere else. The reasons aren’t new — application-layer crypto gives you better key isolation, better key rotation, better algorithm agility, and a dramatically smaller attack surface. CVE-2026-2005 adds one more reason: the database does not love crypto code, the database does not fuzz crypto code, and the database runs that crypto code as the postgres OS user. A buffer overflow in pgcrypto is a shell on your database host. A buffer overflow in your application’s libsodium binding is a shell on a stateless app server.
If you’re using pgcrypto for column-level encryption today, the upgrade path is:
- Upgrade to 18.3 / 17.9 / 16.13 / 15.17 / 14.22 (released February 26, 2026). Today. Not next maintenance window. Today.
- Audit which functions are being called and on what input.
- Plan the migration to application-layer crypto. You don’t have to ship it next quarter. You should know what shipping it would look like.
If you’re using pgcrypto for things that aren’t OpenPGP — digest, crypt, HMAC, the random-bytes functions — you’re fine. The CVE is in the PGP code path specifically.
The broader thought
The thing that bothers me about this bug isn’t that it existed. C code from 2002 has bugs. The thing that bothers me is the confidence with which I, and a lot of other people, would have told you in November 2025 that pgcrypto was fine. It had been fine for twenty years. It was fine until it wasn’t.
The next twenty-year pgcrypto-shaped bug is sitting in code right now. It is in code I have looked at and approved. Statistically, some of it is in code I wrote.
Upgrade.