PostgreSQL's 52-CVE release (CVE-2026-6460): update, then find out who can create objects

PostgreSQL released 18.7, 17.12, 16.16, 15.20 and 14.25 on 12 November 2026, fixing 52 vulnerabilities in one go, and 32 of them carry a CVSS score of 8.8. The one getting most attention is CVE-2026-6460, which lets an account that can log in and create objects run arbitrary code as the operating system user the database runs under. Every supported branch from 14 to 18 is affected until it reaches those version numbers.

Update the minor version and restart first, then work out which accounts can create objects. As of 09:50 Taipei time on 13 November, the official announcement mentions no exploitation in the wild and the latest CISA KEV catalogue does not list the CVE. The researcher has already published the technical details, though, and all CVE-2026-6460 needs is an authenticated account that can create objects. Under the default privileges plenty of ordinary accounts qualify, including an application’s own connection account once SQL injection hands it to someone else, so there is no reason to wait for confirmed attacks before patching.

What has to be true

The project classes CVE-2026-6460 as type confusion. In the advisory’s words, the system tracked typed values poorly and the values did not identify their own type, so after a type change a stale value could survive in data dictionary entries, PL/pgSQL variables and elsewhere, and be read as the wrong type. The main route is ALTER ... SET DATA TYPE, and the advisory says other DDL routes exist. It is scored CVSS 3.0 8.8 with the vector AV:N/AC:L/PR:L/UI:N, meaning the attacker has to be authenticated and has to be what the advisory calls an object creator.

Most of the 32 issues scored 8.8 say “executes arbitrary code” in the title. The causes range from type confusion and use-after-free to integer wraparound and heap buffer overflows, in code that includes plpgsql, plperl, pltcl and dblink as well as the full-text search functions. Six of the vulnerabilities affect client tools, among them pg_dump, pg_restore, reindexdb, vacuumdb and pg_basebackup, so backup hosts and CI runners need the update as well as the servers. Only a handful need no login at all. CVE-2026-116322 can be triggered without authentication, scores 5.3 and affects 18 only, and most of the other no-login issues rely on someone restoring a dump or subscribing to data from the attacker.

14.25 is the final release of PostgreSQL 14, and the announcement says 14 is now end-of-life. Anything still on 14 should have a major-version upgrade scheduled once this update is in. 13 and earlier were already out of upstream support and get no fixed release this time; if you pay a vendor for extended support, their advisory is the one to read.

Who counts as an object creator

It is easy to assume that an ordinary account nobody ever granted CREATE to is out of the picture. The PostgreSQL documentation says otherwise: every database grants CONNECT and TEMPORARY to PUBLIC when it is created, so any role that can log in can create temporary tables. From 15 onwards the public schema in a new database no longer lets PUBLIC create objects, but the release notes say upgrades and dump restores keep the old permissions, so a cluster that has been carried forward from 14 or earlier may still have public open.

I checked this on OrbStack with the official images. The test only creates a role and reads the system catalogues; it runs nothing that changes a type and does not reproduce the vulnerability. Each of two fresh clusters gets a role lowpriv with LOGIN and nothing else, and then runs the same query:

select r.rolname,
       has_database_privilege(r.oid, current_database(), 'TEMPORARY') as temp,
       (select string_agg(n.nspname, ',') from pg_namespace n
         where n.nspname !~ '^pg_' and n.nspname <> 'information_schema'
           and has_schema_privilege(r.oid, n.oid, 'CREATE')) as create_in,
       (select count(*) from pg_class c
         where c.relowner = r.oid and c.relkind in ('r','p')) as owned_tables
from pg_roles r
where r.rolcanlogin and not r.rolsuper
order by 1;

Actual output, postgres:18.7 first and postgres:14.25 second:

 rolname | temp | create_in | owned_tables 
---------+------+-----------+--------------
 lowpriv | t    |           |            0

 rolname | temp | create_in | owned_tables 
---------+------+-----------+--------------
 lowpriv | t    | public    |            0

A role that was granted nothing still gets TEMPORARY on both versions, and on 14.25 it can also create objects in public. The query is read-only, so you can run it as is in every database and treat any account with temp true, a value in create_in or owned_tables above 0 as able to create objects. It is a first pass only. It leaves out CREATE at the database level and anything a role picks up through SET ROLE, and owned_tables counts only tables the role owns directly, so an empty result does not mean you are safe.

People forget the application’s own account. Plenty of systems use one account for both migrations and everyday traffic, which makes it the owner of the tables, and ALTER TABLE checks ownership, so revoking CREATE on the schema does not take that ability away. When sizing up a SQL injection bug, people usually think about what that account can do inside the database. Now you also have to count whatever the operating system user on the database host can reach, including the data directory and any other databases on the same machine. Services that let users write their own SQL, such as multi-tenant platforms or reporting tools, go to the front of the queue.

Revoking privileges only narrows the exposure

If you cannot update straight away, you can take back the privileges handed out by default:

revoke temporary on database postgres from public;
revoke create on schema public from public;

After running these, temp turned false on both versions and create_in emptied on 14.25. Both revoke only what was granted to PUBLIC, so grants made directly to a role, or inherited from another role, stay in place. Their scope differs as well: the first changes only the database it names, so writing postgres touches only that one, and the second changes only the public schema of the database you are connected to. You have to repeat them for every database, with the right name and connection each time.

New databases also start from the defaults again. In the test, a database created afterwards had TEMPORARY back on, and on 14.25 public had CREATE again. Revoking in template1 first does carry the public schema change over to new databases, but TEMPORARY still came back, because database-level privileges are not copied from the template, so each new database needs the revoke again. Before you revoke anything, check whether something relies on temporary tables; reporting and batch jobs often do.

Red Hat’s suggested mitigation is along the same lines: restrict network access and take object creation away from untrusted accounts. The official advisory lists no workaround and says there is more than one attack route. I have not tested which of them the revokes block, so treat this as narrowing the exposure while the actual fix remains the update. Over the longer term, split the account that runs migrations from the one the service connects with day to day, and leave the everyday account only what it uses. The next time a bug of this kind turns up, there will be far less to inventory.

Where each provider stands

TargetStatus at time of checking
Upstream PostgreSQL18.7, 17.12, 16.16, 15.20 and 14.25 released on 2026-11-12; 14.25 is the last 14 release
Official Docker imagepostgres:18.7, 17.12 and 14.25 can be pulled; tested version strings include 18.7 (Debian 18.7-1.pgdg13+2)
Debianbookworm 15.19, trixie 17.11 and forky 18.6 still open (unfixed); sid has 18.7-1
Ubuntuarchive still at jammy 14.24, noble 16.15 and resolute 18.6, published 2026-08-20
Red Hatrated Important; the CVE record is still a draft and only lists Hardened Images postgresql17, postgresql18 and libpq as Affected; no RHSA yet, and products it does not list should not be read as unaffected
RDS, Cloud SQL, AlloyDB, Azureversion pages and release notes still stop at the 18.6 round, with no notice of this release

Managed services sometimes backport fixes quietly, so a missing entry only means I could not see one when I checked. Run select version() to see what you are actually on, then compare it with your provider’s notice for this release. For Supabase, Neon and the rest I only searched and found no statement.

Published details, attacks in the wild and KEV

Mehmet Ince, the researcher, has published a series of articles walking through the exploitation of CVE-2026-6460, so the details are out; this post does not link to them. A vulnerability aggregator also lists an entry marked as a PoC, which I have not verified. The latest CISA KEV catalogue is 2026.11.10, released on 2026-11-10 with 1,779 entries, and it does not include CVE-2026-6460. That edition predates the fix by two days, so all it tells you is that the CVE has not been added yet.

Inventory and update

  1. List every PostgreSQL instance, including standbys and replicas, containers, managed instances, and the backup hosts and CI runners that run pg_dump and pg_restore.
  2. Check each one with select version(). The package version only tells you what is installed on disk.
  3. Where a fixed release is available, update the minor version and restart; no dump and restore is needed. The announcement lists some follow-up work: GiST indexes using gist_trgm_ops and B-tree indexes on bpchar columns declared without a length need REINDEX, GIN indexes need one if you suspect corruption, and anyone using incremental backups should take a fresh full backup.
  4. If you use C-language extensions, rebuild them against the new release. The release notes say full protection depends on it, and some extensions need source changes. A new composite_type_mismatch setting can switch these checks off, but the project warns that doing so carries security risks, so it is not a compatibility fix. The same patches also tighten ALTER COLUMN TYPE; for example, it is no longer allowed when a function’s default expression depends on the composite type, so a migration may now fail. Try one in a test environment before updating.
  5. Where no fixed release is available yet, first limit who can reach the database port, then use the query above to find accounts that can create objects and revoke what they do not need.
  6. If you are still on 14, schedule the major-version upgrade.
# What the server is actually running, and the client tools on this machine
psql --no-psqlrc --tuples-only --command 'select version()'
pg_dump --version

Notes

  • The release fixes 52 vulnerabilities, 32 of them scored CVSS 8.8, and CVE-2026-6460 is only one of them.
  • Versions 14 to 18 are affected; the fixed releases are 18.7, 17.12, 16.16, 15.20 and 14.25, and 14 reaches end-of-life with this one.
  • Most of the issues need a login that can create objects, and TEMPORARY is granted to PUBLIC by default.
  • An application account that owns tables counts as an object creator.
  • Revoking TEMPORARY and schema CREATE has to be done per database, and new databases get TEMPORARY back, so it only narrows the exposure.
  • The client tools are on the list as well, so update backup hosts and CI, and rebuild C-language extensions.

Further reading

Ideas and technical judgement by Sheng; drafted with Claude · reviewed by Codex and Grok; privilege checks run on OrbStack with postgres:18.7 and postgres:14.25, vulnerability not reproduced; facts checked up to 09:50 on 13 November 2026 (Taipei time)