Dev News Daily ENDE
Security

Four CVEs in pgAdmin let a request choose which database server your password goes to

Version 9.18 of pgAdmin 4, published on 17 September, carries 29 fixes, four of them security issues numbered CVE-2026-86861 to CVE-2026-86864. Read together they describe one theme rather than four unrelated bugs: a value the caller supplies was trusted by code that then talked to something else on the user's behalf.

The announcement describes an identity asserted in an HTTP header being accepted for any account, including an administrator's, with no credential — now opt-in, limited to configured trusted proxies (CVE-2026-86863). Two more are the same injection in two tools: a database name handed to pg_dump as a bare positional argument, where a leading dash turns it into further options such as --file, and the same field reaching --dbname in Restore and Maintenance, where it can redirect the connection — and the exported PGPASSWORD — to a server the caller picks (CVE-2026-86864, CVE-2026-86862). The fourth is a time-of-check to time-of-use hole in the File Manager's save endpoint, where a symlink planted between the permission check and the open() wrote outside the user's storage directory (CVE-2026-86861).

What it means

Upgrade, but the more useful takeaway is where these bugs live. None of them is in PostgreSQL. They are in the admin tool that sits in front of it — the piece that runs with a database administrator's rights, is exposed on an internal port because "it's internal", and gets patched on whatever schedule the server does not.

Three things worth doing this week:

  • Find the pgAdmin instances nobody owns. Every organisation has one on a jump box, put there for a migration in a previous year. An authentication bypass by header is precisely the bug that turns such a box into a database console.
  • Check whether anything in front of it sets identity headers. A proxy that adds a trusted header and a tool that accepts it unconditionally is the pair that makes the first CVE exploitable; each half is defensible alone.
  • Assume the password moves. The two injections do not read data — they send your credential somewhere. Rotate the accounts pgAdmin uses rather than only patching, because patching fixes the path and does nothing about what already travelled it.

And the design lesson, which outlives this release: the reason the injections were possible is that a string went into an argument vector and a connection string. The fix was not better escaping, it was moving the value to PGDATABASE, an environment variable libpq never expands. When user input must reach a subprocess, the win is finding a channel that cannot be interpreted — not sanitising one that can.

Four CVEs in pgAdmin let a request choose which database server your password goes to
Four CVEs in pgAdmin let a request choose which database server your password goes to — Dev News Daily

The pattern behind all four

Three of these four are one bug wearing different clothes: a value from the request ends up in a place where syntax has meaning. In the argument vector of a subprocess, a leading dash is syntax. In a connection string, a semicolon is syntax. In a path, a symlink is syntax the filesystem resolves for you.

The fourth — the time-of-check to time-of-use hole — is the other classic: the state you validated and the state you acted on are not the same state, because something changed in between. Checking a path and then opening it by name is the canonical example; the fix is to hold a handle rather than a name.

What this means for tools you build in-house

Admin tools written for "just us" concentrate privilege by design, and they are exactly where these bugs survive longest, because nobody threat-models an internal page. Two habits pay for themselves: pass user input to subprocesses through the environment or a file rather than the command line, and make the tool authenticate users itself rather than believing a header something upstream set.

Primary source
PostgreSQL — pgAdmin 4 v9.18 Released
https://www.postgresql.org/about/news/pgadmin-4-v918-released-3381/