pgAdmin 4 Query Tool Authenticated eval() RCE (CVE-2025-2945)
by plur1bu5 · 2026-07-06
- Severity
- Critical
- CVE
- CVE-2025-2945
- Category
- web
- Affected product
- pgAdmin 4 (web-based PostgreSQL administration tool)
- Affected versions
- 8.10 through 9.1 (fixed in 9.2, released April 4, 2025)
- Disclosed
- 2026-07-06
- Patch status
- patched
Tags
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-06 |
| Last Updated | 2026-07-06 |
| Author / Researcher | plur1bu5 |
| CVE / Advisory | CVE-2025-2945 |
| Category | web |
| Severity | Critical |
| CVSS Score | 9.9 (per NVD) |
| Status | Weaponized |
| Tags | pgadmin, postgresql, rce, eval-injection, code-injection, cwe-95, python, authenticated, sqleditor |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | pgAdmin 4 (web-based PostgreSQL administration tool) |
| Versions Affected | 8.10 through 9.1 (fixed in 9.2, released April 4, 2025) |
| Language / Platform | Python (PoC and target server-side code); targets pgAdmin 4 web application |
| Authentication Required | Yes — any valid pgAdmin login plus valid credentials for a database server registered in pgAdmin |
| Network Access Required | Yes |
Summary
pgAdmin 4’s Query Tool “download” endpoint accepts a query_commited parameter and passes it directly to Python’s built-in eval() without any sanitization, allowing an authenticated attacker to run arbitrary Python code under the pgAdmin service account. The root cause is unsafe use of eval() on user-controlled input in the query tool download handler (/sqleditor/query_tool/download/<trans_id>). The included PoC authenticates to pgAdmin, initializes a query tool session against a registered database server, and posts a Python expression (e.g. __import__('os').system('id')) as the query_commited field, achieving command execution on the pgAdmin host. Impact is full remote code execution with the privileges of the pgAdmin process, which commonly has network access to backend PostgreSQL infrastructure.
Vulnerability Details
Root Cause
In pgAdmin 4 versions prior to 9.2, the query tool download handler evaluates user-supplied input directly:
| |
No input validation, sandboxing, or allowlisting is applied to the query_commited field before it reaches eval(), so any valid Python expression submitted by an authenticated user executes with the privileges of the pgAdmin process.
Attack Vector
- Authenticate — POST credentials to
/authenticate/login(or the legacy/loginon pgAdmin 8.x) using a CSRF token extracted from the/loginpage (either a hiddencsrf_tokeninput on older versions, or JSON embedded in awindow.renderSecurityPage()call on the 9.x React SPA). - Initialize sqleditor — POST to
/sqleditor/initialize/sqleditor/<trans_id>/<sgid>/<sid>/<did>with valid database credentials to establish a query tool session tied to a registered PostgreSQL server. - Discover server ID — GET
/sqleditor/get_server_connection/<sgid>/<sid>, iterating candidate server IDs until one returnsdata.status == true. - Trigger eval — POST to
/sqleditor/query_tool/download/<trans_id>with a JSON body{"query_commited": "<python payload>"}. The server evaluates the payload witheval(); a resulting HTTP 500 response confirms code execution.
Impact
An attacker with any valid pgAdmin account and knowledge of (or access to) a registered database server’s credentials can achieve arbitrary Python code execution on the host running pgAdmin — including OS command execution (e.g. spawning a reverse shell) — under the pgAdmin service account.
Environment / Lab Setup
- Target: pgAdmin 4, versions 8.10 - 9.1 (vulnerable), reachable over HTTP/HTTPS
- Target must have at least one PostgreSQL server registered with known credentials
- Attacker: Python 3.7+
- pip install requests faker
Proof of Concept
PoC Script
See
poc.pyin this folder.
| |
Detection & Indicators of Compromise
- Unexpected HTTP 500 responses from POST /sqleditor/query_tool/download/<trans_id>
- JSON request bodies containing a "query_commited" field with non-SQL, Python-expression-like content
(e.g. references to __import__, os.system, subprocess, eval)
- pgAdmin worker/service process spawning unexpected child processes (shells, curl, wget, netcat)
- Outbound network connections initiated from the pgAdmin host to unfamiliar external IPs
Signs of compromise:
- Anomalous child processes under the pgAdmin service account
- Reverse shell connections originating from the pgAdmin server
- Repeated probing of
/sqleditor/get_server_connection/<sgid>/<sid>across many sequential IDs (server-ID brute-forcing) - pgAdmin access/error logs showing 500 errors tied to
/sqleditor/query_tool/download/
Remediation
| Action | Detail |
|---|---|
| Primary fix | Upgrade to pgAdmin 4 version 9.2 or later, which removes the unsafe eval() call on the query_commited parameter. |
| Interim mitigation | Restrict pgAdmin access to trusted networks/VPN only, enforce strong per-user authentication, avoid sharing pgAdmin accounts, and monitor for anomalous process execution and outbound connections from the pgAdmin host. |
References
- Source repository
- https://nvd.nist.gov/vuln/detail/CVE-2025-2945
- https://www.tenable.com/security/research/tra-2025-2945
- https://github.com/rapid7/metasploit-framework/tree/master/modules/exploits/multi/http/pgadmin_query_tool_rce
Notes
Mirrored from https://github.com/plur1bu5/CVE-2025-2945-pgadmin-rce on 2026-07-06. The PoC handles both the legacy pgAdmin 8.x login flow (POST /login) and the pgAdmin 9.x React SPA flow (POST /authenticate/login with CSRF token embedded via window.renderSecurityPage()).
| |