Summary
A SQL injection flaw in pgAdmin 4's Statistics and Dependencies views allows unescaped object names to be interpolated directly into SQL templates.
A low-privileged user who can create tables, publications, or subscriptions with crafted names may trigger arbitrary SQL execution when another user opens the affected UI tabs.
Affected context
The vulnerability appears in pgAdmin 4's server-side SQL templates that power the Index Statistics and Publication/Subscription Dependencies views.
It arises specifically when object names (tables, publications, subscriptions) derived from PostgreSQL catalog data are interpolated using raw template variables rather than a quoting routine like qtLiteral.
Deployments where users can create database objects and other users rely on pgAdmin's Statistics or Dependencies tabs to inspect those objects are within the affected context.
Exploit preconditions
- Attack surface: pgAdmin 4 web interface, specifically the Statistics and Dependencies tabs for:
- All-indexes statistics listing (coll_stats.sql templates)
- Publication dependencies and position queries (dependencies.sql, get_position.sql)
- Subscription dependencies and position queries
- Required access: Ability for a low-privileged database user to:
- Connect to the target database
- CREATE TABLE, CREATE PUBLICATION, or CREATE SUBSCRIPTION
- Trigger point: A victim (potentially with higher privileges) opens the Statistics or Dependencies tab for a crafted object in pgAdmin, causing pgAdmin to render and execute the associated SQL template.
- Why exploitation is possible: Object names pulled from
pg_catalogare assumed safe and inserted unescaped into SQL templates, allowing an embedded apostrophe to terminate the intended literal and introduce attacker-controlled SQL fragments.
Why this happens
The underlying bug is a classic SQL injection condition caused by unsafe construction of SQL statements from untrusted data.
pgAdmin's Jinja-based templates used {{ name }} directly for identifiers sourced from pg_catalog without passing them through a quoting helper like qtLiteral(conn).
Because PostgreSQL identifiers can contain arbitrary characters when quoted, including apostrophes, this assumption that catalog-derived names are inherently safe breaks down.
This aligns with CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection'), where special characters in user-influenced input are not properly encoded or parameterized before being embedded into SQL.
Potential impact
- Arbitrary SQL statements may execute in the context of the viewing user's database session.
- Attackers could alter or delete data accessible to the victim's role.
- Privilege escalation within the database may occur if the victim has higher privileges than the attacker.
- Lateral movement to other schemas or databases the victim can access may become possible.
- Security logging and auditing data may be manipulated or erased.
General mitigation guidance
Effective mitigation centers on ensuring that all dynamic SQL is built using safe, consistent quoting and parameterization patterns.
Common defensive approaches include:
- Routing all user- or catalog-influenced SQL fragments through a dedicated quoting/escaping routine (e.g.,
qtLiteralor driver-provided quote functions). - Designing templates so that identifiers and values are never concatenated directly, but instead use parameter binding or specialized identifier-quoting APIs.
- Treating catalog-derived names as untrusted input and applying the same sanitization and encoding rules as for explicit user input.
- Establishing secure-by-default template helpers where raw interpolation is discouraged or linted, and safe filters are mandatory for SQL fragments.
- Limiting the privileges of pgAdmin database connections so that even if a query is compromised, its ability to cause damage is constrained.
How teams detect issues like this
Detection typically combines static and review-based techniques:
- Implement template linting or static analysis that flags raw
{{ name }}-style interpolations in SQL templates unless a safe quoting filter is applied. - Use automated tests that render each SQL template with intentionally hostile identifiers (e.g., containing apostrophes and stacked statements) and verify the resulting SQL parses as a single statement.
- Incorporate secure coding checklists into code review, requiring reviewers to verify that all identifiers and values in SQL templates are either parameterized or passed through a quoting helper.
- Apply policy in CI/CD pipelines that rejects merges when unsafe string concatenation or unfiltered template variables are found in SQL-related code.
- Consider periodic security-focused review of pgAdmin extensions and plugins, particularly those that introduce new SQL templates.

