On February 13, 2025, the PostgreSQL Global Development Group published a security release covering CVE-2025-1094, a SQL injection vulnerability in the psql interactive terminal client and in the libpq PQescapeString* family of functions when handling untrusted input under specific encodings. The CVSS v3.1 base score is 8.1 (vector AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H). Rapid7 discovered the issue in the course of analyzing CVE-2024-12356 in BeyondTrust Privileged Remote Access, where the BeyondTrust vendor's mitigation was incomplete because the underlying root cause lived in libpq. The PostgreSQL fix is therefore the canonical remediation — and any application that processes user-controlled SQL strings through libpq needs the patched library, not just a patched psql binary.
What does the vendor advisory say?
PostgreSQL's advisory describes CVE-2025-1094 as improper neutralization of quoting syntax when invalid UTF-8 byte sequences are passed to PQescapeLiteral(), PQescapeIdentifier(), PQescapeString(), or PQescapeStringConn(). Under those specific conditions, the library can produce output that the server interprets as a SQL command separator, enabling an attacker who controls one parameter in an otherwise-parameterized query to inject additional SQL. The advisory clarifies that this is not a flaw in the SQL grammar itself but a multi-byte string-handling bug in the escaping helpers. The recommended fix is to upgrade to the patched libpq AND to the patched server, because the escaping logic lives client-side but the server processes the resulting injection. The PostgreSQL team also recommends migrating any remaining PQescapeString usage to parameterized queries via PQexecParams, which is unaffected.
Which versions are affected and which are patched?
Affected: PostgreSQL 17.0 through 17.2; 16.0 through 16.6; 15.0 through 15.10; 14.0 through 14.15; 13.0 through 13.18. Patched releases shipped February 13, 2025:
- PostgreSQL 17.3
- PostgreSQL 16.7
- PostgreSQL 15.11
- PostgreSQL 14.16
- PostgreSQL 13.19
PostgreSQL 12 reached end-of-life on November 14, 2024 and will not receive a fix — migrate to 13.19 minimum. Verify the running version on each cluster with SELECT version() from psql and the bundled libpq version with pg_config --version. Note that on Linux systems, the database server, the psql CLI, and libpq are often packaged separately; defenders must patch all three. Common downstream distribution builds:
- Red Hat Enterprise Linux 9: postgresql-server-15.11-1.el9_5 via RHSA-2025:1487
- Debian 12: postgresql-15 15.11-0+deb12u1
- Ubuntu 24.04 LTS: postgresql-16 16.7-0ubuntu0.24.04.1
- AWS RDS: PostgreSQL 16.7 available February 26, 2025; AWS auto-applies during the next maintenance window unless customers opt out
- Google Cloud SQL: Postgres 16.7 available March 1, 2025
- Azure Database for PostgreSQL: 16.7 rolled out beginning March 3, 2025
Is it in CISA KEV and what is the EPSS score?
CVE-2025-1094 is not in CISA KEV. EPSS at publication was 0.31 — moderate, reflecting the user-interaction requirement and the encoding-specific trigger. The reason this CVE is still serious for defenders is the BeyondTrust connection: CVE-2024-12356 was in CISA KEV (added December 19, 2024) and was the subject of multiple federal advisories about state-sponsored exploitation against the U.S. Treasury Department. The Rapid7 disclosure that CVE-2025-1094 was the deeper root cause means any organization that patched only the BeyondTrust product without also patching libpq downstream still has a residual risk.
How do you find vulnerable instances in your SBOM?
PostgreSQL appears in SBOMs as both a server binary and a library dependency. Many language runtimes statically link or bundle libpq, particularly in container images. Safeguard saved query:
# Identify PostgreSQL servers and libpq library copies on affected versions
safeguard scan --cve CVE-2025-1094 --component "pkg:generic/postgresql" \
--component "pkg:generic/libpq"
# Surface ORMs and language drivers that bundle libpq statically
safeguard sboms query \
--component "pkg:pypi/psycopg2-binary@<2.9.10" \
--component "pkg:gem/pg@<1.5.8" \
--component "pkg:npm/pg-native@<3.2.0"
For shops without SBOM tooling, the lightest enumeration is psql --version and pg_config --version on every host, plus a dependency audit of every application's language-specific PostgreSQL driver. Container images frequently ship an older libpq baked into the layer; rebuilding the image is required even if the application code is unchanged.
What is the recommended patch rollout?
PostgreSQL recommends an in-place minor-version upgrade, which is binary-compatible and requires no pg_upgrade data migration. Standard sequence for a single primary:
- Take a logical backup with
pg_dump --format=custom --verbose --file=$BACKUP_DIR/$DBNAME.dump $DBNAMEper critical database. - Snapshot the underlying volume if using cloud storage.
- Stop the PostgreSQL service:
systemctl stop postgresql-16. - Upgrade the packages:
yum update postgresql16-server postgresql16-libs(or equivalent apt-get/zypper invocation). - Restart:
systemctl start postgresql-16. - Verify version:
psql -U postgres -c 'SELECT version()'.
For streaming replication (primary + replicas), upgrade replicas first, then perform a planned failover, then upgrade the demoted primary. The PostgreSQL minor-version policy ensures wire-protocol compatibility, so brief mixed-version operation is safe. For managed services, set the maintenance window to apply the patch promptly and verify with the cloud provider's console after the rolling restart completes.
Compensating control while patching: ensure all application queries use parameterized statements via PQexecParams or the language driver's equivalent (cursor.execute(query, params) in Python, client.query(query, params) in Node pg). Code that uses PQescapeString directly should be refactored regardless of the patch — the function has been deprecated guidance for years.
What detections does the vendor or CISA publish?
PostgreSQL does not publish network signatures because the bug is in the client-side escaping helper. Defenders should instead monitor the server for anomalous query patterns suggestive of injection — multi-statement queries from application service accounts, unexpected ; separators in logged SQL, and DDL operations from accounts that should only run DML. Splunk's official PostgreSQL TA shipped a saved search for this on February 18, 2025:
# Source: PostgreSQL community detection guidance, 2025-02-18
title: PostgreSQL Multi-Statement Query From Application Account
status: stable
logsource:
product: postgresql
service: query_log
detection:
selection:
user_name|startswith:
- 'app_'
- 'svc_'
query|contains: ';'
query|re: '.*;\s*(DROP|CREATE|ALTER|GRANT|REVOKE)\s.*'
condition: selection
fields:
- user_name
- client_addr
- query
- database
level: high
Enable log_statement = 'mod' and log_min_duration_statement = 0 temporarily during the patching window to ensure the detection has source data.
A subtle gotcha for application teams: prepared_statement mode in connection poolers like PgBouncer running in transaction-pool mode does not protect against CVE-2025-1094 because the escaping happens client-side in libpq before the statement reaches the pooler. The pooler is a transport optimization, not a security boundary, for this class of bug. The only application-side defense is genuine parameterization via PQexecParams or driver-level prepared statements, not string interpolation that uses PQescapeString as a substitute.
For analytical workloads that pull PostgreSQL data through ETL frameworks (Airflow, Dagster, dbt, Fivetran), audit the connection drivers each framework bundles. Several popular dbt adapters statically link an older libpq; bumping the adapter version is the cleanest way to inherit the fix.
How Safeguard Helps
Safeguard's SBOM matcher resolves PostgreSQL across all packaging ecosystems (pkg:deb, pkg:rpm, pkg:apk, pkg:generic, plus language-specific drivers in pkg:pypi, pkg:gem, pkg:npm, pkg:cargo, pkg:maven) so the same CVE-2025-1094 query catches both server installations and library copies bundled inside application containers. Policy gates fail CI promotions of any artifact still matching the CVE, and VEX statements from PostgreSQL Global Development Group and Rapid7 are auto-ingested when application configurations use parameterized queries exclusively. Griffin AI scores PostgreSQL clusters by exposure (internet, partner-VPN, internal-only), authentication mode (md5, scram-sha-256, peer), and data sensitivity from the data-classification overlay, surfacing the highest-blast-radius databases first. The remediation engine generates per-cluster runbooks for the minor-version upgrade, including the logical-backup command pre-flight, the package update for the specific distro, and a post-flight SELECT version() verification — closing the loop between SBOM detection and verified database fix.