
Proof-of-Concept, der den SQL-Injection-Bypass CVE-2026-17351 im KI-Assistenten von pgAdmin 4 über die sqlparse/PostgreSQL-Lexerdifferenz demonstriert, einschließlich der Zustellung indirekter Prompt-Injection.
PoC für CVE-2026-17351, einen kritischen (CVSS 9.0) SQL-Injection-Bypass im KI-Assistenten von pgAdmin 4.
pgAdmin 4 Versionen 9.13 bis 9.16 verwenden die Python-Bibliothek sqlparse, um zu validieren, dass LLM-generierte SQL-Abfragen einzelne, reine Leseanweisungen sind. Unter PostgreSQL standard_conforming_strings = on (der Standard seit PostgreSQL 9.1) sind sich sqlparse und PostgreSQL uneinig darüber, wie Backslashes innerhalb von String-Literalen behandelt werden. Dies ermöglicht es einem Angreifer, eine Nutzlast zu erstellen, die die Validierung von sqlparse als einzelnes SELECT besteht, aber in PostgreSQL als vier Anweisungen ausgeführt wird – einschließlich eines COMMIT, das die Read-Only-Transaktion beendet, sowie nachfolgender Schreibanweisungen.
SELECT '\';COMMIT;CREATE TABLE pwn(x int);SELECT 1 --'
SELECT mit einem großen String-Literal → Validierung bestehtSELECT '\' + COMMIT + CREATE TABLE pwn(x int) + SELECT 1 --'Die Nutzlast wird per indirekter Prompt-Injection zugestellt: Der Angreifer platziert sie in einem Datenbankobjekt (Spaltenkommentar, Zeilenwert, View-Definition), das der KI-Assistent liest. Wenn ein legitimer Benutzer eine Frage stellt, liest das LLM die manipulierten Daten und gibt sie als execute_sql_query-Toolaufruf aus.
| Datei | Beschreibung |
|---|---|
poc.py | Demonstriert die Lexer-Differenz: sqlparse-Validierung besteht, Simple Query Protocol führt mehrere Anweisungen aus, Extended Query Protocol blockiert sie |
prompt_injection_demo.py | Simuliert die vollständige Angriffskette: Nutzlast als Spaltenkommentar platziert → simuliertes LLM liest sie → Validierung besteht → Ausführung |
pip install psycopg[binary] sqlparse
Außerdem benötigen Sie eine laufende PostgreSQL-Instanz (14+):
docker run -d --name pg-poc \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=testdb \
-p 5433:5432 \
postgres:18
# Haupt-PoC — Demonstration der Lexer-Differenz
python3 poc.py
# Demo der Prompt-Injection-Zustellung
python3 prompt_injection_demo.py
# Benutzerdefinierte Verbindungsparameter
python3 poc.py --host 10.0.0.5 --port 5432 --user postgres --password mypw
Die Behebung erzwingt das Extended Query Protocol von psycopg3, indem:
conn.prepare_threshold = 0 auf der dedizierten Verbindung des LLM gesetzt wirdprepare=True an cursor.execute() übergeben wirdDadurch wird der eigene Parse-Schritt von PostgreSQL zur Autorität für Anweisungsgrenzen – er lehnt mehrteilige Anweisungstexte strukturell ab, unabhängig davon, wie ein clientseitiger Lexer sie klassifiziert.
Dieser PoC dient ausschließlich Bildungs- und autorisierten Testzwecken.