
Prueba de concepto que demuestra el bypass de inyección SQL CVE-2026-17351 en el Asistente de IA de pgAdmin 4 mediante el diferencial del lexer sqlparse/PostgreSQL, incluida la entrega indirecta de inyección de prompts.
PoC para CVE-2026-17351, un bypass crítico (CVSS 9.0) de inyección SQL en el Asistente de IA de pgAdmin 4.
Las versiones de pgAdmin 4 desde la 9.13 hasta la 9.16 utilizan la librería sqlparse de Python para validar que las consultas SQL generadas por LLM sean sentencias únicas y de solo lectura. Con standard_conforming_strings = on en PostgreSQL (el valor predeterminado desde PostgreSQL 9.1), sqlparse y PostgreSQL no coinciden en cómo se manejan las barras invertidas dentro de los literales de cadena. Esto permite a un atacante crear un payload que supera la validación de sqlparse como un único SELECT, pero que se ejecuta como cuatro sentencias en PostgreSQL, incluido un COMMIT que finaliza la transacción de solo lectura y las sentencias de escritura posteriores.
SELECT '\';COMMIT;CREATE TABLE pwn(x int);SELECT 1 --'
SELECT con un literal de cadena grande → la validación se superaSELECT '\' + COMMIT + CREATE TABLE pwn(x int) + SELECT 1 --'El payload se entrega mediante inyección indirecta de prompt: el atacante lo introduce dentro de un objeto de base de datos (comentario de columna, valor de fila, definición de vista) que el Asistente de IA lee. Cuando un usuario legítimo hace una pregunta, el LLM lee los datos envenenados y los emite como una llamada a la herramienta execute_sql_query.
| Archivo | Descripción |
|---|---|
poc.py | Demuestra la diferencia de lexer: la validación de sqlparse se supera, el protocolo de consulta simple ejecuta varias sentencias, el protocolo de consulta extendido lo bloquea |
prompt_injection_demo.py | Simula la cadena de ataque completa: payload plantado como comentario de columna → el LLM simulado lo lee → la validación se supera → ejecución |
pip install psycopg[binary] sqlparse
También necesitarás una instancia de PostgreSQL en ejecución (14+):
docker run -d --name pg-poc \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=testdb \
-p 5433:5432 \
postgres:18
# 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
La solución fuerza el protocolo de consulta extendido de psycopg3 al:
conn.prepare_threshold = 0 en la conexión dedicada del LLMprepare=True a cursor.execute()Esto convierte al propio paso Parse de PostgreSQL en la autoridad en cuanto a los límites de las sentencias: rechaza estructuralmente el texto de múltiples sentencias, sin importar cómo lo clasifique cualquier lexer del lado del cliente.
Este PoC es únicamente para fines educativos y de pruebas autorizadas.