Exploit and test stand for CVE-2025-2945# pgAdminOpendoor (CVE-2025-2945)
<p align="center">
<img src="logo.jpg" width="450">
</p>
### Overview
This is my research on CVE-2025-2945. This repo includes a test stand with postgress and vulnerable pgadmin, and exploit.
### CVE
Usefull links:
- [NVD Details](https://nvd.nist.gov/vuln/detail/CVE-2025-2945)
- [WIZ Analyze](https://www.wiz.io/vulnerability-database/cve/cve-2025-2945)
- [Metasploit](https://github.com/rapid7/metasploit-framework/issues/20011)
## Breakdown
In pgAdmin backend, which is written in python, there are two endpoint with lack of proper sanitization. Endpoints are:
- `/sqleditor/query_tool/download` - `query_commited` parameter
- `/cloud/deploy` - `high_availability` parameter
Let's look at the [code of sqleditor](https://github.com/pgadmin-org/pgadmin4/blob/a2b35b35f303348f3ec9a8c1a2e84810245b4c56/web/pgadmin/tools/sqleditor/__init__.py#L2159):
```python
for key, value in data.items():
if key == 'query':
sql = value
if key == 'query_commited':
query_commited = (
eval(value) if isinstance(value, str) else value
)
```
What we see here, is the value of `query_commited` been directly passed to `eval()` function. You can see the [docs for eval](https://docs.python.org/3/library/functions.html#eval).
Also see the [code of pgacloud](https://github.com/pgadmin-org/pgadmin4/blob/a2b35b35f303348f3ec9a8c1a2e84810245b4c56/web/pgacloud/providers/google.py#L139):
```python
def _create_google_postgresql_instance(self, args):
credentials = self._get_credentials(self._scopes)
service = discovery.build('sqladmin', 'v1beta4',
credentials=credentials)
high_availability = \
'REGIONAL' if eval(args.high_availability) else 'ZONAL'
```
Same problem here.
It still requires a valid creds and access to pgadmin to reach this endpoints. But any attacker, able to access it, is also able to pass any value to eval. Which is equal to executing any python oneliner.
## Exploitation
Exploitation is pretty straight-forward. See the `exploit/src/main.py`. Don't forget to update the config in order to get a reverse shell.
## Stand Setup
To setup stand run this:
```bash
cd stand
docker compose up -d
```
And that's it. pgAdmin is now accessable at `http://localhost:8080`. Default creds are:
```
admin@example.com
admin
```
Feel free to edit the `.env` file and `stand/init-db/01-init.sql`.
---
prod by _I3r1h0n_.
[4.0K] /data/pocs/25bc08349b9b6bed980bdc41de01eb5f2cac923e
├── [4.0K] exploit
│ ├── [ 198] pyproject.toml
│ ├── [4.0K] resource
│ │ └── [ 201] config.toml
│ └── [4.0K] src
│ └── [5.4K] main.py
├── [ 26K] logo.jpg
├── [2.4K] README.md
└── [4.0K] stand
├── [ 912] docker-compose.yaml
├── [4.0K] init-db
│ └── [ 174] 01-init.sql
└── [4.0K] pgadmin
└── [ 441] servers.json
7 directories, 8 files