CVE-2026-12044 (GCVE-0-2026-12044)

Vulnerability from cvelistv5 – Published: 2026-06-18 23:37 – Updated: 2026-06-22 18:37
VLAI
Title
pgAdmin 4: SQL injection in COMMENT ON ... IS '<description>' rendering across dialog templates
Summary
SQL injection in pgAdmin 4 across every dialog template that renders ``COMMENT ON ... IS '<description>'`` for a user-supplied description field. The Jinja templates for Domains (and their constraints), Foreign Tables, Languages, and Event Triggers, plus the Views OID-lookup query, interpolated the description directly inside a single-quoted SQL literal -- ``'{{ data.description }}'`` -- instead of passing it through the ``qtLiteral`` escape filter. An authenticated pgAdmin user with permission to create or alter the affected object types could submit a description containing an apostrophe, break out of the literal and chain arbitrary SQL. The injected SQL runs under the PostgreSQL role the user is already authenticated as; for a connected role with ``COPY ... TO/FROM PROGRAM`` (typically PostgreSQL superuser), this chains to OS command execution on the PostgreSQL host. The defect does not cross a privilege boundary -- the user already has direct SQL access to that role through pgAdmin's Query Tool -- so the attacker gains no capability beyond what their database role already grants. The marginal impact captures bypass of any application-layer Query Tool gating an operator may have configured. The defect was originally reported against the Domain Dialog ``description`` field; a code-wide audit identified sixteen sites of the same pattern across the templates listed above. The same review also surfaced ten related sinks in the pgstattuple/pgstatindex stats templates -- ``pgstattuple('{{schema}}.{{table}}')`` and the matching pgstatindex shape -- where ``qtIdent`` escapes embedded double quotes inside the identifier but not apostrophes, so a user with CREATE privilege on a schema could plant a table or index named ``foo'bar`` and a later stats viewer would render an unbalanced literal. Fix is layered: 1. Sites: replace every ``'{{ x.description }}'`` with ``{{ x.description|qtLiteral(conn) }}`` (no surrounding quotes -- the filter wraps the value in escaped quotes itself). Plumb ``conn=self.conn`` through every ``render_template`` call that loads one of these templates. Also corrects a ``{ % elif`` Jinja typo in the foreign-table schema diff (dead branch). Rewrite the ten pgstattuple/pgstatindex stats sites to address the relation via OID + ``::oid::regclass`` cast (e.g. ``pgstattuple({{ tid }}::oid::regclass)``), eliminating the embedded literal-call form entirely so that bug-class can no longer recur there. 2. Driver hardening: ``qtLiteral`` (in ``utils/driver/psycopg3/__init__.py``) used to silently return the raw unescaped value when its ``conn`` argument was falsy. It now raises ``ValueError`` -- surfacing the entire bug class going forward. The change immediately uncovered eight latent plumbing bugs (in ``schemas/__init__.py``, ``schemas/functions/__init__.py``, ``schemas/tables/utils.py``, ``foreign_servers/__init__.py``, and seven sites in ``roles/__init__.py``) -- all fixed as part of this patch. The inner ``except`` block that swallowed adapter-level failures and returned the raw value is also removed, so unadaptable inputs raise instead of leaking unescaped values. 3. Regression tests: a per-template behavioural test renders each previously-vulnerable template with an apostrophe-injection payload and asserts the escaped fragment is present and the vulnerable fragment absent; a lint test walks every ``*.sql`` template flagging any ``'{{ ... }}'`` single-quote-wrapped interpolation against an explicit allowlist; unit tests cover the new qtLiteral fail-fast and inner-except raise paths. This issue affects pgAdmin 4: from 1.0 before 9.16.
SSVC
Exploitation: none Automatable: no Technical Impact: total
CISA Coordinator · CISA-ADP (v2.0.3)
Decision recorded 2026-06-22 18:36 UTC
CWE
  • CWE-89 - Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
  • CWE-116 - Improper Encoding or Escaping of Output
Assigner
PostgreSQL CNA under the redhat root
CNA scorecard C 69/100 over 115 records in the last 180 days details
Impacted products
Vendor Product Version CPE status
pgadmin.org pgAdmin 4 Affected: 1.0 , < 9.16 (custom)
guessed Create a notification for this product.
Show details on NVD website

{
  "containers": {
    "adp": [
      {
        "metrics": [
          {
            "other": {
              "content": {
                "id": "CVE-2026-12044",
                "options": [
                  {
                    "Exploitation": "none"
                  },
                  {
                    "Automatable": "no"
                  },
                  {
                    "Technical Impact": "total"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-06-22T18:36:59.059744Z",
                "version": "2.0.3"
              },
              "type": "ssvc"
            }
          }
        ],
        "providerMetadata": {
          "dateUpdated": "2026-06-22T18:37:05.999Z",
          "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
          "shortName": "CISA-ADP"
        },
        "title": "CISA ADP Vulnrichment"
      }
    ],
    "cna": {
      "affected": [
        {
          "defaultStatus": "unaffected",
          "modules": [
            "Domains",
            "Domain Constraints",
            "Foreign Tables",
            "Languages",
            "Event Triggers",
            "Views",
            "Tables",
            "Indexes",
            "Index Constraints",
            "Exclusion Constraints",
            "Materialized Views",
            "Driver (qtLiteral)"
          ],
          "product": "pgAdmin 4",
          "programFiles": [
            "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/utils/driver/psycopg3/__init__.py",
            "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/default/create.sql",
            "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/tables/sql/16_plus/stats.sql"
          ],
          "repo": "https://github.com/pgadmin-org/pgadmin4",
          "vendor": "pgadmin.org",
          "versions": [
            {
              "lessThan": "9.16",
              "status": "affected",
              "version": "1.0",
              "versionType": "custom"
            }
          ]
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "Jasser Chebbi \u003cjasserchebbi@outlook.com\u003e"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Dave Page \u003cdpage@pgadmin.org\u003e"
        },
        {
          "lang": "en",
          "type": "remediation developer",
          "value": "Ashesh Vashi \u003cashesh.vashi@enterprisedb.com\u003e"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "value": "SQL injection in pgAdmin 4 across every dialog template that renders ``COMMENT ON ... IS \u0027\u003cdescription\u003e\u0027`` for a user-supplied description field. The Jinja templates for Domains (and their constraints), Foreign Tables, Languages, and Event Triggers, plus the Views OID-lookup query, interpolated the description directly inside a single-quoted SQL literal -- ``\u0027{{ data.description }}\u0027`` -- instead of passing it through the ``qtLiteral`` escape filter. An authenticated pgAdmin user with permission to create or alter the affected object types could submit a description containing an apostrophe, break out of the literal and chain arbitrary SQL. The injected SQL runs under the PostgreSQL role the user is already authenticated as; for a connected role with ``COPY ... TO/FROM PROGRAM`` (typically PostgreSQL superuser), this chains to OS command execution on the PostgreSQL host. The defect does not cross a privilege boundary -- the user already has direct SQL access to that role through pgAdmin\u0027s Query Tool -- so the attacker gains no capability beyond what their database role already grants. The marginal impact captures bypass of any application-layer Query Tool gating an operator may have configured.\n\nThe defect was originally reported against the Domain Dialog ``description`` field; a code-wide audit identified sixteen sites of the same pattern across the templates listed above. The same review also surfaced ten related sinks in the pgstattuple/pgstatindex stats templates -- ``pgstattuple(\u0027{{schema}}.{{table}}\u0027)`` and the matching pgstatindex shape -- where ``qtIdent`` escapes embedded double quotes inside the identifier but not apostrophes, so a user with CREATE privilege on a schema could plant a table or index named ``foo\u0027bar`` and a later stats viewer would render an unbalanced literal.\n\nFix is layered:\n\n  1. Sites: replace every ``\u0027{{ x.description }}\u0027`` with ``{{ x.description|qtLiteral(conn) }}`` (no surrounding quotes -- the filter wraps the value in escaped quotes itself). Plumb ``conn=self.conn`` through every ``render_template`` call that loads one of these templates. Also corrects a ``{ % elif`` Jinja typo in the foreign-table schema diff (dead branch). Rewrite the ten pgstattuple/pgstatindex stats sites to address the relation via OID + ``::oid::regclass`` cast (e.g. ``pgstattuple({{ tid }}::oid::regclass)``), eliminating the embedded literal-call form entirely so that bug-class can no longer recur there.\n\n  2. Driver hardening: ``qtLiteral`` (in ``utils/driver/psycopg3/__init__.py``) used to silently return the raw unescaped value when its ``conn`` argument was falsy. It now raises ``ValueError`` -- surfacing the entire bug class going forward. The change immediately uncovered eight latent plumbing bugs (in ``schemas/__init__.py``, ``schemas/functions/__init__.py``, ``schemas/tables/utils.py``, ``foreign_servers/__init__.py``, and seven sites in ``roles/__init__.py``) -- all fixed as part of this patch. The inner ``except`` block that swallowed adapter-level failures and returned the raw value is also removed, so unadaptable inputs raise instead of leaking unescaped values.\n\n  3. Regression tests: a per-template behavioural test renders each previously-vulnerable template with an apostrophe-injection payload and asserts the escaped fragment is present and the vulnerable fragment absent; a lint test walks every ``*.sql`` template flagging any ``\u0027{{ ... }}\u0027`` single-quote-wrapped interpolation against an explicit allowlist; unit tests cover the new qtLiteral fail-fast and inner-except raise paths.\n\nThis issue affects pgAdmin 4: from 1.0 before 9.16."
        }
      ],
      "metrics": [
        {
          "cvssV3_1": {
            "attackComplexity": "LOW",
            "attackVector": "NETWORK",
            "availabilityImpact": "HIGH",
            "baseScore": 8.8,
            "baseSeverity": "HIGH",
            "confidentialityImpact": "HIGH",
            "integrityImpact": "HIGH",
            "privilegesRequired": "LOW",
            "scope": "UNCHANGED",
            "userInteraction": "NONE",
            "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
            "version": "3.1"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "The 8.8 is earned by the stored pgstattuple / pgstatindex sinks (commit 2ae0d3610). The chain: a low-privilege user with CREATE on a schema names a table or index containing an apostrophe (e.g. foo\u0027bar); pgAdmin\u0027s stats templates render that identifier inside a string literal -- pgstattuple(\u0027schema.foo\u0027bar\u0027) -- when a *different* user views statistics on the object. If the viewing user is a PostgreSQL superuser, the SQL injection executes under the superuser role, reaching COPY ... TO/FROM PROGRAM and OS code execution on the DB host. The privilege crossing (low-priv planter -\u003e superuser viewer) is what justifies C:H/I:H/A:H even with S:U: the impact is felt by a different security principal than the attacker, but it lands inside the same DB authority.\n\nThe originally-reported Domain Dialog description field is the same code-class but is self-SQLi -- the user injecting the apostrophe is the user whose role runs it. That sub-vector alone would score in the #10026 range (4.3 MEDIUM); it is bundled here because the fix is the same qtLiteral plumbing across all sixteen sites.\n\nS:U because pgAdmin does not mediate a privilege boundary between the planter, the viewer, and the DB role; the boundary that is crossed lives entirely inside PostgreSQL\u0027s privilege model."
            }
          ]
        },
        {
          "cvssV4_0": {
            "baseScore": 8.7,
            "baseSeverity": "HIGH",
            "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
            "version": "4.0"
          },
          "format": "CVSS",
          "scenarios": [
            {
              "lang": "en",
              "value": "Same reasoning as the CVSS 3.1 entry: the stored pgstattuple sink is the load-bearing impact (low-priv planter, superuser viewer, superuser-role SQL). VC:H/VI:H/VA:H from the COPY ... TO PROGRAM reach; SC/SI/SA:N because pgAdmin is not the security authority being crossed -- the boundary lives in PostgreSQL."
            }
          ]
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-89",
              "description": "CWE-89 Improper Neutralization of Special Elements used in an SQL Command (\u0027SQL Injection\u0027)",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-116",
              "description": "CWE-116 Improper Encoding or Escaping of Output",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-06-18T23:37:16.202Z",
        "orgId": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
        "shortName": "PostgreSQL"
      },
      "references": [
        {
          "tags": [
            "issue-tracking"
          ],
          "url": "https://github.com/pgadmin-org/pgadmin4/issues/10078"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/pgadmin-org/pgadmin4/commit/658bb585d"
        },
        {
          "tags": [
            "patch"
          ],
          "url": "https://github.com/pgadmin-org/pgadmin4/commit/2ae0d3610"
        }
      ],
      "source": {
        "discovery": "EXTERNAL"
      },
      "title": "pgAdmin 4: SQL injection in COMMENT ON ... IS \u0027\u003cdescription\u003e\u0027 rendering across dialog templates"
    }
  },
  "cveMetadata": {
    "assignerOrgId": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
    "assignerShortName": "PostgreSQL",
    "cveId": "CVE-2026-12044",
    "datePublished": "2026-06-18T23:37:16.202Z",
    "dateReserved": "2026-06-11T20:40:05.751Z",
    "dateUpdated": "2026-06-22T18:37:05.999Z",
    "state": "PUBLISHED"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2",
  "vulnerability-lookup:meta": {
    "epss": {
      "cve": "CVE-2026-12044",
      "date": "2026-10-31",
      "epss": "0.00709",
      "percentile": "0.53517"
    },
    "nvd": {
      "cve": {
        "affected": [
          {
            "affectedData": [
              {
                "defaultStatus": "unaffected",
                "modules": [
                  "Domains",
                  "Domain Constraints",
                  "Foreign Tables",
                  "Languages",
                  "Event Triggers",
                  "Views",
                  "Tables",
                  "Indexes",
                  "Index Constraints",
                  "Exclusion Constraints",
                  "Materialized Views",
                  "Driver (qtLiteral)"
                ],
                "product": "pgAdmin 4",
                "programFiles": [
                  "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/utils/driver/psycopg3/__init__.py",
                  "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/default/create.sql",
                  "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/tables/sql/16_plus/stats.sql"
                ],
                "repo": "https://github.com/pgadmin-org/pgadmin4",
                "vendor": "pgadmin.org",
                "versions": [
                  {
                    "lessThan": "9.16",
                    "status": "affected",
                    "version": "1.0",
                    "versionType": "custom"
                  }
                ]
              }
            ],
            "source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007"
          }
        ],
        "configurations": [
          {
            "nodes": [
              {
                "cpeMatch": [
                  {
                    "criteria": "cpe:2.3:a:pgadmin:pgadmin_4:*:*:*:*:*:postgresql:*:*",
                    "matchCriteriaId": "6C00C802-CCFB-4A7B-835E-FBE61FFC87FD",
                    "versionEndExcluding": "9.16",
                    "versionStartIncluding": "1.0",
                    "vulnerable": true
                  }
                ],
                "negate": false,
                "operator": "OR"
              }
            ]
          }
        ],
        "cveTags": [],
        "descriptions": [
          {
            "lang": "en",
            "value": "SQL injection in pgAdmin 4 across every dialog template that renders ``COMMENT ON ... IS \u0027\u003cdescription\u003e\u0027`` for a user-supplied description field. The Jinja templates for Domains (and their constraints), Foreign Tables, Languages, and Event Triggers, plus the Views OID-lookup query, interpolated the description directly inside a single-quoted SQL literal -- ``\u0027{{ data.description }}\u0027`` -- instead of passing it through the ``qtLiteral`` escape filter. An authenticated pgAdmin user with permission to create or alter the affected object types could submit a description containing an apostrophe, break out of the literal and chain arbitrary SQL. The injected SQL runs under the PostgreSQL role the user is already authenticated as; for a connected role with ``COPY ... TO/FROM PROGRAM`` (typically PostgreSQL superuser), this chains to OS command execution on the PostgreSQL host. The defect does not cross a privilege boundary -- the user already has direct SQL access to that role through pgAdmin\u0027s Query Tool -- so the attacker gains no capability beyond what their database role already grants. The marginal impact captures bypass of any application-layer Query Tool gating an operator may have configured.\n\nThe defect was originally reported against the Domain Dialog ``description`` field; a code-wide audit identified sixteen sites of the same pattern across the templates listed above. The same review also surfaced ten related sinks in the pgstattuple/pgstatindex stats templates -- ``pgstattuple(\u0027{{schema}}.{{table}}\u0027)`` and the matching pgstatindex shape -- where ``qtIdent`` escapes embedded double quotes inside the identifier but not apostrophes, so a user with CREATE privilege on a schema could plant a table or index named ``foo\u0027bar`` and a later stats viewer would render an unbalanced literal.\n\nFix is layered:\n\n  1. Sites: replace every ``\u0027{{ x.description }}\u0027`` with ``{{ x.description|qtLiteral(conn) }}`` (no surrounding quotes -- the filter wraps the value in escaped quotes itself). Plumb ``conn=self.conn`` through every ``render_template`` call that loads one of these templates. Also corrects a ``{ % elif`` Jinja typo in the foreign-table schema diff (dead branch). Rewrite the ten pgstattuple/pgstatindex stats sites to address the relation via OID + ``::oid::regclass`` cast (e.g. ``pgstattuple({{ tid }}::oid::regclass)``), eliminating the embedded literal-call form entirely so that bug-class can no longer recur there.\n\n  2. Driver hardening: ``qtLiteral`` (in ``utils/driver/psycopg3/__init__.py``) used to silently return the raw unescaped value when its ``conn`` argument was falsy. It now raises ``ValueError`` -- surfacing the entire bug class going forward. The change immediately uncovered eight latent plumbing bugs (in ``schemas/__init__.py``, ``schemas/functions/__init__.py``, ``schemas/tables/utils.py``, ``foreign_servers/__init__.py``, and seven sites in ``roles/__init__.py``) -- all fixed as part of this patch. The inner ``except`` block that swallowed adapter-level failures and returned the raw value is also removed, so unadaptable inputs raise instead of leaking unescaped values.\n\n  3. Regression tests: a per-template behavioural test renders each previously-vulnerable template with an apostrophe-injection payload and asserts the escaped fragment is present and the vulnerable fragment absent; a lint test walks every ``*.sql`` template flagging any ``\u0027{{ ... }}\u0027`` single-quote-wrapped interpolation against an explicit allowlist; unit tests cover the new qtLiteral fail-fast and inner-except raise paths.\n\nThis issue affects pgAdmin 4: from 1.0 before 9.16."
          },
          {
            "lang": "es",
            "value": "Inyecci\u00f3n SQL en pgAdmin 4 en cada plantilla de di\u00e1logo que renderiza \u201cCOMMENT ON ... IS \u2019[description]\u2019\u201d para un campo de descripci\u00f3n proporcionado por el usuario. Las plantillas Jinja para Dominios (y sus restricciones), Tablas For\u00e1neas, Lenguajes y Disparadores de Eventos, adem\u00e1s de la consulta de b\u00fasqueda de OID de Vistas, interpolaron la descripci\u00f3n directamente dentro de un literal SQL entre comillas simples - \u201c\u2019{{ data.description }}\u2019\u201d - en lugar de pasarla a trav\u00e9s del filtro de escape \u201cqtLiteral\u201d. Un usuario autenticado de pgAdmin con permiso para crear o alterar los tipos de objetos afectados podr\u00eda enviar una descripci\u00f3n que contenga un ap\u00f3strofe, salir del literal y encadenar SQL arbitrario. El SQL inyectado se ejecuta bajo el rol de PostgreSQL con el que el usuario ya est\u00e1 autenticado; para un rol conectado con \u201cCOPY ... TO/FROM PROGRAM\u201d (t\u00edpicamente superusuario de PostgreSQL), esto se encadena a la ejecuci\u00f3n de comandos del sistema operativo en el host de PostgreSQL. El defecto no cruza un l\u00edmite de privilegios - el usuario ya tiene acceso SQL directo a ese rol a trav\u00e9s de la Herramienta de Consulta de pgAdmin - por lo que el atacante no obtiene ninguna capacidad m\u00e1s all\u00e1 de lo que su rol de base de datos ya le otorga. El impacto marginal captura la omisi\u00f3n de cualquier control de la Herramienta de Consulta a nivel de aplicaci\u00f3n que un operador pueda haber configurado.\n\nEl defecto fue reportado originalmente contra el campo \u201cdescription\u201d del Di\u00e1logo de Dominio; una auditor\u00eda de c\u00f3digo a nivel general identific\u00f3 diecis\u00e9is sitios del mismo patr\u00f3n en las plantillas mencionadas anteriormente. La misma revisi\u00f3n tambi\u00e9n revel\u00f3 diez sumideros relacionados en las plantillas de estad\u00edsticas pgstattuple/pgstatindex - \u201cpgstattuple(\u2019{{schema}}.{{table}}\u2019)\u201d y la forma coincidente de pgstatindex - donde \u201cqtIdent\u201d escapa las comillas dobles incrustadas dentro del identificador pero no los ap\u00f3strofes, por lo que un usuario con privilegio CREATE en un esquema podr\u00eda \u201cplantar\u201d una tabla o \u00edndice llamado \u201cfoo\u2019bar\u201d y un visor de estad\u00edsticas posterior renderizar\u00eda un literal desequilibrado.\n\nLa soluci\u00f3n est\u00e1 en capas:\n\n1.  Sitios: reemplazar cada \u201c\u2019{{ x.description }}\u2019\u201d con \u201c{{ x.description|qtLiteral(conn) }}\u201d (sin comillas circundantes - el filtro envuelve el valor en comillas escapadas por s\u00ed mismo). Conectar \u201cconn=self.conn\u201d a trav\u00e9s de cada llamada a \u201crender_template\u201d que carga una de estas plantillas. Tambi\u00e9n corrige un error tipogr\u00e1fico de Jinja \u201c{ % elif\u201d en la diferencia de esquema de tabla for\u00e1nea (rama muerta). Reescribir los diez sitios de estad\u00edsticas pgstattuple/pgstatindex para abordar la relaci\u00f3n a trav\u00e9s de OID + conversi\u00f3n \u201c::oid::regclass\u201d (p. ej., \u201cpgstattuple({{ tid }}::oid::regclass)\u201d), eliminando por completo la forma de llamada literal incrustada para que esa clase de error ya no pueda repetirse all\u00ed.\n\n2.  Reforzamiento del controlador: \u201cqtLiteral\u201d (en \u201cutils/driver/psycopg3/__init__.py\u201d) sol\u00eda devolver silenciosamente el valor sin escapar en bruto cuando su argumento \u201cconn\u201d era falso. Ahora lanza \u201cValueError\u201d - sacando a la luz toda la clase de errores en el futuro. El cambio descubri\u00f3 inmediatamente ocho errores de conexi\u00f3n latentes (en \u201cschemas/__init__.py\u201d, \u201cschemas/functions/__init__.py\u201d, \u201cschemas/tables/utils.py\u201d, \u201cforeign_servers/__init__.py\u201d, y siete sitios en \u201croles/__init__.py\u201d) - todos corregidos como parte de este parche. Tambi\u00e9n se elimina el bloque \u201cexcept\u201d interno que \u201ctragaba\u201d los fallos a nivel de adaptador y devolv\u00eda el valor en bruto, por lo que las entradas no adaptables lanzan una excepci\u00f3n en lugar de filtrar valores sin escapar.\n\n3.  Pruebas de regresi\u00f3n: una prueba de comportamiento por plantilla renderiza cada plantilla previamente vulnerable con una carga \u00fatil de inyecci\u00f3n de ap\u00f3strofe y afirma que el fragmento escapado est\u00e1 presente y el fragmento vulnerable ausente; una prueba de lint recorre cada plantilla \u201c*.sql\u201d marcando cualquier interpolaci\u00f3n \u201c\u2019{{ ... }}\u2019\u201d envuelta en comillas simples contra una lista de permitidos expl\u00edcita; las pruebas --TRUNCADO--"
          }
        ],
        "id": "CVE-2026-12044",
        "lastModified": "2026-10-26T11:10:00.157",
        "metrics": {
          "cvssMetricV31": [
            {
              "cvssData": {
                "attackComplexity": "LOW",
                "attackVector": "NETWORK",
                "availabilityImpact": "HIGH",
                "baseScore": 8.8,
                "baseSeverity": "HIGH",
                "confidentialityImpact": "HIGH",
                "integrityImpact": "HIGH",
                "privilegesRequired": "LOW",
                "scope": "UNCHANGED",
                "userInteraction": "NONE",
                "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
                "version": "3.1"
              },
              "exploitabilityScore": 2.8,
              "impactScore": 5.9,
              "source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
              "type": "Secondary"
            }
          ],
          "cvssMetricV40": [
            {
              "cvssData": {
                "Automatable": "NOT_DEFINED",
                "Recovery": "NOT_DEFINED",
                "Safety": "NOT_DEFINED",
                "attackComplexity": "LOW",
                "attackRequirements": "NONE",
                "attackVector": "NETWORK",
                "availabilityRequirement": "NOT_DEFINED",
                "baseScore": 8.7,
                "baseSeverity": "HIGH",
                "confidentialityRequirement": "NOT_DEFINED",
                "exploitMaturity": "NOT_DEFINED",
                "integrityRequirement": "NOT_DEFINED",
                "modifiedAttackComplexity": "NOT_DEFINED",
                "modifiedAttackRequirements": "NOT_DEFINED",
                "modifiedAttackVector": "NOT_DEFINED",
                "modifiedPrivilegesRequired": "NOT_DEFINED",
                "modifiedSubAvailabilityImpact": "NOT_DEFINED",
                "modifiedSubConfidentialityImpact": "NOT_DEFINED",
                "modifiedSubIntegrityImpact": "NOT_DEFINED",
                "modifiedUserInteraction": "NOT_DEFINED",
                "modifiedVulnAvailabilityImpact": "NOT_DEFINED",
                "modifiedVulnConfidentialityImpact": "NOT_DEFINED",
                "modifiedVulnIntegrityImpact": "NOT_DEFINED",
                "privilegesRequired": "LOW",
                "providerUrgency": "NOT_DEFINED",
                "subAvailabilityImpact": "NONE",
                "subConfidentialityImpact": "NONE",
                "subIntegrityImpact": "NONE",
                "userInteraction": "NONE",
                "valueDensity": "NOT_DEFINED",
                "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
                "version": "4.0",
                "vulnAvailabilityImpact": "HIGH",
                "vulnConfidentialityImpact": "HIGH",
                "vulnIntegrityImpact": "HIGH",
                "vulnerabilityResponseEffort": "NOT_DEFINED"
              },
              "source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
              "type": "Secondary"
            }
          ],
          "ssvcV203": [
            {
              "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "ssvcData": {
                "id": "CVE-2026-12044",
                "options": [
                  {
                    "exploitation": "none"
                  },
                  {
                    "automatable": "no"
                  },
                  {
                    "technicalImpact": "total"
                  }
                ],
                "role": "CISA Coordinator",
                "timestamp": "2026-06-22T18:36:59.059744Z",
                "version": "2.0.3"
              }
            }
          ]
        },
        "published": "2026-06-19T00:16:46.053",
        "references": [
          {
            "source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
            "tags": [
              "Patch"
            ],
            "url": "https://github.com/pgadmin-org/pgadmin4/commit/2ae0d3610"
          },
          {
            "source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
            "tags": [
              "Patch"
            ],
            "url": "https://github.com/pgadmin-org/pgadmin4/commit/658bb585d"
          },
          {
            "source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
            "tags": [
              "Issue Tracking",
              "Patch",
              "Third Party Advisory"
            ],
            "url": "https://github.com/pgadmin-org/pgadmin4/issues/10078"
          }
        ],
        "sourceIdentifier": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
        "vulnStatus": "Analyzed",
        "weaknesses": [
          {
            "description": [
              {
                "lang": "en",
                "value": "CWE-89"
              },
              {
                "lang": "en",
                "value": "CWE-116"
              }
            ],
            "source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
            "type": "Secondary"
          }
        ]
      }
    },
    "redhat_vex": {
      "current_release_date": "2026-06-28T07:15:54+00:00",
      "cve": "CVE-2026-12044",
      "id": "CVE-2026-12044",
      "initial_release_date": "2026-06-18T23:37:16.202000+00:00",
      "product_status:known_not_affected": "1",
      "source": "Red Hat CSAF VEX",
      "status": "final",
      "title": "pgadmin4: pgAdmin 4: Arbitrary code execution via SQL injection",
      "url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-12044.json",
      "version": "3"
    },
    "vulnrichment": {
      "containers": {
        "adp": [
          {
            "metrics": [
              {
                "other": {
                  "content": {
                    "id": "CVE-2026-12044",
                    "options": [
                      {
                        "Exploitation": "none"
                      },
                      {
                        "Automatable": "no"
                      },
                      {
                        "Technical Impact": "total"
                      }
                    ],
                    "role": "CISA Coordinator",
                    "timestamp": "2026-06-22T18:36:59.059744Z",
                    "version": "2.0.3"
                  },
                  "type": "ssvc"
                }
              }
            ],
            "providerMetadata": {
              "dateUpdated": "2026-06-22T18:37:02.398Z",
              "orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
              "shortName": "CISA-ADP"
            },
            "title": "CISA ADP Vulnrichment"
          }
        ],
        "cna": {
          "affected": [
            {
              "defaultStatus": "unaffected",
              "modules": [
                "Domains",
                "Domain Constraints",
                "Foreign Tables",
                "Languages",
                "Event Triggers",
                "Views",
                "Tables",
                "Indexes",
                "Index Constraints",
                "Exclusion Constraints",
                "Materialized Views",
                "Driver (qtLiteral)"
              ],
              "product": "pgAdmin 4",
              "programFiles": [
                "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/utils/driver/psycopg3/__init__.py",
                "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/domains/templates/domains/sql/default/create.sql",
                "https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/tables/sql/16_plus/stats.sql"
              ],
              "repo": "https://github.com/pgadmin-org/pgadmin4",
              "vendor": "pgadmin.org",
              "versions": [
                {
                  "lessThan": "9.16",
                  "status": "affected",
                  "version": "1.0",
                  "versionType": "custom"
                }
              ]
            }
          ],
          "credits": [
            {
              "lang": "en",
              "type": "finder",
              "value": "Jasser Chebbi \u003cjasserchebbi@outlook.com\u003e"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Dave Page \u003cdpage@pgadmin.org\u003e"
            },
            {
              "lang": "en",
              "type": "remediation developer",
              "value": "Ashesh Vashi \u003cashesh.vashi@enterprisedb.com\u003e"
            }
          ],
          "descriptions": [
            {
              "lang": "en",
              "value": "SQL injection in pgAdmin 4 across every dialog template that renders ``COMMENT ON ... IS \u0027\u003cdescription\u003e\u0027`` for a user-supplied description field. The Jinja templates for Domains (and their constraints), Foreign Tables, Languages, and Event Triggers, plus the Views OID-lookup query, interpolated the description directly inside a single-quoted SQL literal -- ``\u0027{{ data.description }}\u0027`` -- instead of passing it through the ``qtLiteral`` escape filter. An authenticated pgAdmin user with permission to create or alter the affected object types could submit a description containing an apostrophe, break out of the literal and chain arbitrary SQL. The injected SQL runs under the PostgreSQL role the user is already authenticated as; for a connected role with ``COPY ... TO/FROM PROGRAM`` (typically PostgreSQL superuser), this chains to OS command execution on the PostgreSQL host. The defect does not cross a privilege boundary -- the user already has direct SQL access to that role through pgAdmin\u0027s Query Tool -- so the attacker gains no capability beyond what their database role already grants. The marginal impact captures bypass of any application-layer Query Tool gating an operator may have configured.\n\nThe defect was originally reported against the Domain Dialog ``description`` field; a code-wide audit identified sixteen sites of the same pattern across the templates listed above. The same review also surfaced ten related sinks in the pgstattuple/pgstatindex stats templates -- ``pgstattuple(\u0027{{schema}}.{{table}}\u0027)`` and the matching pgstatindex shape -- where ``qtIdent`` escapes embedded double quotes inside the identifier but not apostrophes, so a user with CREATE privilege on a schema could plant a table or index named ``foo\u0027bar`` and a later stats viewer would render an unbalanced literal.\n\nFix is layered:\n\n  1. Sites: replace every ``\u0027{{ x.description }}\u0027`` with ``{{ x.description|qtLiteral(conn) }}`` (no surrounding quotes -- the filter wraps the value in escaped quotes itself). Plumb ``conn=self.conn`` through every ``render_template`` call that loads one of these templates. Also corrects a ``{ % elif`` Jinja typo in the foreign-table schema diff (dead branch). Rewrite the ten pgstattuple/pgstatindex stats sites to address the relation via OID + ``::oid::regclass`` cast (e.g. ``pgstattuple({{ tid }}::oid::regclass)``), eliminating the embedded literal-call form entirely so that bug-class can no longer recur there.\n\n  2. Driver hardening: ``qtLiteral`` (in ``utils/driver/psycopg3/__init__.py``) used to silently return the raw unescaped value when its ``conn`` argument was falsy. It now raises ``ValueError`` -- surfacing the entire bug class going forward. The change immediately uncovered eight latent plumbing bugs (in ``schemas/__init__.py``, ``schemas/functions/__init__.py``, ``schemas/tables/utils.py``, ``foreign_servers/__init__.py``, and seven sites in ``roles/__init__.py``) -- all fixed as part of this patch. The inner ``except`` block that swallowed adapter-level failures and returned the raw value is also removed, so unadaptable inputs raise instead of leaking unescaped values.\n\n  3. Regression tests: a per-template behavioural test renders each previously-vulnerable template with an apostrophe-injection payload and asserts the escaped fragment is present and the vulnerable fragment absent; a lint test walks every ``*.sql`` template flagging any ``\u0027{{ ... }}\u0027`` single-quote-wrapped interpolation against an explicit allowlist; unit tests cover the new qtLiteral fail-fast and inner-except raise paths.\n\nThis issue affects pgAdmin 4: from 1.0 before 9.16."
            }
          ],
          "metrics": [
            {
              "cvssV3_1": {
                "attackComplexity": "LOW",
                "attackVector": "NETWORK",
                "availabilityImpact": "HIGH",
                "baseScore": 8.8,
                "baseSeverity": "HIGH",
                "confidentialityImpact": "HIGH",
                "integrityImpact": "HIGH",
                "privilegesRequired": "LOW",
                "scope": "UNCHANGED",
                "userInteraction": "NONE",
                "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
                "version": "3.1"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "The 8.8 is earned by the stored pgstattuple / pgstatindex sinks (commit 2ae0d3610). The chain: a low-privilege user with CREATE on a schema names a table or index containing an apostrophe (e.g. foo\u0027bar); pgAdmin\u0027s stats templates render that identifier inside a string literal -- pgstattuple(\u0027schema.foo\u0027bar\u0027) -- when a *different* user views statistics on the object. If the viewing user is a PostgreSQL superuser, the SQL injection executes under the superuser role, reaching COPY ... TO/FROM PROGRAM and OS code execution on the DB host. The privilege crossing (low-priv planter -\u003e superuser viewer) is what justifies C:H/I:H/A:H even with S:U: the impact is felt by a different security principal than the attacker, but it lands inside the same DB authority.\n\nThe originally-reported Domain Dialog description field is the same code-class but is self-SQLi -- the user injecting the apostrophe is the user whose role runs it. That sub-vector alone would score in the #10026 range (4.3 MEDIUM); it is bundled here because the fix is the same qtLiteral plumbing across all sixteen sites.\n\nS:U because pgAdmin does not mediate a privilege boundary between the planter, the viewer, and the DB role; the boundary that is crossed lives entirely inside PostgreSQL\u0027s privilege model."
                }
              ]
            },
            {
              "cvssV4_0": {
                "baseScore": 8.7,
                "baseSeverity": "HIGH",
                "vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
                "version": "4.0"
              },
              "format": "CVSS",
              "scenarios": [
                {
                  "lang": "en",
                  "value": "Same reasoning as the CVSS 3.1 entry: the stored pgstattuple sink is the load-bearing impact (low-priv planter, superuser viewer, superuser-role SQL). VC:H/VI:H/VA:H from the COPY ... TO PROGRAM reach; SC/SI/SA:N because pgAdmin is not the security authority being crossed -- the boundary lives in PostgreSQL."
                }
              ]
            }
          ],
          "problemTypes": [
            {
              "descriptions": [
                {
                  "cweId": "CWE-89",
                  "description": "CWE-89 Improper Neutralization of Special Elements used in an SQL Command (\u0027SQL Injection\u0027)",
                  "lang": "en",
                  "type": "CWE"
                },
                {
                  "cweId": "CWE-116",
                  "description": "CWE-116 Improper Encoding or Escaping of Output",
                  "lang": "en",
                  "type": "CWE"
                }
              ]
            }
          ],
          "providerMetadata": {
            "dateUpdated": "2026-06-18T23:37:16.202Z",
            "orgId": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
            "shortName": "PostgreSQL"
          },
          "references": [
            {
              "tags": [
                "issue-tracking"
              ],
              "url": "https://github.com/pgadmin-org/pgadmin4/issues/10078"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/pgadmin-org/pgadmin4/commit/658bb585d"
            },
            {
              "tags": [
                "patch"
              ],
              "url": "https://github.com/pgadmin-org/pgadmin4/commit/2ae0d3610"
            }
          ],
          "source": {
            "discovery": "EXTERNAL"
          },
          "title": "pgAdmin 4: SQL injection in COMMENT ON ... IS \u0027\u003cdescription\u003e\u0027 rendering across dialog templates"
        }
      },
      "cveMetadata": {
        "assignerOrgId": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
        "assignerShortName": "PostgreSQL",
        "cveId": "CVE-2026-12044",
        "datePublished": "2026-06-18T23:37:16.202Z",
        "dateReserved": "2026-06-11T20:40:05.751Z",
        "dateUpdated": "2026-06-22T18:37:05.999Z",
        "state": "PUBLISHED"
      },
      "dataType": "CVE_RECORD",
      "dataVersion": "5.2"
    }
  }
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.