{"id":"CVE-2026-17566","summary":"pgAdmin 4: RCE via backslash-escape mismatch in Import/Export Data query guard (incomplete defense, sibling gap to CVE-2025-13780)","details":"pgAdmin 4's Import/Export Data tool builds a psql \\copy (...) command line by interpolating a user-supplied SQL query into a Jinja template and passing the rendered line to psql via --command. To stop an attacker from breaking out of the (...) wrapper, create_import_export_job() (route POST /import_export/job/\u003csid\u003e, gated only by the ordinary, commonly-granted tools_import_export_data permission) validated the query with a hand-written parenthesis-balance checker, _is_query_parens_balanced(). That checker always treated a backslash before a single quote (\\') as escaping the quote, i.e. as if standard_conforming_strings were off. PostgreSQL has defaulted standard_conforming_strings to on since 9.1 (2010), the default on every PostgreSQL version pgAdmin 4 currently supports (13-18); under that default psql's own \\copy tokenizer treats \\ as an ordinary character, so a single quote immediately after it closes the string literal. A query such as SELECT 'a\\') TO PROGRAM 'echo pwned' x' was therefore accepted as \"balanced\" by pgAdmin's checker (which believed the ) was still inside the string), while psql, run through the actual rendered command line, closes the string at that point and treats the following ) as the end of the wrapping \\copy (...) subquery, exposing an attacker-chosen TO PROGRAM '\u003ccommand\u003e' clause that psql executes via popen() -- independent of a subsequent syntax error later on the same line. This is the same class of bug as CVE-2025-12762/CVE-2025-13780 (RCE via psql meta-command/COPY injection during PLAIN-format dump restore), reached through an independently written defense in a different module (Import/Export Data rather than Restore) that had its own, different logic bug (inverted backslash-escape semantics rather than a BOM-defeated regex anchor).\n\nThe fix rejects any backslash inside a single-quoted string in the query outright, rather than picking one of the two possible psql interpretations. This is intentionally conservative: because the correct interpretation of \\ depends on the target server's standard_conforming_strings setting, which the checker cannot reliably know at validation time, refusing the query is safer than guessing.\n\nThis issue affects pgAdmin 4: from the introduction of _is_query_parens_balanced() before 9.18.","modified":"2026-08-07T03:30:47.410466010Z","published":"2026-07-31T16:00:22.738Z","database_specific":{"cwe_ids":["CWE-115","CWE-78"],"osv_generated_from":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/17xxx/CVE-2026-17566.json","unresolved_ranges":[{"extracted_events":[{"fixed":"9.18"}],"source":"AFFECTED_FIELD"},{"extracted_events":[{"fixed":"9.18"}],"source":"DESCRIPTION"}],"cna_assigner":"PostgreSQL"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/17xxx/CVE-2026-17566.json"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-17566"},{"type":"REPORT","url":"https://github.com/pgadmin-org/pgadmin4/issues/10213"},{"type":"FIX","url":"https://github.com/pgadmin-org/pgadmin4/commit/1496fabe28c9f825f6bac0f0d000d9d3276322c3"},{"type":"PACKAGE","url":"https://github.com/pgadmin-org/pgadmin4"}],"affected":[{"ranges":[{"type":"GIT","repo":"https://github.com/pgadmin-org/pgadmin4","events":[{"introduced":"0"},{"fixed":"1496fabe28c9f825f6bac0f0d000d9d3276322c3"}],"database_specific":{"source":"REFERENCES"}}],"versions":["REL-9_16","REL-9_15","REL-9_14","REL-9_13","REL-9_12","REL-9_11","REL-9_10","REL-9_9","REL-9_8","REL-9_7","REL-9_6","REL-9_5","REL-9_4","REL-9_3","REL-9_2","REL-9_1","REL-9_0","REL-8_14","REL-8_13","REL-8_12","REL-8_11","REL-8_10","REL-8_9","REL-8_8","REL-8_7","REL-8_6","REL-8_5","REL-8_4","REL-8_3","REL-8_2","REL-8_1","REL-8_0","REL-7_8","REL-7_7","REL-7_6","REL-7_5","REL-7_4","REL-7_3","REL-7_2","REL-7_1","REL-7_0","REL-6_21","REL-6_20","REL-6_19","REL-6_18","REL-6_17","REL-6_16","REL-6_15","REL-6_14","REL-6_13","REL-6_12","REL-6_11","REL-6_10","REL-6_9","REL-6_8","REL-6_7","REL-6_6","REL-6_5","REL-6_4","REL-6_3","REL-6_2","REL-6_1","REL-6_0","REL-5_7","REL-5_6","REL-5_5","REL-5_4","REL-5_3","REL-5_2","REL-5_1","REL-5_0","REL-4_30","REL-4_29","REL-4_28","REL-4_27","REL-4_26","REL-4_25","REL-4_24","REL-4_23","REL-4_22","REL-4_21","REL-4_20","REL-4_19","REL-4_18","REL-4_17","REL-4_16","REL-4_15","REL-4_14","REL-4_13","REL-4_12","REL-4_11","REL-4_10","REL-4_9","REL-4_8","REL-4_7","REL-4_6","REL-4_5","REL-4_4","REL-4_3","REL-4_2","REL-4_1","REL-4_0","REL-3_6","REL-3_5","REL-3_4","REL-3_3","REL-3_2","REL-3_1","REL-3_0","REL-2_1","REL-2_0","REL-2_0-RC2","REL-2_0-RC1","REL-1_6","REL-1_5","REL-1_4","REL-1_3","REL-1_2","REL-1_1","REL-1_0","REL-1_0-RC1","REL-1_0-BETA4","REL-1_0-BETA3","REL-1_0-BETA2","REL-1_0-BETA1"],"database_specific":{"source":"https://storage.googleapis.com/cve-osv-conversion/osv-output/CVE-2026-17566.json"}}],"schema_version":"1.8.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H"}]}