Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-17349

CVE-2026-17349: pgAdmin 4 Privilege Escalation Vulnerability

CVE-2026-17349 is a privilege escalation flaw in pgAdmin 4 that allows non-owners to inherit admin credentials via adhoc workspace connections. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-17349 Overview

CVE-2026-17349 is a broken access control vulnerability in pgAdmin 4's Workspaces feature. The /misc/workspace/adhoc_connect_server endpoint clones a target server row using Server.clone(), copying every column including user_id, shared, shared_username, password, save_password, and tunnel_password. When a non-owner triggers an adhoc connect against another user's shared server, the clone inherits that user's ownership and stored database credentials verbatim. The non-owner can then open the clone and pgAdmin will connect using the source user's stored password. The flaw affects pgAdmin 4 from version 9.0 before 9.17 [CWE-522].

Critical Impact

A low-privileged pgAdmin user can gain use of another user's stored PostgreSQL credentials, including administrator credentials on shared servers, and inherit whatever database privileges those credentials confer.

Affected Products

  • pgAdmin 4 versions 9.0 through 9.16 (Workspaces feature)
  • Deployments exposing shared server records to multiple pgAdmin users
  • Multi-tenant pgAdmin instances used by administrators alongside standard users

Discovery Timeline

  • 2026-07-31 - CVE-2026-17349 published to NVD
  • 2026-08-05 - Last updated in NVD database

Technical Details for CVE-2026-17349

Vulnerability Analysis

The Workspaces feature introduced in pgAdmin 4 9.0 exposes an endpoint at /misc/workspace/adhoc_connect_server that accepts the id of an existing server and clones it for an ad-hoc connection. The clone is created by Server.clone(), which performs a column-wise copy of the source row. The pre-patch code did not sanitize ownership or credential fields on the resulting row before persisting it. This let a caller create a database server record they owned but that carried another user's stored secrets.

Because pgAdmin writes the cloned row to persistent storage before attempting the connection, the record survives even when the underlying database connection fails. A non-owner can then reopen the cloned server and pgAdmin will authenticate to PostgreSQL using the original owner's stored password on their behalf. This effectively transfers database privileges across tenancy boundaries within pgAdmin.

Root Cause

The root cause is missing ownership and credential scrubbing after cloning a server object. Server.clone() is a full-fidelity copy, and the calling handler in web/pgadmin/misc/workspaces/__init__.py failed to re-home the clone to current_user or clear inherited secret material before commit. This is a classic insufficiently protected credentials weakness combined with broken access control on a write path.

Attack Vector

Exploitation requires authenticated access to pgAdmin as a low-privileged user who can see or reference the id of a shared server owned by another user, typically an administrator. The attacker calls the adhoc connect endpoint with that server id. The resulting persisted server row is owned by the attacker but retains the victim's password, save_password, and tunnel_password. The attacker then connects, and pgAdmin transparently authenticates to the target PostgreSQL instance using the victim's credentials.

python
# Patch: web/pgadmin/misc/workspaces/__init__.py
# Re-home adhoc cloned servers to the calling user and drop inherited secrets

                # Clone the server object
                server = server.clone()

                # Server.clone() copies every column from the source row,
                # including user_id/shared/shared_username. Force the new
                # adhoc record to belong to the current user and be private.
                server.user_id = current_user.id
                server.shared = False
                server.shared_username = None

                # The clone also inherits the source server's stored
                # credentials; drop them so a non-owner can't persist
                # another user's secret material under their own row.
                server.password = None
                server.save_password = False
                server.tunnel_password = None

                # Replace the following with the new/changed value.
                server.maintenance_db = new_db

Source: pgAdmin commit a7e74a6 and pgAdmin commit 64a9cdb.

Detection Methods for CVE-2026-17349

Indicators of Compromise

  • Rows in the pgAdmin config database where a server owned by user A has an identical password or tunnel_password value to a server previously owned by user B.
  • Access log entries showing POSTs to /misc/workspace/adhoc_connect_server from non-administrator accounts referencing server ids owned by administrators.
  • PostgreSQL server logs showing successful logins from a pgAdmin host using an administrator role at times correlated with non-administrator pgAdmin sessions.

Detection Strategies

  • Audit the pgAdmin server table for records where user_id was recently changed and save_password is true, indicating cloned rows that retained credentials.
  • Compare stored credential hashes across pgAdmin server rows owned by different users to identify cross-tenant credential reuse.
  • Instrument the adhoc_connect_server handler with request logging that captures caller identity, source server id, and resulting server row id.

Monitoring Recommendations

  • Forward pgAdmin application logs and PostgreSQL authentication logs to a centralized data lake for correlation between pgAdmin users and database logins.
  • Alert on any authenticated PostgreSQL session where the database role does not match the expected mapping for the originating pgAdmin user.
  • Track new server registrations in pgAdmin per user account and flag spikes that follow visibility of shared administrator servers.

How to Mitigate CVE-2026-17349

Immediate Actions Required

  • Upgrade pgAdmin 4 to version 9.17 or later, which re-homes cloned adhoc server rows and clears inherited credentials.
  • Rotate any PostgreSQL credentials that were stored in shared pgAdmin server definitions while the vulnerable versions were in use.
  • Review the pgAdmin server table and delete any adhoc-cloned rows created between deployment of 9.0 and the upgrade to 9.17.

Patch Information

The fix is delivered in pgAdmin 4 9.17 via commits a7e74a6 and 64a9cdb. The patch forces user_id, shared, shared_username, password, save_password, and tunnel_password on the cloned record to the calling user and cleared/private values before commit, regardless of the source server's state. A regression test asserts the persisted row is owned by the caller, is not shared, and carries none of the source's stored credentials. See the upstream issue for details.

Workarounds

  • Disable the Workspaces feature or restrict access to the /misc/workspace/adhoc_connect_server endpoint at a reverse proxy until the upgrade is applied.
  • Unshare administrator-owned server definitions and require each user to configure their own server connections with their own credentials.
  • Configure pgAdmin server definitions to not store passwords (save_password = false) so that no credential material is available to inherit through the clone path.
bash
# Verify installed pgAdmin 4 version and confirm 9.17 or later
pip show pgadmin4 | grep -i version

# Example reverse-proxy block for the vulnerable endpoint (nginx)
location = /misc/workspace/adhoc_connect_server {
    return 403;
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.