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

> PostgreSQL 18.7, 17.12, 16.16, 15.20 and 14.25 fix 52 vulnerabilities, 32 of them scored CVSS 8.8. Most need a login that can create objects. Update first, then work out which accounts can.

Source: https://sheng.page/en/posts/postgresql-cve-2026-6460-what-to-check/ · Published: 2026-11-13 · Author: Sheng

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:

```sql
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:

```text
 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:

```sql
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

| Target | Status at time of checking |
| --- | --- |
| Upstream PostgreSQL | 18.7, 17.12, 16.16, 15.20 and 14.25 released on 2026-11-12; 14.25 is the last 14 release |
| Official Docker image | `postgres:18.7`, `17.12` and `14.25` can be pulled; tested version strings include 18.7 (Debian 18.7-1.pgdg13+2) |
| Debian | bookworm 15.19, trixie 17.11 and forky 18.6 still open (unfixed); sid has 18.7-1 |
| Ubuntu | archive still at jammy 14.24, noble 16.15 and resolute 18.6, published 2026-08-20 |
| Red Hat | rated 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, Azure | version 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.

```sh
# 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

- [PostgreSQL 18.7, 17.12, 16.16, 15.20, 14.25 release announcement](https://www.postgresql.org/about/news/postgresql-187-1712-1616-1520-and-1425-released-3442/): the project's announcement, a primary source and the reference for the vulnerability count, the end of 14 and the post-update steps.
- [PostgreSQL 18.7 release notes](https://www.postgresql.org/docs/release/18.7/): official release notes, a primary source for the extension rebuild and the `ALTER COLUMN TYPE` change; 17.12, 14.25 and the other releases have their own pages.
- [CVE-2026-6460](https://www.postgresql.org/support/security/CVE-2026-6460/): the project's security advisory, a primary source with the description and CVSS.
- [PostgreSQL Security Information](https://www.postgresql.org/support/security/): the project's CVE table, a primary source listing versions and scores for all 52.
- [Privileges](https://www.postgresql.org/docs/current/ddl-priv.html): official documentation, a primary source on each privilege and the defaults granted to PUBLIC.
- [PostgreSQL 15 release notes](https://www.postgresql.org/docs/release/15.0/): official release notes, a primary source for the change to public schema defaults.
- [Versioning Policy](https://www.postgresql.org/support/versioning/): the project's support policy, a primary source for each major version's final release date.
- [Red Hat Security Data API: CVE-2026-6460](https://access.redhat.com/hydra/rest/securitydata/cve/CVE-2026-6460.json): Red Hat's vendor data, still a draft when checked.
- [Debian security tracker CVE-2026-6460](https://security-tracker.debian.org/tracker/CVE-2026-6460): Debian's official tracker, a primary source for per-release status.
- [Launchpad postgresql-16](https://launchpad.net/ubuntu/+source/postgresql-16): Ubuntu's package publishing history, a primary source; swap the package name for other major versions.
- [CISA Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog): CISA's official list, a primary source for vulnerabilities confirmed as exploited.
- [Type safety (Wikipedia)](https://en.wikipedia.org/wiki/Type_safety): an encyclopaedia entry, background on type errors only.

> 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)