RCE pgAdmin 4 via Import/Export, corrigé dans 9.19 (CVE-2026-116241)
pgAdmin 4 avant 9.19 laisse tout utilisateur d'Import/Export exécuter une commande TO PROGRAM psql et permet de deviner sans limite le mot de passe administrateur ; corrigé dans 9.19.
TL;DR
- L'outil Import/Export Data de pgAdmin 4 exécute une commande du système d'exploitation d'un attaquant sur l'hôte pgAdmin quand un export de requête forgée passe sa garde de parenthèses : CVE-2026-116241, CVSS v3.1 9,9 Critical selon le CNA PostgreSQL. La permission de l'outil est accordée aux non-administrateurs par défaut.
- La même publication corrige une deuxième faille critique, CVE-2026-116238 : quatre contournements de la limite de tentatives de connexion permettent à un attaquant non authentifié de deviner le mot de passe de l'administrateur sans limite en mode serveur. Yonghwa Lee de Xint by Theori a signalé l'injection.
- Les 14 failles du lot, publiées le 30 octobre 2026, sont toutes corrigées dans pgAdmin 4 9.19, publié le 29 octobre 2026. Toutes les versions antérieures, jusqu'aux premières publications de l'outil, sont concernées.
- Aucun exploit public ni aucune exploitation n'a été signalé, et aucune des deux failles critiques n'est dans le catalogue KEV (Known Exploited Vulnerabilities) de la CISA au 31 octobre 2026. Passez à 9.19 maintenant ; les changements de verrouillage du mode serveur demandent une préparation, listés dans « Comment corriger ? ».
Suis-je concerné ?
Pour l'injection de commandes, CVE-2026-116241, toutes ces conditions doivent être réunies :
- Vous exécutez pgAdmin 4 à une version quelconque avant 9.19, dans tout déploiement : mode serveur, mode bureau ou conteneur.
- Un utilisateur auquel vous ne faites pas totalement confiance peut utiliser l'outil Import/Export Data sur une connexion à un serveur. La permission
tools_import_export_dataest accordée aux non-administrateurs par défaut. - Le rôle de base de données de cet utilisateur sur le serveur cible peut exécuter
SET standard_conforming_strings = offpour lui-même, ce qu'un rôle ordinaire peut faire.
Le code est présent dans les deux modes, mais le chemin d'attaquant distant est le mode serveur, où d'autres personnes détiennent des comptes pgAdmin. Le mode bureau ne l'expose qu'à celui qui peut déjà piloter le poste.
Pour le contournement du verrouillage, CVE-2026-116238, toutes ces conditions doivent être réunies :
- Vous exécutez pgAdmin 4 en mode serveur, à toute version avant 9.19. Le mode bureau n'est pas concerné.
- L'instance est atteignable par quiconque n'est pas de confiance totale.
- Un compte interne existe, ce qui est le défaut : l'administrateur initial en est un, et
MAX_LOGIN_ATTEMPTSvaut 3 par défaut.
| Branche | Versions concernées | Version corrigée |
|---|---|---|
| pgAdmin 4 | toute publication avant 9.19 | 9.19 (2026-10-29) |
Le lot est volumineux : l'autorité d'attribution des numéros CVE (CNA) PostgreSQL a publié quatorze enregistrements le concernant, deux critical, dix high, un medium et un low, tous corrigés dans la même publication.
Vérifiez ce que vous exécutez. Le conteneur journalise sa version au démarrage :
docker logs pgadmin 2>&1 | grep -m1 "Starting pgAdmin"
Pour une installation pip :
pip show pgadmin4 | head -2
Dans l'interface, la boîte À propos du menu Aide affiche la version. Pour l'image Docker, le tag la porte aussi, mais les tags flottants comme latest demandent l'un des contrôles de version de cette section.
Comment corriger ?
Mettez à jour chaque déploiement vers 9.19, la seule version corrigée :
- Sauvegardez la base de données de configuration de pgAdmin, qui contient les utilisateurs, les rôles et les définitions de serveurs.
- Avant toute mise à jour ou tout redémarrage, consultez les quatre changements du mode serveur ci-dessous et configurez les listes autorisées. La migration des commandes de mot de passe peut supprimer définitivement les commandes sans correspondance.
- Mettez à jour le déploiement. Pour pip :
pip install --upgrade pgadmin4==9.19
- Avec Docker Compose, définissez d’abord l’image du service pgadmin sur
dpage/pgadmin4:9.19dans le fichier Compose, puis téléchargez l’image et recréez ce service :
docker compose pull pgadmin
docker compose up -d pgadmin
Le mode serveur dans 9.19 verrouille quatre choses, chacune avec un coût à prévoir :
- Les commandes d'exécution de mot de passe en texte libre ne s'exécutent plus.
ENABLE_SERVER_PASS_EXEC_CMDest obsolète et ignoré. Les administrateurs listent désormais les commandes permises par nom dansSERVER_PASSEXEC_COMMANDS; au premier démarrage après la mise à jour, chaque commande existante qui correspond exactement à une entrée listée est convertie en son nom, et toute autre commande est effacée, sans retour possible. SiSERVER_PASSEXEC_COMMANDSest vide, la conversion est reportée et un avertissement est journalisé à chaque démarrage. - Les services de connexion sont restreints.
SERVER_MODE_ALLOWED_SERVICESest vide par défaut, donc les serveurs configurés avec un service de connexion cessent de se connecter jusqu'à ce que l'administrateur le liste. Lister un service partage ses identifiants avec tous les utilisateurs de pgAdmin. - Les tunnels SSH ne lisent plus l'identité SSH propre du compte de service. En mode serveur, ils n'utilisent plus ses clés
~/.ssh, son fichier de configuration ni son agent, donc un tunnel qui en dépendait doit recevoir un fichier d'identité dans le stockage de l'utilisateur ou un mot de passe sur le serveur. - Sur Python 3.10 et suivants, pgAdmin exige Flask-Security-Too 5.8.3 ou plus, qui corrige une redirection ouverte, GHSA-ccgp-pv8r-95wm. Cela compte pour quiconque installe le wheel dans un environnement existant.
Pour les déploiements qui ne peuvent pas encore mettre à jour, une solution de contournement ferme le chemin d'injection de CVE-2026-116241 :
- Retirez la permission Import/Export Data à chaque rôle auquel vous ne feriez pas confiance pour des commandes sur l'hôte. En mode serveur, un administrateur retire la permission
tools_import_export_dataaux rôles dans la gestion des rôles de l'interface. - La solution couvre uniquement CVE-2026-116241. Elle ne fait rien pour le contournement du verrouillage, CVE-2026-116238, ni pour les douze autres failles, et elle retire l'outil d'export aux utilisateurs qui en ont besoin.
Pour le contournement du verrouillage sans mise à jour, restreignez qui peut atteindre la page de connexion de pgAdmin : un VPN, une liste d'adresses autorisées sur le reverse proxy, ou une source d'authentification qui s'interpose entièrement. Aucune ne change les quatre contournements ; chacune retire l'accessibilité non authentifiée dont ils ont besoin.
Quelle est la cause racine ?
L'outil Import/Export Data enveloppe la requête SQL d'un utilisateur dans une commande psql \copy (...) TO et la valide par une vérification manuelle d'équilibre des parenthèses dont les règles de citation diffèrent de celles de psql. La route, POST /import_export/job/<sid>, n'est protégée que par la permission ordinaire tools_import_export_data, et elle passe la ligne \copy rendue à psql.
La garde, _is_query_parens_balanced() dans web/pgadmin/tools/import_export/__init__.py, parcourait la requête pour s'assurer que rien ne pouvait fermer prématurément les parenthèses de l'enveloppe. Avant 9.19, son traitement des identifiants entre guillemets doubles était :
# Double-quoted identifier: skip to closing quote
# (handles "" escape sequences — matches strtokx behavior)
if ch == '"':
i += 1
while i < length:
if query[i] == '"':
Le commentaire revendique une correspondance avec le découpeur de jetons de psql, strtokx, mais les deux divergent sur l'antislash. strtokx prend son caractère d'échappement du paramètre standard_conforming_strings de la connexion et l'applique à le guillemet qui a ouvert le jeton. Avec ce paramètre désactivé, un antislash avant un guillemet double est un guillemet échappé pour psql, alors que la garde de pgAdmin le traite comme fermant l'identifiant. Le message du commit correctif énonce la conséquence : dans un export de requête comme SELECT "a\"b"c) TO PROGRAM 'cmd' x", la parenthèse fermante cachée au contrôle termine l'enveloppe \copy (...), et la clause TO PROGRAM qui suit s'exécute via popen() sur l'hôte pgAdmin avant que le serveur de base de données ne rejette l'instruction.
Le contournement du verrouillage, CVE-2026-116238, a quatre lacunes distinctes dans un même mécanisme. pgAdmin compte les échecs de connexion et verrouille un compte interne après MAX_LOGIN_ATTEMPTS, mais Flask-Security-Too enregistre ses propres vues à côté de celles de pgAdmin, et les soumissions à ces vues contournaient entièrement le comptage et les contrôles de pgAdmin : /login connectait sans compter les échecs, /verify vérifiait le mot de passe d'un utilisateur connecté sans limite, /change changeait un mot de passe sans la permission de pgAdmin, et /reset appliquait des réinitialisations pour des comptes non internes ou verrouillés. Un nom d'utilisateur saisi dans une autre casse n'était ni compté ni vérifié contre le verrou. Avec l'authentification LDAP ou webserver aussi configurée, un mot de passe interne erroné n'était jamais compté.
Comment la faille est-elle exploitée ?
Aucun code de preuve de concept n'est public et aucune exploitation n'a été signalée. Les étapes viennent des enregistrements CVE et du commit correctif :
- L'attaquant a besoin d'un compte pgAdmin avec la permission Import/Export Data, que les non-administrateurs détiennent par défaut, et d'un rôle ordinaire sur un serveur de base de données cible.
- Sur cette connexion, l'attaquant exécute
SET standard_conforming_strings = offpour lui-même, ce qu'un rôle ordinaire peut faire. - Il soumet un export fondé sur requête dont la requête cache une parenthèse fermante dans un identifiant entre guillemets doubles contenant un antislash, suivi d'une clause
TO PROGRAM. La forme d'exemple de l'enregistrement CVE estSELECT "a\"b"c) TO PROGRAM 'cmd' x". - Le contrôle d'équilibre de pgAdmin, lisant l'antislash différemment de psql, estime les parenthèses équilibrées et accepte le travail.
- Le
\copy (...) TO PROGRAM 'cmd'rendu s'exécute dans psql. Parce que psql applique la règle d'échappement de la connexion, la parenthèse cachée ferme l'enveloppe. psql exécute alors la commandeTO PROGRAMviapopen()sur l'hôte pgAdmin, sous le compte du système d'exploitation qui exécute pgAdmin, avant que le serveur de base de données ne rejette l'instruction.
Pour le contournement du verrouillage, les étapes sont plus simples :
- Un attaquant non authentifié atteint la page de connexion de pgAdmin d'un déploiement en mode serveur.
- Il soumet des essais de mot de passe contre le compte administrateur interne par l'un des chemins non comptés. Ces chemins sont la propre vue
/loginde Flask-Security, un nom d'utilisateur saisi dans une autre casse, et un mot de passe interne erroné alors que l'authentification LDAP ou webserver est aussi configurée. - Aucun verrou ne s'engage jamais, donc les essais continuent sans limite jusqu'à ce que le mot de passe cède.
Quel est le flux de l'attaque ?
Un compte pgAdmin ordinaire transforme un export de requête en commande du système d'exploitation en désynchronisant le contrôle de citation de pgAdmin par rapport au découpeur de jetons de psql. Le contrôle qui échoue est la lecture de l'antislash par la garde de parenthèses.
Import/Export et un rôle ordinaire"] --> B["SET standard_conforming_strings=off
sur la connexion"] B --> C["La requête d'export cache ) dans un
identifiant à antislash + TO PROGRAM"] C --> D["échec du contrôle : la garde lit
l'antislash différemment de psql"] D --> E["\\copy (...) TO PROGRAM
s'exécute dans psql via popen"] E --> F["Commande arbitraire exécutée sur
l'hôte pgAdmin"]
Que peut faire un attaquant ?
L'injection, CVE-2026-116241, exécute une commande arbitraire du système d'exploitation sur l'hôte qui exécute pgAdmin, sous le compte qui exécute pgAdmin. Dans le conteneur officiel, il s’agit normalement du compte pgadmin non privilégié (UID 5050) ; dans une installation pip ou paquet, c'est celui qui exécute le serveur web. La commande s'exécute avant que le serveur de base de données ne rejette l'instruction, donc l'attaque n'exige aucun privilège sur la base au-delà d'un rôle ordinaire. Le CNA PostgreSQL la note 9,9 Critical, CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H, et 9,4 Critical en CVSS v4.0, CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H.
Le contournement du verrouillage, CVE-2026-116238, donne à un attaquant non authentifié des essais de mot de passe illimités contre tout compte interne dans la configuration par défaut, y compris celui de l'administrateur. Le même CVE consigne qu'une réinitialisation de mot de passe pouvait être envoyée pour, ou appliquée à, un compte d'une autre source d'authentification quand des comptes partagent une adresse e-mail. Ses scores sont 8,1 High en CVSS v3.1, CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H, et 9,2 Critical en CVSS v4.0, CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N.
Aucune des deux failles n'a d'exploit public, de signalement d'exploitation ou d'entrée KEV au 31 octobre 2026. L'injection est le troisième tour d'une même famille. CVE-2025-13780 exécutait des commandes via les restaurations de dumps au format PLAIN jusqu'à 9.10. CVE-2026-17566, corrigé dans 9.17 en juillet 2026, fermait l'écart du même garde-fou d'export pour les chaînes entre apostrophes. Celui-ci le rouvre via les identifiants entre guillemets doubles.
Pourquoi le correctif fonctionne-t-il ?
Le commit 9be31a019f78, « Reject a backslash in a double-quoted identifier in the Import/Export Data query export », change la garde pour refuser le cas ambigu au lieu de deviner une lecture :
- # Double-quoted identifier: skip to closing quote
- # (handles "" escape sequences — matches strtokx behavior)
+ # Double-quoted identifier: skip to closing quote.
+ # Handles the "" escape sequence to match psql's strtokx.
+ # A bare backslash is rejected outright for the same reason
+ # as in a single-quoted string (see docstring): strtokx
+ # applies its escape character to whichever quote opened the
+ # token, so under standard_conforming_strings=off a \" is an
+ # escaped quote to psql but a closing quote to us.
if ch == '"':
i += 1
while i < length:
- if query[i] == '"':
+ if query[i] == '\\':
+ return False # ambiguous escape: reject
+ elif query[i] == '"':
if i + 1 < length and query[i + 1] == '"':
i += 2 # escaped quote
else:
Le correctif rejette aussi un antislash ou un octet nul dans les noms de schéma, de table ou de colonne d'un export ou d'un import. Ces noms atteignent la même commande \copy via qtIdent(). Les tests de régression du commit épinglent les deux directions de la désynchronisation. Chacune porte une charge TO PROGRAM 'echo pwned' désormais refusée : le cas des apostrophes du correctif de juillet, et le cas des guillemets doubles de celui-ci.
Le contournement du verrouillage est corrigé par quatre changements :
- les soumissions aux propres vues de Flask-Security sont refusées avec HTTP 405
- chaque échec de connexion interne est compté, quelle que soit la casse du nom d'utilisateur et quelles que soient les autres sources d'authentification configurées
- une connexion qui publie un mot de passe ne bascule plus vers une source qui ne le vérifie pas
- seule l'authentification interne est verrouillée, donc un compte interne verrouillé ne bloque plus les autres sources
Le correctif de l'injection est complet pour ce garde-fou. Tout jeton cité qu'une commande \copy peut contenir, chaînes entre apostrophes comme identifiants entre guillemets doubles, refuse désormais l'antislash. Les noms de paramètres sont couverts par le rejet d'antislash et d'octet nul dans qtIdent(). Le correctif du verrouillage ferme les quatre chemins de contournement décrits par l'enregistrement CVE.
Comment vérifier si j'ai été compromis ?
Pour l'injection, aucun indicateur de compromission n'est publié. Passez en revue les enregistrements que votre déploiement conserve des travaux Import/Export : le panneau Processus de l'interface pgAdmin les liste, et leur historique est stocké dans la base de configuration de pgAdmin. Cherchez des exports dont la requête contient TO PROGRAM :
# The Processes panel or the job history in the pgAdmin SQLite database.
sqlite3 -readonly /var/lib/pgadmin/pgadmin4.db \
"SELECT pid, start_time, command, arguments FROM process WHERE arguments LIKE '%TO PROGRAM%';"
Une occurrence signifie qu'un travail a tenté d'exécuter un programme via psql ; c'est une piste, pas une preuve, car un export légitime peut mentionner TO PROGRAM dans un commentaire. Vérifiez ensuite sur l'hôte ce qu'une commande popen() a pu laisser : processus inconnus, entrées cron et fichiers sous le répertoire personnel de l'utilisateur pgAdmin.
Un SET standard_conforming_strings de session ne persiste pas dans la configuration, mais les journaux de requêtes ou d’audit peuvent le conserver. Examinez les journaux disponibles ; l’absence d’historique des tâches n’exclut pas une tentative.
Pour le contournement du verrouillage, cherchez dans les journaux de pgAdmin un volume d'échecs de connexion n'ayant jamais déclenché de verrou, qui est la signature propre du contournement. Aucune victime, indicateur ou campagne n'a été signalée nulle part au 31 octobre 2026.
Chronologie
- 2026-07-27 : pgAdmin commite 1496fabe, le correctif de CVE-2026-17566, fermant l'écart d'antislash pour les chaînes entre apostrophes dans le même garde-fou.
- 2026-07-31 : pgAdmin 9.17 livre ce correctif, et l'enregistrement CVE-2026-17566 est publié le même jour.
- 2026-10-23 : pgAdmin commite 9be31a019f78, le correctif de l'identifiant entre guillemets doubles pour CVE-2026-116241, écrit par hiteshjambhale avec Dave Page, signalé par Yonghwa Lee de Xint by Theori.
- 2026-10-25 : pgAdmin commite les correctifs du contournement du verrouillage pour CVE-2026-116238, dont 58c531d83117.
- 2026-10-29 : pgAdmin 4 9.19 est publié, mis en ligne sur PyPI à 15 h 36 UTC, avec des notes de publication couvrant les 14 correctifs de sécurité.
- 2026-10-30 : le CNA PostgreSQL publie le lot de 14 enregistrements CVE à 19 h 55 UTC.
FAQ
9.18 me protège-t-il ?
Non. 9.18 porte le correctif de juillet pour CVE-2026-17566, qui fermait le cas de l'antislash dans les chaînes entre apostrophes, mais le cas des identifiants entre guillemets doubles de CVE-2026-116241 bat ce correctif. Les contournements de verrouillage de CVE-2026-116238 sont aussi présents dans 9.18. Seul 9.19 corrige les deux.
Le mode bureau est-il concerné ?
Le code de l'injection tourne dans les deux modes, mais l'exploiter à distance exige le mode serveur, où d'autres personnes détiennent des comptes et peuvent atteindre l'outil. Le mode bureau n'expose la même faille qu'à celui qui peut déjà piloter ce poste. Le contournement du verrouillage ne concerne que le mode serveur ; le mode bureau n'est pas concerné.
Qu'est-ce que la mise à jour vers 9.19 casse en mode serveur ?
Le mode serveur change quatre choses, toutes listées dans « Comment corriger ? ». Les deux les plus coûteuses à préparer sont la liste de commandes de mot de passe, qui convertit ou efface les commandes existantes au premier démarrage, et SERVER_MODE_ALLOWED_SERVICES, dont chaque serveur configuré avec un service de connexion a besoin avant de se reconnecter.
Les douze autres CVE sont-elles aussi corrigées dans 9.19 ?
Oui. Les 14 enregistrements disent « before 9.19 », et les notes de publication de 9.19 portent une ligne de correctif pour chacun. Ils vont des courses de chemins du gestionnaire de fichiers de CVE-2026-116240 aux usages d'identifiants du compte de service de CVE-2026-116242 à CVE-2026-116247. Une seule mise à jour corrige le lot.
Qui a trouvé ces failles ?
Yonghwa Lee de Xint by Theori a signalé l'injection, CVE-2026-116241. Yves Soete de Blacksight LLC a signalé la confusion de connexion entre sources de CVE-2026-116239, et Harsh Singh l'échappement de chemin de binaires de CVE-2026-116243. Le contournement du verrouillage, CVE-2026-116238, ne cite aucun rapporteur externe dans l'enregistrement CVE, qui crédite Ashesh Vashi d'EnterpriseDB pour le développement de la correction.
Recensez vos serveurs pgAdmin exposés avec Ostorlab
Le découverte de surface d'attaque d'Ostorlab recense vos serveurs et services exposés sur Internet. Il montre quels déploiements exécutent un point de terminaison pgAdmin exposé au réseau, pour savoir lesquels passer à 9.19 en premier et lesquels replacer derrière un VPN.
Références
- Enregistrement CVE-2026-116241, publié par le CNA PostgreSQL avec CVSS v3.1 9.9 et CVSS v4.0 9.4, créditant Yonghwa Lee : https://www.cve.org/CVERecord?id=CVE-2026-116241 (consulté le 31 octobre 2026)
- Enregistrement CVE-2026-116238, les quatre contournements du verrouillage, avec CVSS v3.1 8.1 et CVSS v4.0 9.2 : https://www.cve.org/CVERecord?id=CVE-2026-116238 (consulté le 31 octobre 2026)
- Notes de publication de pgAdmin 4 9.19, datées du 29 octobre 2026, avec les lignes de correctifs de sécurité pour les tickets #10663, #10676, #10674 et le reste du lot, et les quatre changements du mode serveur : https://www.pgadmin.org/docs/pgadmin4/9.19/release_notes_9_19.html (consulté le 31 octobre 2026)
- Commit correctif 9be31a019f78 pour CVE-2026-116241, dont le message documente la désynchronisation de
strtokxet la requête d'exemple, signalé par Yonghwa Lee de Xint by Theori : https://github.com/pgadmin-org/pgadmin4/commit/9be31a019f8780647fb689c458408c8803263c812 (consulté le 31 octobre 2026) - Commit correctif 58c531d83117 pour CVE-2026-116238, « Act only on the internal account when accounts from other sources share its email or username » : https://github.com/pgadmin-org/pgadmin4/commit/58c531d831179dc9b0a5bf873ccb80c71c8d387a (consulté le 31 octobre 2026)
- Enregistrement CVE-2026-17566, le correctif de juillet du cas des apostrophes dans le même garde-fou, avec le commit 1496fabe : https://www.cve.org/CVERecord?id=CVE-2026-17566 (consulté le 31 octobre 2026)
- Enregistrement CVE-2025-13780, premier tour de la famille, exécution de commandes via les restaurations de dumps au format PLAIN jusqu'à 9.10 : https://www.cve.org/CVERecord?id=CVE-2025-13780 (consulté le 31 octobre 2026)
- pgAdmin 4 9.19 sur PyPI, mis en ligne le 29 octobre 2026 à 15 h 36 UTC : https://pypi.org/project/pgadmin4/ (consulté le 31 octobre 2026)
- Image Docker dpage/pgadmin4, canal de déploiement standard du mode serveur, avec 453 158 170 téléchargements : https://hub.docker.com/r/dpage/pgadmin4 (consulté le 31 octobre 2026)
-
Catalogue Known Exploited Vulnerabilities de la CISA, flux JSON, recherché pour les deux critical : https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json (consulté le 31 octobre 2026)
-
Documentation officielle du conteneur, compte non privilégié et configuration: https://www.pgadmin.org/docs/pgadmin4/9.19/container_deployment.html