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

CVE-2026-17347: pgAdmin 4 RCE Vulnerability

CVE-2026-17347 is a remote code execution vulnerability in pgAdmin 4 that allows authenticated users to execute arbitrary commands via shell metacharacters. This post covers technical details, affected versions, and mitigation steps.

Published:

CVE-2026-17347 Overview

CVE-2026-17347 is an OS command injection vulnerability [CWE-78] in pgAdmin 4, affecting versions from 7.2 before 9.17. The flaw resides in the MASTER_PASSWORD_HOOK feature, which invokes an administrator-configured external command to retrieve per-user encryption keys. The previous implementation substituted the username into the command string and executed the result with subprocess.Popen(..., shell=True). When the username originates from an external authentication source such as OAuth/OIDC, Kerberos, or webserver authentication, an authenticated user with a username containing shell metacharacters can execute arbitrary commands as the pgAdmin service account.

Critical Impact

Authenticated users can achieve remote code execution as the pgAdmin service account in deployments where the configured MASTER_PASSWORD_HOOK uses the %u username placeholder.

Affected Products

  • pgAdmin 4 versions 7.2 through 9.16
  • Deployments using external authentication (OAuth/OIDC, Kerberos, webserver auth)
  • Any pgAdmin 4 instance with MASTER_PASSWORD_HOOK configured using %u

Discovery Timeline

  • 2026-07-31 - CVE-2026-17347 published to NVD
  • 2026-08-04 - Last updated in NVD database

Technical Details for CVE-2026-17347

Vulnerability Analysis

The MASTER_PASSWORD_HOOK setting was introduced in pgAdmin 4 version 7.2 to let administrators configure an external command that returns a per-user encryption key. The configured command string may contain the %u token, which pgAdmin replaces with the current user's name before execution. The vulnerable implementation performed this substitution against the raw command string and then executed the result via a shell subprocess, treating the entire string as shell input.

When pgAdmin is integrated with an external identity provider, the authenticated username is derived from OAuth/OIDC claims, Kerberos principals, or headers passed by a fronting web server. These sources can contain characters that pgAdmin does not sanitize. A username containing ;, $(), backticks, pipes, &&, or newlines is interpreted by the shell as command separators or substitution operators, allowing arbitrary command execution under the pgAdmin process identity.

Root Cause

The root cause is unsafe command construction in get_master_password_from_master_hook() within web/pgadmin/utils/master_password.py. The function concatenated an untrusted username into a shell command string and passed it to subprocess.Popen with shell=True, violating the principle of keeping data separate from code.

Attack Vector

An attacker authenticates to pgAdmin through the configured external identity provider using a username that contains shell metacharacters. When pgAdmin invokes the master password hook for that session, the shell interprets the metacharacters and executes the injected commands as the pgAdmin service account.

python
# Vulnerable implementation (pre-patch)
cmd = config.MASTER_PASSWORD_HOOK
command = cmd.replace('%u', current_user.username) \
    if '%u' in cmd else cmd
p = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)

# Patched implementation
import shlex
import subprocess
cmd = config.MASTER_PASSWORD_HOOK
if not cmd:
    return None
# Tokenise the trusted hook string first, then substitute the
# untrusted username into individual argv elements, execute shell=False

Source: GitHub Commit ea7e798

Detection Methods for CVE-2026-17347

Indicators of Compromise

  • Child processes spawned by the pgAdmin service account that do not match the configured hook script path.
  • User accounts in the connected identity provider whose usernames contain ;, $(), backticks, |, &, or newline characters.
  • Unexpected outbound network connections or file writes originating from the pgAdmin process shortly after user authentication events.

Detection Strategies

  • Audit pgAdmin authentication logs and correlate login events with process-creation telemetry for the pgAdmin service account.
  • Compare running pgAdmin versions against the fixed release (9.17 or later) using software inventory data.
  • Review the configured MASTER_PASSWORD_HOOK value and identify deployments where it contains %u.

Monitoring Recommendations

  • Alert on shell interpreter invocations (/bin/sh, cmd.exe) as children of the pgAdmin process.
  • Monitor identity provider provisioning for usernames containing shell metacharacters and reject them at the source.
  • Log and review changes to config.py or config_local.py where MASTER_PASSWORD_HOOK is defined.

How to Mitigate CVE-2026-17347

Immediate Actions Required

  • Upgrade pgAdmin 4 to version 9.17 or later on all servers.
  • Until patching, unset or comment out MASTER_PASSWORD_HOOK in config.py if the deployment does not strictly require it.
  • Enforce username validation at the identity provider to reject values containing shell metacharacters.

Patch Information

The fix, delivered in pgAdmin 4 9.17, tokenises the administrator-configured hook string into an argument vector using shlex in POSIX-quoting mode with backslash-escaping disabled so Windows-style paths are not mis-parsed. The untrusted username is then substituted into individual argv elements and executed with shell=False, confining the username to a single argument where shell metacharacters are inert. See GitHub Commit ea7e798 and the Windows tokenisation follow-up in GitHub Commit e7a8576.

Workarounds

  • Remove the %u placeholder from MASTER_PASSWORD_HOOK and derive the username inside the invoked script from an environment variable instead.
  • Restrict pgAdmin authentication to an internal identity source where administrators fully control the username format.
  • Move any shell logic (pipes, redirection, globbing) out of the MASTER_PASSWORD_HOOK string and into the invoked script, since the patched code no longer interprets shell syntax in the hook value.
bash
# Example post-patch configuration in web/config.py
# The command is split into arguments and executed directly, without a
# shell. Quote paths that contain spaces.
MASTER_PASSWORD_HOOK = '"/opt/pgadmin/scripts/passwdgen_script.sh" %u'

# On Windows, invoke a batch file via an executable wrapper:
# MASTER_PASSWORD_HOOK = 'cmd.exe /c "C:\\pgadmin\\passwdgen.bat" %u'

Source: GitHub Issue #10191

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.