
Auditorías Autoderrotantes: laboratorio reproducible que muestra un rol de PostgreSQL con bajos privilegios cegando reversiblemente a un auditor basado en disparadores + envenenando la atribución (PG14/16), con defensas. Datos sintéticos; se citan primitivas de CVE-2018-1058.
Licencia: MIT (código) / CC BY 4.0 (documentación)
Investigación de seguridad defensiva que comprueba si un rol de base de datos de bajos privilegios y solo inyección puede cegar reversiblemente a un auditor basado en disparadores y envenenar la atribución en PostgreSQL estándar — y qué defensas lo detienen realmente. Todo se ejecuta en contenedores Docker locales desechables con datos totalmente sintéticos (IPs de documentación RFC 5737, identidades inventadas). No se involucran sistemas, credenciales ni datos reales.
Consulta el informe del laboratorio en self-defeating-audits-lab.md.
Construye la víctima de "disparador de auditoría" del wiki de PostgreSQL, ampliamente copiada, y luego recorre una matriz de viabilidad celda por celda en PostgreSQL 14 y 16, midiendo — fuera de banda — si cada vector de ataque realmente impide que una escritura sea auditada, si es reversible sin residuos, y qué defensas lo neutralizan.
cd scripts
./00_up.sh 16 # bring up postgres:16-alpine on localhost:55432
./run_matrix.sh 16 # run every matrix cell + defenses -> ../results/RESULTS_pg16.md
docker rm -f sda_pg16 # free the port between versions
./00_up.sh 14
./run_matrix.sh 14 # -> ../results/RESULTS_pg14.md
./teardown.sh # remove all lab containers
Requiere Docker. Utiliza las imágenes -alpine (motor funcionalmente idéntico).
El estudio se reproduce en dos familias de motores más. Familia MySQL (MariaDB,
modelo de disparador DEFINER) en scripts/mysql/:
cd scripts/mysql
./00_up.sh 10.11 && ./run_matrix_mysql.sh 10.11 # -> results/RESULTS_mariadb_10.11.md
docker rm -f sda_maria_10_11 && ./00_up.sh 10.6 && ./run_matrix_mysql.sh 10.6
SQL Server (modelo de encadenamiento de propiedad) en scripts/mssql/ — se ejecuta en una instancia LocalDB local
mediante sqlcmd del host (sin Docker; el atacante de bajos privilegios se modela con
suplantación EXECUTE AS USER):
cd scripts/mssql
./run_matrix_mssql.sh # -> results/RESULTS_mssql.md ; then ./teardown.sh
Advertencia de reproducibilidad (SQL Server). A diferencia de los motores Docker, el tercer motor, SQL Server, tiene una dependencia del host: se ejecuta en una instancia local de SQL Server LocalDB mediante
sqlcmd(ruta fijada enrun_matrix_mssql.sh), por lo que solo funciona en Windows y no puede fijarse a un digest de imagen. El atacante de bajos privilegios se modela con suplantaciónEXECUTE AS USER(LocalDB solo admite autenticación de Windows). Los hallazgos sobre el comportamiento del motor (DISABLE sin residuos; el guard de DDL no detecta DISABLE) son independientes del tipo de sesión y se mantendrían para un login real; los hallazgos sobre concesiones de objetos se aplican fielmente mediante la suplantación. Es el menos portable de los tres motores — se señala con honestidad.
Resultado principal entre motores: la clase de sombra search_path (1A/1B) no se traslada
ni a MySQL ni a SQL Server (no existe search_path), pero la sustitución de disparadores, la evasión latente
y el envenenamiento de atribución sí se trasladan. El enlace de privilegios DEFINER de MySQL hace que el cegado
sea más detectable; SQL Server, como PostgreSQL, permite una restauración perfecta byte a byte y añade una
primitiva DISABLE TRIGGER sin residuos que su propia defensa DDL no detecta. Comparación completa de los tres motores
en FINDINGS.md.
| Ruta | Descripción |
|---|---|
scripts/00_up.sh | contenedor desechable fijado a una versión (PostgreSQL) |
scripts/mysql/ | víctima, ataques, defensas y orquestador de la familia MySQL (MariaDB) |
scripts/mssql/ | víctima, ataques, defensas y orquestador de SQL Server (LocalDB) |
results/RESULTS_mariadb_*.md | matrices de viabilidad y defensa de la familia MySQL |
results/RESULTS_mssql.md | matriz de viabilidad y defensa de SQL Server |
scripts/10_victim.sql | la víctima de auditoría con patrón de wiki (disparador SECURITY DEFINER); parámetros: pub_create, pin_path, app_owns |
scripts/20_attacks.sql | todos los vectores de ataque (1A/1B/1C, estado latente GUC, envenenamiento de atribución), un guard \if por celda |
scripts/30_defenses.sql | guard de disparador de eventos DDL + sumidero de solo adición encadenado por hash |
scripts/40_snapshot.sql | instantánea de catálogo para el diff de objetos RQ2 |
scripts/run_matrix.sh | orquestador: construye cada celda, mide, escribe results/RESULTS_pg<VER>.md |
scripts/teardown.sh | elimina contenedores |
results/RESULTS_pg14.md, results/RESULTS_pg16.md | matrices de viabilidad y defensa completadas por versión |
results/_evidence_pg*.log, results/_snap_1C_*.txt | evidencia bruta de comandos/salidas |
FINDINGS.md | veredicto RQ1–RQ5 en lenguaje sencillo, matriz defensa×ataque, posicionamiento de novedad, resumen |
CREATE SCHEMA), no porque "pg_catalog gana". Plantar en public, que ya es escribible, sí genera sombra: 1S (misma firma, orden de esquemas) y 1T (la sobrecarga exacta vence a la función integrada incluso con pg_catalog primero — aísla la especificidad de tipo) ambas ciegan (payload anulado).row_to_json — ciega al auditor cuando
public es escribible y el search_path del disparador no está fijado. Alcanzable en
PostgreSQL estándar ≤14; en 15+ requiere una mala configuración. Fijar search_path lo neutraliza en todos los casos.pgaudit/log_statement) + el sumidero encadenado por hash son las defensas que cierran el conjunto.Reproducido en PostgreSQL estándar en un laboratorio local con datos anonimizados/sintéticos. El
SQL de ataque está limitado al esquema de laboratorio construido aquí; se trata de un estudio de viabilidad/defensa, no
de un payload armado. Las primitivas subyacentes ya son públicas (CVE-2018-1058;
escalada de search_path en SECURITY DEFINER) — la contribución es la composición y la
evaluación de defensas. No se identifica ningún sistema real.