
Preuve de concept démontrant le contournement de l'injection SQL CVE-2026-17351 dans l'assistant IA de pgAdmin 4 via le différentiel de lexer sqlparse/PostgreSQL, y compris la distribution d'injections indirectes de prompt.
PoC pour CVE-2026-17351, un contournement critique (CVSS 9.0) d'injection SQL dans l'assistant IA de pgAdmin 4.
Les versions 9.13 à 9.16 de pgAdmin 4 utilisent la bibliothèque sqlparse de Python pour valider que les requêtes SQL générées par LLM sont des instructions uniques en lecture seule. Avec standard_conforming_strings = on de PostgreSQL (la valeur par défaut depuis PostgreSQL 9.1), sqlparse et PostgreSQL ne sont pas d'accord sur la façon dont les barres obliques inverses dans les littéraux de chaîne sont traitées. Cela permet à un attaquant de créer une charge utile qui passe la validation de sqlparse comme un SELECT unique mais qui s'exécute comme quatre instructions dans PostgreSQL — y compris un COMMIT qui met fin à la transaction en lecture seule et des instructions d'écriture ultérieures.
SELECT '\';COMMIT;CREATE TABLE pwn(x int);SELECT 1 --'
SELECT avec un grand littéral de chaîne → la validation passeSELECT '\' + COMMIT + CREATE TABLE pwn(x int) + SELECT 1 --'La charge utile est diffusée via injection indirecte par invite : l'attaquant la place dans un objet de base de données (commentaire de colonne, valeur de ligne, définition de vue) que l'assistant IA lit. Lorsqu'un utilisateur légitime pose une question, le LLM lit les données empoisonnées et les émet comme un appel d'outil execute_sql_query.
| Fichier | Description |
|---|---|
poc.py | Démontre la différence de lexer : la validation sqlparse passe, le protocole de requête simple exécute plusieurs instructions, le protocole de requête étendu le bloque |
prompt_injection_demo.py | Simule la chaîne d'attaque complète : charge utile placée comme commentaire de colonne → LLM simulé la lit → validation passe → exécution |
pip install psycopg[binary] sqlparse
Vous avez également besoin d'une instance PostgreSQL en cours d'exécution (14+) :
docker run -d --name pg-poc \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=testdb \
-p 5433:5432 \
postgres:18
# PoC principal — démonstration de la différence de lexer
python3 poc.py
# Démo de diffusion par injection par invite
python3 prompt_injection_demo.py
# Paramètres de connexion personnalisés
python3 poc.py --host 10.0.0.5 --port 5432 --user postgres --password mypw
Le correctif force le protocole de requête étendu de psycopg3 en :
conn.prepare_threshold = 0 sur la connexion dédiée du LLMprepare=True à cursor.execute()Cela fait de l'étape Parse de PostgreSQL elle-même l'autorité en matière de limites d'instructions — elle rejette structurellement le texte multi-instructions, quelle que soit la façon dont un lexer côté client le classe.
Ce PoC est destiné uniquement à des fins éducatives et à des tests autorisés.