## https://sploitus.com/exploit?id=B1D1E25F-BBB1-5A30-95D8-EC0DD6E59C87
# CVE-2026-17351 โ pgAdmin 4 AI Assistant Read-Only Bypass via Lexer Differential
PoC for CVE-2026-17351, a critical (CVSS 9.0) SQL injection bypass in pgAdmin 4's AI Assistant.
## Vulnerability Summary
pgAdmin 4 versions 9.13 through 9.16 use Python's `sqlparse` library to validate that LLM-generated SQL queries are single, read-only statements. Under PostgreSQL's `standard_conforming_strings = on` (the default since PostgreSQL 9.1), `sqlparse` and PostgreSQL disagree about how backslashes inside string literals are handled. This allows an attacker to craft a payload that passes `sqlparse`'s validation as a single `SELECT` but executes as four statements in PostgreSQL โ including a `COMMIT` that ends the read-only transaction and subsequent write statements.
## Payload
```sql
SELECT '\';COMMIT;CREATE TABLE pwn(x int);SELECT 1 --'
```
- **sqlparse** sees one `SELECT` with a big string literal โ validation passes
- **PostgreSQL** sees: `SELECT '\'` + `COMMIT` + `CREATE TABLE pwn(x int)` + `SELECT 1 --'`
## Delivery
The payload is delivered via **indirect prompt injection**: the attacker plants it inside a database object (column comment, row value, view definition) that the AI Assistant reads. When a legitimate user asks a question, the LLM reads the poisoned data and emits it as an `execute_sql_query` tool call.
## Files
| File | Description |
|---|---|
| `poc.py` | Demonstrates the lexer differential: sqlparse validation passes, simple query protocol executes multiple statements, extended query protocol blocks it |
| `prompt_injection_demo.py` | Simulates the full attack chain: payload planted as a column comment โ simulated LLM reads it โ validation passes โ execution |
## Requirements
```bash
pip install psycopg[binary] sqlparse
```
You also need a running PostgreSQL instance (14+):
```bash
docker run -d --name pg-poc \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=testdb \
-p 5433:5432 \
postgres:18
```
## Usage
```bash
# Main PoC โ lexer differential demonstration
python3 poc.py
# Prompt injection delivery demo
python3 prompt_injection_demo.py
# Custom connection parameters
python3 poc.py --host 10.0.0.5 --port 5432 --user postgres --password mypw
```
## The Fix (pgAdmin 9.17)
The fix forces psycopg3's **extended query protocol** by:
1. Setting `conn.prepare_threshold = 0` on the LLM's dedicated connection
2. Passing `prepare=True` to `cursor.execute()`
This makes PostgreSQL's own `Parse` step the authority on statement boundaries โ it structurally rejects multi-statement text regardless of how any client-side lexer classifies it.
## References
- CVE: https://nvd.nist.gov/vuln/detail/CVE-2026-17351
- GitHub Issue: https://github.com/pgadmin-org/pgadmin4/issues/10192
- Fix Commit: https://github.com/pgadmin-org/pgadmin4/commit/ef76102bcd1cdb544eb9b4ef18d3382f22b76752
- Original CVE (CVE-2026-12045): https://nvd.nist.gov/vuln/detail/CVE-2026-12045
- Reporter: Kai Aizen (SnailSploit)
## Disclaimer
This PoC is for educational and authorized testing purposes only.