CVE-2026-17566 Overview
CVE-2026-17566 is a command injection vulnerability in pgAdmin 4's Import/Export Data tool. The flaw lives in create_import_export_job() at the POST /import_export/job/<sid> route, gated only by the commonly-granted tools_import_export_data permission. The tool builds a psql \copy (...) command line by interpolating a user-supplied SQL query into a Jinja template. Its _is_query_parens_balanced() guard used inverted backslash-escape semantics, allowing an attacker to break out of the wrapper and inject a TO PROGRAM '<command>' clause that psql executes via popen().
Critical Impact
Authenticated pgAdmin 4 users with the routine tools_import_export_data permission can achieve remote code execution on the pgAdmin host by submitting a crafted SQL query.
Affected Products
- pgAdmin 4 versions prior to 9.18
- pgAdmin 4 deployments connected to PostgreSQL 9.1 through 18 (default standard_conforming_strings=on)
- Any pgAdmin 4 installation where the tools_import_export_data permission is granted to non-administrative users
Discovery Timeline
- 2026-07-31 - CVE-2026-17566 published to NVD
- 2026-08-05 - Last updated in NVD database
Technical Details for CVE-2026-17566
Vulnerability Analysis
The Import/Export Data tool wraps a user-supplied SQL query inside a psql \copy (...) meta-command and passes the rendered line to psql via --command. To prevent an attacker from closing the wrapping parenthesis early, pgAdmin validated the query with a hand-written parenthesis-balance checker, _is_query_parens_balanced(). That checker unconditionally treated a backslash before a single quote (\') as escaping the quote, matching only the behavior of PostgreSQL when standard_conforming_strings=off.
PostgreSQL has defaulted standard_conforming_strings to on since version 9.1 (2010). Under that default, the psql\copy tokenizer treats \ as an ordinary character. A single quote immediately after a backslash therefore closes the string literal. The checker and psql disagree about where the string ends, which is the entire bug. This maps to [CWE-78] (OS Command Injection) and belongs to the same class as CVE-2025-12762 and CVE-2025-13780, both of which reached RCE through a differently written but similarly flawed defense in pgAdmin's Restore module.
Root Cause
The validator's tokenizer models a PostgreSQL string-literal grammar that no longer matches the server default. Because _is_query_parens_balanced() cannot know the target server's standard_conforming_strings setting at validation time, any code path that guesses one interpretation is exploitable when the runtime disagrees. The bug is a semantic mismatch between the guard and the downstream psql parser.
Attack Vector
An authenticated user with the tools_import_export_data permission submits a query such as SELECT 'a\') TO PROGRAM 'echo pwned' x' to the Import/Export job endpoint. The checker treats \' as an escaped quote and reports the parentheses as balanced. psql, running with standard_conforming_strings=on, closes the string at the quote and treats the next ) as the end of the wrapping \copy (...) subquery. The attacker-supplied TO PROGRAM 'echo pwned' clause then executes via popen(), independent of any later syntax error on the same line.
# skip content inside those constructs either, or an attacker can
# hide an unbalanced ) inside them to bypass validation.
#
# - Backslash escapes (\x) are always handled inside single-quoted
# - strings. psql enables this for E'...' strings unconditionally
# - and for all strings when standard_conforming_strings=off. By
# - always handling backslashes, we match psql in all modes. This
# - may reject some valid queries (e.g. '\') with scs=on) but
# - will never accept a dangerous one.
# + Backslash handling inside single-quoted strings is ambiguous:
# + psql treats \ as a plain character when
# + standard_conforming_strings=on (the default since PG 9.1, and
# + the default on every PostgreSQL version pgAdmin 4 supports), but
# + as an escape character when scs=off or inside E'...' strings.
# + Silently picking one interpretation can let a crafted query be
# + accepted here while psql parses it differently (e.g. a \'
# + that we treat as "still inside the string" while psql, under
# + the default scs=on, treats it as the string's closing quote,
# + exposing a real ')' immediately after). Rather than guess the
# + server's scs setting, we reject any query containing a
# + backslash inside a single-quoted string outright.
# """
# depth = 0
# i = 0
# Source: https://github.com/pgadmin-org/pgadmin4/commit/1496fabe28c9f825f6bac0f0d000d9d3276322c3
The patch abandons the two-interpretation gamble. It rejects any query that contains a backslash inside a single-quoted string, refusing rather than guessing the server's standard_conforming_strings state.
Detection Methods for CVE-2026-17566
Indicators of Compromise
- psql child processes spawned by the pgAdmin 4 web process with --command arguments containing TO PROGRAM
- Unexpected child processes (shells, interpreters, downloaders) parented to the pgAdmin 4 Python or WSGI worker
- HTTP POST requests to /import_export/job/<sid> containing \' or TO PROGRAM inside the query field
- Outbound network connections initiated by processes descended from the pgAdmin runtime host
Detection Strategies
- Alert on process-tree anomalies where the pgAdmin 4 service becomes the ancestor of shells or command interpreters.
- Inspect application and reverse-proxy logs for Import/Export job submissions that include backslash-quote sequences or TO PROGRAM clauses.
- Correlate psql invocations from the pgAdmin host with the user, session, and query text to identify unauthorized job submissions.
Monitoring Recommendations
- Forward pgAdmin 4 application logs and host process telemetry to a centralized analytics platform for retention and correlation.
- Baseline normal psql command lines executed by pgAdmin and alert on deviations, especially those containing PROGRAM keywords.
- Track grants of the tools_import_export_data permission and review any accounts that hold it.
How to Mitigate CVE-2026-17566
Immediate Actions Required
- Upgrade pgAdmin 4 to version 9.18 or later, which contains the corrected validator.
- Revoke the tools_import_export_data permission from any user role that does not require Import/Export functionality.
- Restrict network access to the pgAdmin 4 web interface to trusted administrative networks only.
- Review pgAdmin and host logs for prior Import/Export job submissions containing backslash escapes or TO PROGRAM clauses.
Patch Information
The fix is committed in the pgAdmin 4 repository at pgAdmin 4 security commit 1496fabe and discussed in pgAdmin 4 issue #10213. The patch modifies _is_query_parens_balanced() in web/pgadmin/tools/import_export/__init__.py to reject any query containing a backslash inside a single-quoted string rather than choosing one of the two possible psql interpretations.
Workarounds
- Remove the tools_import_export_data role permission from all non-administrator accounts until patching is complete.
- Deploy a reverse-proxy or WAF rule that blocks requests to /import_export/job/ containing TO PROGRAM or backslash-quote sequences in the query body.
- Run pgAdmin 4 under a low-privilege service account with no shell access and restricted egress to limit the impact of successful exploitation.
# Confirm the running pgAdmin 4 version and upgrade via pip
pip show pgadmin4 | grep -i version
pip install --upgrade 'pgadmin4>=9.18'
# Restart the pgAdmin service after upgrade
systemctl restart pgadmin4
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

