CVE-2026-17350 (GCVE-0-2026-17350)
Vulnerability from cvelistv5 – Published: 2026-07-31 16:00 – Updated: 2026-07-31 17:05
VLAI
EPSS
VEX
Title
pgAdmin 4: Tool permission bypass via backend routes and Socket.IO handlers
Summary
The per-tool permission system (custom roles / role-based tool permissions, introduced in pgAdmin 4 9.3) did not enforce its permission check consistently. In SERVER mode, pgAdmin 4 gates each tool behind a per-tool Flask-Security permission, but the permission decorator (permissions_required) was applied only to a single "front door" route per tool. Every other backend route and Socket.IO handler in that tool's workflow relied solely on pga_login_required/socket_login_required, which check authentication but not the tool permission.
The reporter verified three cases against a test build: (1) a user without tools_query_tool permission received 403 on the protected sqleditor initialization route, but the same session went on to connect the server, initialize the viewdata backend chain, and retrieve real table row content; (2) a user without tools_grant_wizard received 403 on the protected acl route, but the same session still enumerated grantable objects, generated GRANT SQL, and successfully applied it -- confirmed database-side via has_table_privilege(); (3) a user without tools_schema_diff received 403 on the protected panel route, but the same session initialized schema diff, enumerated and connected databases, and obtained real DDL differences via the compare_database Socket.IO handler. The reporter also confirmed a related but distinct issue: a non-owner triggering /misc/workspace/adhoc_connect_server against an administrator-owned shared server caused pgAdmin to persist a new server row still owned by the administrator (user_id/shared unchanged from the source), even though the connection attempt itself reported failure.
During remediation, the same front-door-only permission gap was found to also affect the ERD, PSQL, and Debugger tools, and the Backup, Restore, Maintenance, and Import/Export blueprints, none of which were part of the original report; these were fixed using the same pattern as an extension of the reported defect class.
An authenticated user who had valid pgAdmin login and a stored, working database connection, but had been explicitly denied a specific tool's permission by an administrator, could therefore still drive that tool end-to-end through its other routes and sockets, including obtaining an interactive psql session over the /pty Socket.IO namespace and invoking backup/restore/maintenance/import-export jobs.
Because the bypass only restores access to tools operating over the user's own already-authenticated database connection, it does not grant the user any database privilege they did not already hold; it circumvents pgAdmin's own tool-level access-control policy (an organisational segregation-of-duties control, separate from database-level authorization), letting a user reach a pgAdmin feature an administrator intended to withhold from them, using capabilities their existing database role already permits through other means.
Socket.IO event handlers had no permission-aware equivalent of permissions_required; only socket_login_required existed, checking authentication but not the tool permission.
Fix adds a socket_permissions_required decorator (mirroring permissions_required, honouring the Administrator bypass, reading permissions via has_permission()) and applies it, alongside permissions_required, as the outermost decorator on every backend route and Socket.IO handler for the affected tools. Regression tests assert 403 on every gated route and socket handler for a permission-less user.
This issue affects pgAdmin 4 in SERVER mode: from 9.3 before 9.17.
Severity
5.4 (Medium)
SSVC
Exploitation: none
Automatable: no
Technical Impact: partial
CISA Coordinator · CISA-ADP (v2.0.3)
Decision recorded 2026-07-31 17:01 UTC
CWE
- CWE-862 - Missing Authorization
Assigner
References
5 references
Impacted products
1 product
| Vendor | Product | Version | CPE status | |
|---|---|---|---|---|
| pgadmin.org | pgAdmin 4 |
Affected:
9.3 , < 9.17
(custom)
|
guessed |
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-17350",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-07-31T17:01:15.662893Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-07-31T17:05:44.535Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"defaultStatus": "unaffected",
"modules": [
"Query Tool",
"Grant Wizard",
"Schema Diff",
"ERD Tool",
"PSQL Tool",
"Debugger",
"Backup",
"Restore",
"Maintenance",
"Import/Export"
],
"product": "pgAdmin 4",
"programFiles": [
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/authenticate/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/sqleditor/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/grant_wizard/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/schema_diff/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/erd/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/psql/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/debugger/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/backup/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/restore/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/maintenance/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/import_export/__init__.py"
],
"repo": "https://github.com/pgadmin-org/pgadmin4",
"vendor": "pgadmin.org",
"versions": [
{
"lessThan": "9.17",
"status": "affected",
"version": "9.3",
"versionType": "custom"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "LXY (@TA-MU-TA)"
},
{
"lang": "en",
"type": "remediation developer",
"value": "Dave Page \u003cpage@pgadmin.org\u003e"
},
{
"lang": "en",
"type": "remediation developer",
"value": "Ashesh Vashi \u003cashesh.vashi@enterprisedb.com\u003e"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "Ashesh Vashi \u003cashesh.vashi@enterprisedb.com\u003e"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "Kundan Sable \u003ckundan.sable@enterprisedb.com\u003e"
}
],
"descriptions": [
{
"lang": "en",
"value": "The per-tool permission system (custom roles / role-based tool permissions, introduced in pgAdmin 4 9.3) did not enforce its permission check consistently. In SERVER mode, pgAdmin 4 gates each tool behind a per-tool Flask-Security permission, but the permission decorator (permissions_required) was applied only to a single \"front door\" route per tool. Every other backend route and Socket.IO handler in that tool\u0027s workflow relied solely on pga_login_required/socket_login_required, which check authentication but not the tool permission.\n\nThe reporter verified three cases against a test build: (1) a user without tools_query_tool permission received 403 on the protected sqleditor initialization route, but the same session went on to connect the server, initialize the viewdata backend chain, and retrieve real table row content; (2) a user without tools_grant_wizard received 403 on the protected acl route, but the same session still enumerated grantable objects, generated GRANT SQL, and successfully applied it -- confirmed database-side via has_table_privilege(); (3) a user without tools_schema_diff received 403 on the protected panel route, but the same session initialized schema diff, enumerated and connected databases, and obtained real DDL differences via the compare_database Socket.IO handler. The reporter also confirmed a related but distinct issue: a non-owner triggering /misc/workspace/adhoc_connect_server against an administrator-owned shared server caused pgAdmin to persist a new server row still owned by the administrator (user_id/shared unchanged from the source), even though the connection attempt itself reported failure.\n\nDuring remediation, the same front-door-only permission gap was found to also affect the ERD, PSQL, and Debugger tools, and the Backup, Restore, Maintenance, and Import/Export blueprints, none of which were part of the original report; these were fixed using the same pattern as an extension of the reported defect class.\n\nAn authenticated user who had valid pgAdmin login and a stored, working database connection, but had been explicitly denied a specific tool\u0027s permission by an administrator, could therefore still drive that tool end-to-end through its other routes and sockets, including obtaining an interactive psql session over the /pty Socket.IO namespace and invoking backup/restore/maintenance/import-export jobs.\n\nBecause the bypass only restores access to tools operating over the user\u0027s own already-authenticated database connection, it does not grant the user any database privilege they did not already hold; it circumvents pgAdmin\u0027s own tool-level access-control policy (an organisational segregation-of-duties control, separate from database-level authorization), letting a user reach a pgAdmin feature an administrator intended to withhold from them, using capabilities their existing database role already permits through other means.\n\nSocket.IO event handlers had no permission-aware equivalent of permissions_required; only socket_login_required existed, checking authentication but not the tool permission.\n\nFix adds a socket_permissions_required decorator (mirroring permissions_required, honouring the Administrator bypass, reading permissions via has_permission()) and applies it, alongside permissions_required, as the outermost decorator on every backend route and Socket.IO handler for the affected tools. Regression tests assert 403 on every gated route and socket handler for a permission-less user.\n\nThis issue affects pgAdmin 4 in SERVER mode: from 9.3 before 9.17."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 5.4,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "LOW",
"integrityImpact": "LOW",
"privilegesRequired": "LOW",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"version": "3.1"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "Threat model: an authenticated SERVER-mode pgAdmin user, who already has a stored working database connection, has been explicitly denied one or more tool permissions by an administrator as an in-app segregation-of-duties policy. By calling the tool\u0027s non-entry-point backend routes or Socket.IO handlers directly (bypassing the UI, which only surfaces the gated entry point), the user regains functional access to the tool. Scope is Unchanged: the bypass stays entirely within pgAdmin\u0027s own authorization model and the database connection the user already legitimately holds -- it does not grant any new database privilege, only pgAdmin\u0027s own UI/feature access that an administrator intended to withhold. Confidentiality/Integrity impact is capped at Low because the marginal capability gained beyond the user\u0027s own pre-existing database role is bounded (an alternate in-app SQL client / GRANT-issuing UI / interactive psql session against a database they can already reach); Availability is None since none of the bypassed routes on their own cause denial of service."
}
]
},
{
"cvssV4_0": {
"baseScore": 5.3,
"baseSeverity": "MEDIUM",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
"version": "4.0"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "Same reasoning as the CVSS 3.1 entry: an authenticated, permission-denied user drives ungated backend routes/sockets to use a tool they were blocked from, but the marginal capability gained is bounded by the database privileges the user already legitimately holds. VC:L/VI:L reflect that bounded marginal gain; VA:N since none of the bypassed routes independently cause denial of service. No subsequent system beyond pgAdmin\u0027s own authorization scope is affected, so SC:N/SI:N/SA:N."
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-862",
"description": "CWE-862 Missing Authorization",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-07-31T16:00:03.744Z",
"orgId": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"shortName": "PostgreSQL"
},
"references": [
{
"tags": [
"issue-tracking"
],
"url": "https://github.com/pgadmin-org/pgadmin4/issues/10190"
},
{
"tags": [
"patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/461c3afba92baad37c70a6fbd52d205d13a9de53"
},
{
"tags": [
"patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/d36bd8dc96812c716664feac533d240544e70adc"
},
{
"tags": [
"patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/ba1984718ad703011740ad48cb9b82402b89cc2c"
},
{
"tags": [
"patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/64a9cdbd6a240a962144f84418beaf9e66419779"
}
],
"source": {
"discovery": "EXTERNAL"
},
"title": "pgAdmin 4: Tool permission bypass via backend routes and Socket.IO handlers"
}
},
"cveMetadata": {
"assignerOrgId": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"assignerShortName": "PostgreSQL",
"cveId": "CVE-2026-17350",
"datePublished": "2026-07-31T16:00:03.744Z",
"dateReserved": "2026-07-25T02:52:56.800Z",
"dateUpdated": "2026-07-31T17:05:44.535Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2",
"vulnerability-lookup:meta": {
"epss": {
"cve": "CVE-2026-17350",
"date": "2026-10-31",
"epss": "0.00376",
"percentile": "0.31575"
},
"nvd": {
"cve": {
"affected": [
{
"affectedData": [
{
"defaultStatus": "unaffected",
"modules": [
"Query Tool",
"Grant Wizard",
"Schema Diff",
"ERD Tool",
"PSQL Tool",
"Debugger",
"Backup",
"Restore",
"Maintenance",
"Import/Export"
],
"product": "pgAdmin 4",
"programFiles": [
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/authenticate/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/sqleditor/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/grant_wizard/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/schema_diff/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/erd/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/psql/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/debugger/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/backup/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/restore/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/maintenance/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/import_export/__init__.py"
],
"repo": "https://github.com/pgadmin-org/pgadmin4",
"vendor": "pgadmin.org",
"versions": [
{
"lessThan": "9.17",
"status": "affected",
"version": "9.3",
"versionType": "custom"
}
]
}
],
"source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007"
}
],
"configurations": [
{
"nodes": [
{
"cpeMatch": [
{
"criteria": "cpe:2.3:a:pgadmin:pgadmin_4:*:*:*:*:*:postgresql:*:*",
"matchCriteriaId": "B2DF41A8-E84A-4F15-92FE-8973E58F1011",
"versionEndExcluding": "9.17",
"versionStartIncluding": "9.3",
"vulnerable": true
}
],
"negate": false,
"operator": "OR"
}
]
}
],
"cveTags": [],
"descriptions": [
{
"lang": "en",
"value": "The per-tool permission system (custom roles / role-based tool permissions, introduced in pgAdmin 4 9.3) did not enforce its permission check consistently. In SERVER mode, pgAdmin 4 gates each tool behind a per-tool Flask-Security permission, but the permission decorator (permissions_required) was applied only to a single \"front door\" route per tool. Every other backend route and Socket.IO handler in that tool\u0027s workflow relied solely on pga_login_required/socket_login_required, which check authentication but not the tool permission.\n\nThe reporter verified three cases against a test build: (1) a user without tools_query_tool permission received 403 on the protected sqleditor initialization route, but the same session went on to connect the server, initialize the viewdata backend chain, and retrieve real table row content; (2) a user without tools_grant_wizard received 403 on the protected acl route, but the same session still enumerated grantable objects, generated GRANT SQL, and successfully applied it -- confirmed database-side via has_table_privilege(); (3) a user without tools_schema_diff received 403 on the protected panel route, but the same session initialized schema diff, enumerated and connected databases, and obtained real DDL differences via the compare_database Socket.IO handler. The reporter also confirmed a related but distinct issue: a non-owner triggering /misc/workspace/adhoc_connect_server against an administrator-owned shared server caused pgAdmin to persist a new server row still owned by the administrator (user_id/shared unchanged from the source), even though the connection attempt itself reported failure.\n\nDuring remediation, the same front-door-only permission gap was found to also affect the ERD, PSQL, and Debugger tools, and the Backup, Restore, Maintenance, and Import/Export blueprints, none of which were part of the original report; these were fixed using the same pattern as an extension of the reported defect class.\n\nAn authenticated user who had valid pgAdmin login and a stored, working database connection, but had been explicitly denied a specific tool\u0027s permission by an administrator, could therefore still drive that tool end-to-end through its other routes and sockets, including obtaining an interactive psql session over the /pty Socket.IO namespace and invoking backup/restore/maintenance/import-export jobs.\n\nBecause the bypass only restores access to tools operating over the user\u0027s own already-authenticated database connection, it does not grant the user any database privilege they did not already hold; it circumvents pgAdmin\u0027s own tool-level access-control policy (an organisational segregation-of-duties control, separate from database-level authorization), letting a user reach a pgAdmin feature an administrator intended to withhold from them, using capabilities their existing database role already permits through other means.\n\nSocket.IO event handlers had no permission-aware equivalent of permissions_required; only socket_login_required existed, checking authentication but not the tool permission.\n\nFix adds a socket_permissions_required decorator (mirroring permissions_required, honouring the Administrator bypass, reading permissions via has_permission()) and applies it, alongside permissions_required, as the outermost decorator on every backend route and Socket.IO handler for the affected tools. Regression tests assert 403 on every gated route and socket handler for a permission-less user.\n\nThis issue affects pgAdmin 4 in SERVER mode: from 9.3 before 9.17."
},
{
"lang": "es",
"value": "El sistema de permisos por herramienta (roles personalizados / permisos de herramienta basados en roles, introducido en pgAdmin 4 9.3) no aplicaba su verificaci\u00f3n de permisos de manera consistente. En modo SERVIDOR, pgAdmin 4 protege cada herramienta detr\u00e1s de un permiso Flask-Security por herramienta, pero el decorador de permisos (permissions_required) se aplicaba solo a una \u00fanica ruta de \u2019puerta principal\u2019 por herramienta. Cada otra ruta de backend y gestor de Socket.IO en el flujo de trabajo de esa herramienta depend\u00eda \u00fanicamente de pga_login_required/socket_login_required, que verifican la autenticaci\u00f3n pero no el permiso de la herramienta.\n\nEl reportero verific\u00f3 tres casos contra una compilaci\u00f3n de prueba: (1) un usuario sin permiso tools_query_tool recibi\u00f3 403 en la ruta de inicializaci\u00f3n protegida de sqleditor, pero la misma sesi\u00f3n procedi\u00f3 a conectar el servidor, inicializar la cadena de backend de viewdata y recuperar contenido real de filas de tabla; (2) un usuario sin tools_grant_wizard recibi\u00f3 403 en la ruta acl protegida, pero la misma sesi\u00f3n a\u00fan enumer\u00f3 objetos otorgables, gener\u00f3 SQL GRANT y lo aplic\u00f3 con \u00e9xito -- confirmado del lado de la base de datos a trav\u00e9s de has_table_privilege(); (3) un usuario sin tools_schema_diff recibi\u00f3 403 en la ruta de panel protegida, pero la misma sesi\u00f3n inicializ\u00f3 la diferencia de esquema, enumer\u00f3 y conect\u00f3 bases de datos, y obtuvo diferencias DDL reales a trav\u00e9s del gestor de Socket.IO compare_database. El reportero tambi\u00e9n confirm\u00f3 un problema relacionado pero distinto: un no propietario que activ\u00f3 /misc/workspace/adhoc_connect_server contra un servidor compartido propiedad de un administrador hizo que pgAdmin persistiera una nueva fila de servidor que segu\u00eda siendo propiedad del administrador (user_id/shared sin cambios desde el origen), a pesar de que el propio intento de conexi\u00f3n inform\u00f3 un fallo.\n\nDurante la remediaci\u00f3n, se encontr\u00f3 que la misma brecha de permisos solo de puerta principal tambi\u00e9n afectaba a las herramientas ERD, PSQL y Debugger, y a los planos de Backup, Restore, Maintenance e Import/Export, ninguno de los cuales formaba parte del informe original; estos se corrigieron utilizando el mismo patr\u00f3n como una extensi\u00f3n de la clase de defecto reportada.\n\nUn usuario autenticado que ten\u00eda un inicio de sesi\u00f3n pgAdmin v\u00e1lido y una conexi\u00f3n de base de datos almacenada y funcional, pero a quien un administrador le hab\u00eda denegado expl\u00edcitamente el permiso de una herramienta espec\u00edfica, por lo tanto, a\u00fan pod\u00eda manejar esa herramienta de extremo a extremo a trav\u00e9s de sus otras rutas y sockets, incluyendo la obtenci\u00f3n de una sesi\u00f3n psql interactiva sobre el espacio de nombres /pty de Socket.IO e invocando trabajos de backup/restore/maintenance/import-export.\n\nDebido a que el bypass solo restaura el acceso a herramientas que operan sobre la propia conexi\u00f3n de base de datos ya autenticada del usuario, no otorga al usuario ning\u00fan privilegio de base de datos que no poseyera ya; elude la propia pol\u00edtica de control de acceso a nivel de herramienta de pgAdmin (un control de segregaci\u00f3n de funciones organizacional, separado de la autorizaci\u00f3n a nivel de base de datos), permitiendo a un usuario acceder a una caracter\u00edstica de pgAdmin que un administrador pretend\u00eda denegarles, utilizando capacidades que su rol de base de datos existente ya permite por otros medios.\n\nLos gestores de eventos de Socket.IO no ten\u00edan un equivalente de permissions_required consciente de los permisos; solo exist\u00eda socket_login_required, que verificaba la autenticaci\u00f3n pero no el permiso de la herramienta.\n\nLa correcci\u00f3n a\u00f1ade un decorador socket_permissions_required (reflejando permissions_required, respetando el bypass del Administrador, leyendo permisos a trav\u00e9s de has_permission()) y lo aplica, junto con permissions_required, como el decorador m\u00e1s externo en cada ruta de backend y gestor de Socket.IO para las herramientas afectadas. Las pruebas de regresi\u00f3n afirman 403 en cada ruta protegida y gestor de socket para un usuario sin permisos.\n\nEste problema afecta a pgAdmin 4 en modo --TRUNCADO--"
}
],
"id": "CVE-2026-17350",
"lastModified": "2026-10-23T23:10:00.157",
"metrics": {
"cvssMetricV31": [
{
"cvssData": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 5.4,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "LOW",
"integrityImpact": "LOW",
"privilegesRequired": "LOW",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"version": "3.1"
},
"exploitabilityScore": 2.8,
"impactScore": 2.5,
"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": 5.3,
"baseSeverity": "MEDIUM",
"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:L/VI:L/VA:N/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": "NONE",
"vulnConfidentialityImpact": "LOW",
"vulnIntegrityImpact": "LOW",
"vulnerabilityResponseEffort": "NOT_DEFINED"
},
"source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"type": "Secondary"
}
],
"ssvcV203": [
{
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"ssvcData": {
"id": "CVE-2026-17350",
"options": [
{
"exploitation": "none"
},
{
"automatable": "no"
},
{
"technicalImpact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-07-31T17:01:15.662893Z",
"version": "2.0.3"
}
}
]
},
"published": "2026-07-31T16:16:59.617",
"references": [
{
"source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"tags": [
"Patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/461c3afba92baad37c70a6fbd52d205d13a9de53"
},
{
"source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"tags": [
"Patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/64a9cdbd6a240a962144f84418beaf9e66419779"
},
{
"source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"tags": [
"Patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/ba1984718ad703011740ad48cb9b82402b89cc2c"
},
{
"source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"tags": [
"Patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/d36bd8dc96812c716664feac533d240544e70adc"
},
{
"source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"tags": [
"Issue Tracking",
"Patch",
"Vendor Advisory"
],
"url": "https://github.com/pgadmin-org/pgadmin4/issues/10190"
}
],
"sourceIdentifier": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"vulnStatus": "Analyzed",
"weaknesses": [
{
"description": [
{
"lang": "en",
"value": "CWE-862"
}
],
"source": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"type": "Secondary"
}
]
}
},
"redhat_vex": {
"current_release_date": "2026-08-15T07:23:43+00:00",
"cve": "CVE-2026-17350",
"id": "CVE-2026-17350",
"initial_release_date": "2026-07-31T16:00:03.744000+00:00",
"product_status:known_not_affected": "1",
"source": "Red Hat CSAF VEX",
"status": "final",
"title": "pgadmin4: pgAdmin 4: Permission bypass allows authenticated users to access restricted tools",
"url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-17350.json",
"version": "3"
},
"vulnrichment": {
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-17350",
"options": [
{
"Exploitation": "none"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-07-31T17:01:15.662893Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-07-31T17:01:24.861Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"defaultStatus": "unaffected",
"modules": [
"Query Tool",
"Grant Wizard",
"Schema Diff",
"ERD Tool",
"PSQL Tool",
"Debugger",
"Backup",
"Restore",
"Maintenance",
"Import/Export"
],
"product": "pgAdmin 4",
"programFiles": [
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/authenticate/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/sqleditor/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/grant_wizard/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/schema_diff/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/erd/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/psql/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/debugger/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/backup/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/restore/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/maintenance/__init__.py",
"https://github.com/pgadmin-org/pgadmin4/blob/master/web/pgadmin/tools/import_export/__init__.py"
],
"repo": "https://github.com/pgadmin-org/pgadmin4",
"vendor": "pgadmin.org",
"versions": [
{
"lessThan": "9.17",
"status": "affected",
"version": "9.3",
"versionType": "custom"
}
]
}
],
"credits": [
{
"lang": "en",
"type": "finder",
"value": "LXY (@TA-MU-TA)"
},
{
"lang": "en",
"type": "remediation developer",
"value": "Dave Page \u003cpage@pgadmin.org\u003e"
},
{
"lang": "en",
"type": "remediation developer",
"value": "Ashesh Vashi \u003cashesh.vashi@enterprisedb.com\u003e"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "Ashesh Vashi \u003cashesh.vashi@enterprisedb.com\u003e"
},
{
"lang": "en",
"type": "remediation reviewer",
"value": "Kundan Sable \u003ckundan.sable@enterprisedb.com\u003e"
}
],
"descriptions": [
{
"lang": "en",
"value": "The per-tool permission system (custom roles / role-based tool permissions, introduced in pgAdmin 4 9.3) did not enforce its permission check consistently. In SERVER mode, pgAdmin 4 gates each tool behind a per-tool Flask-Security permission, but the permission decorator (permissions_required) was applied only to a single \"front door\" route per tool. Every other backend route and Socket.IO handler in that tool\u0027s workflow relied solely on pga_login_required/socket_login_required, which check authentication but not the tool permission.\n\nThe reporter verified three cases against a test build: (1) a user without tools_query_tool permission received 403 on the protected sqleditor initialization route, but the same session went on to connect the server, initialize the viewdata backend chain, and retrieve real table row content; (2) a user without tools_grant_wizard received 403 on the protected acl route, but the same session still enumerated grantable objects, generated GRANT SQL, and successfully applied it -- confirmed database-side via has_table_privilege(); (3) a user without tools_schema_diff received 403 on the protected panel route, but the same session initialized schema diff, enumerated and connected databases, and obtained real DDL differences via the compare_database Socket.IO handler. The reporter also confirmed a related but distinct issue: a non-owner triggering /misc/workspace/adhoc_connect_server against an administrator-owned shared server caused pgAdmin to persist a new server row still owned by the administrator (user_id/shared unchanged from the source), even though the connection attempt itself reported failure.\n\nDuring remediation, the same front-door-only permission gap was found to also affect the ERD, PSQL, and Debugger tools, and the Backup, Restore, Maintenance, and Import/Export blueprints, none of which were part of the original report; these were fixed using the same pattern as an extension of the reported defect class.\n\nAn authenticated user who had valid pgAdmin login and a stored, working database connection, but had been explicitly denied a specific tool\u0027s permission by an administrator, could therefore still drive that tool end-to-end through its other routes and sockets, including obtaining an interactive psql session over the /pty Socket.IO namespace and invoking backup/restore/maintenance/import-export jobs.\n\nBecause the bypass only restores access to tools operating over the user\u0027s own already-authenticated database connection, it does not grant the user any database privilege they did not already hold; it circumvents pgAdmin\u0027s own tool-level access-control policy (an organisational segregation-of-duties control, separate from database-level authorization), letting a user reach a pgAdmin feature an administrator intended to withhold from them, using capabilities their existing database role already permits through other means.\n\nSocket.IO event handlers had no permission-aware equivalent of permissions_required; only socket_login_required existed, checking authentication but not the tool permission.\n\nFix adds a socket_permissions_required decorator (mirroring permissions_required, honouring the Administrator bypass, reading permissions via has_permission()) and applies it, alongside permissions_required, as the outermost decorator on every backend route and Socket.IO handler for the affected tools. Regression tests assert 403 on every gated route and socket handler for a permission-less user.\n\nThis issue affects pgAdmin 4 in SERVER mode: from 9.3 before 9.17."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 5.4,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "LOW",
"integrityImpact": "LOW",
"privilegesRequired": "LOW",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"version": "3.1"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "Threat model: an authenticated SERVER-mode pgAdmin user, who already has a stored working database connection, has been explicitly denied one or more tool permissions by an administrator as an in-app segregation-of-duties policy. By calling the tool\u0027s non-entry-point backend routes or Socket.IO handlers directly (bypassing the UI, which only surfaces the gated entry point), the user regains functional access to the tool. Scope is Unchanged: the bypass stays entirely within pgAdmin\u0027s own authorization model and the database connection the user already legitimately holds -- it does not grant any new database privilege, only pgAdmin\u0027s own UI/feature access that an administrator intended to withhold. Confidentiality/Integrity impact is capped at Low because the marginal capability gained beyond the user\u0027s own pre-existing database role is bounded (an alternate in-app SQL client / GRANT-issuing UI / interactive psql session against a database they can already reach); Availability is None since none of the bypassed routes on their own cause denial of service."
}
]
},
{
"cvssV4_0": {
"baseScore": 5.3,
"baseSeverity": "MEDIUM",
"vectorString": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N",
"version": "4.0"
},
"format": "CVSS",
"scenarios": [
{
"lang": "en",
"value": "Same reasoning as the CVSS 3.1 entry: an authenticated, permission-denied user drives ungated backend routes/sockets to use a tool they were blocked from, but the marginal capability gained is bounded by the database privileges the user already legitimately holds. VC:L/VI:L reflect that bounded marginal gain; VA:N since none of the bypassed routes independently cause denial of service. No subsequent system beyond pgAdmin\u0027s own authorization scope is affected, so SC:N/SI:N/SA:N."
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-862",
"description": "CWE-862 Missing Authorization",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-07-31T16:00:03.744Z",
"orgId": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"shortName": "PostgreSQL"
},
"references": [
{
"tags": [
"issue-tracking"
],
"url": "https://github.com/pgadmin-org/pgadmin4/issues/10190"
},
{
"tags": [
"patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/461c3afba92baad37c70a6fbd52d205d13a9de53"
},
{
"tags": [
"patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/d36bd8dc96812c716664feac533d240544e70adc"
},
{
"tags": [
"patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/ba1984718ad703011740ad48cb9b82402b89cc2c"
},
{
"tags": [
"patch"
],
"url": "https://github.com/pgadmin-org/pgadmin4/commit/64a9cdbd6a240a962144f84418beaf9e66419779"
}
],
"source": {
"discovery": "EXTERNAL"
},
"title": "pgAdmin 4: Tool permission bypass via backend routes and Socket.IO handlers"
}
},
"cveMetadata": {
"assignerOrgId": "f86ef6dc-4d3a-42ad-8f28-e6d5547a5007",
"assignerShortName": "PostgreSQL",
"cveId": "CVE-2026-17350",
"datePublished": "2026-07-31T16:00:03.744Z",
"dateReserved": "2026-07-25T02:52:56.800Z",
"dateUpdated": "2026-07-31T17:05:44.535Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
}
}
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.
Experimental. This forecast is provided for visualization only and may change without notice. Do not use it for operational decisions.
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
Loading timeline…
Loading…
Loading…
The MITRE ATT&CK techniques below are AI-generated suggestions, inferred from the description of the
vulnerability by the CIRCL/vulnerability-attack-technique-classification-roberta-base
model, served locally by ML-Gateway.
They have not been verified by an analyst and are provided for guidance only.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
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.