
Self-Defeating Audits: laboratoire reproductible montrant un rôle PostgreSQL à faibles privilèges aveuglant de manière réversible un auditeur basé sur des déclencheurs + empoisonnement de l'attribution (PG14/16), avec des défenses. Données synthétiques; primitives CVE-2018-1058 citées.
Licence : MIT (code) / CC BY 4.0 (docs)
Recherche en sécurité défensive testant si un rôle de base de données à faibles privilèges, injection uniquement peut aveugler de manière réversible un auditeur basé sur des triggers et empoisonner l'attribution sur PostgreSQL standard — et quelles défenses l'arrêtent réellement. Tout s'exécute dans des conteneurs Docker locaux jetables avec des données entièrement synthétiques (IP de documentation RFC 5737, identités inventées). Aucun système, identifiant ou donnée réel n'est impliqué.
Voir le brief du laboratoire dans self-defeating-audits-lab.md.
Construit la victime « audit trigger » du wiki PostgreSQL, largement copiée, puis parcourt une matrice de faisabilité cellule par cellule sur PostgreSQL 14 et 16, en mesurant — hors bande — si chaque vecteur d'attaque empêche réellement l'audit d'une écriture, s'il est réversible sans aucune trace résiduelle, et quelles défenses le neutralisent.
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
Nécessite Docker. Utilise les images -alpine (moteur fonctionnellement identique).
L'étude est reproduite sur deux familles de moteurs supplémentaires. Famille MySQL (MariaDB, modèle de trigger DEFINER) dans 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 (modèle de chaînage de propriété) dans scripts/mssql/ — s'exécute sur une instance LocalDB locale via sqlcmd hôte (pas de Docker ; l'attaquant à faibles privilèges est modélisé par l'usurpation EXECUTE AS USER) :
cd scripts/mssql
./run_matrix_mssql.sh # -> results/RESULTS_mssql.md ; then ./teardown.sh
Réserve de reproductibilité (SQL Server). Contrairement aux moteurs Docker, le volet SQL Server a une dépendance hôte : il s'exécute sur une instance locale SQL Server LocalDB via
sqlcmd(chemin codé en dur dansrun_matrix_mssql.sh), il est donc exclusivement Windows et ne peut pas être épinglé à un digest d'image. L'attaquant à faibles privilèges est modélisé par l'usurpationEXECUTE AS USER(LocalDB n'accepte que l'authentification Windows). Les résultats sur le comportement du moteur (DISABLE sans trace résiduelle ; la garde DDL ne détecte pas DISABLE) sont indépendants du type de session et vaudraient pour une vraie connexion ; les résultats sur les octrois d'objets sont appliqués fidèlement par l'usurpation. C'est le moins portable des trois moteurs — constaté en toute honnêteté.
Résultat principal multi-moteurs : la classe d'ombre search_path (1A/1B) ne se transpose ni à MySQL ni à SQL Server (pas de search_path), contrairement au remplacement de trigger, au contournement dormant et à l'empoisonnement de l'attribution. La liaison des privilèges DEFINER de MySQL rend l'aveuglement plus détectable ; SQL Server, comme PostgreSQL, permet une restauration octet pour octet et ajoute une primitive DISABLE TRIGGER sans trace résiduelle que sa propre défense DDL ne détecte pas. Comparaison complète des trois moteurs dans FINDINGS.md.
| Chemin | Description |
|---|---|
scripts/00_up.sh | conteneur jetable épinglé à une version (PostgreSQL) |
scripts/mysql/ | victime, attaques, défenses et orchestrateur pour la famille MySQL (MariaDB) |
scripts/mssql/ | victime, attaques, défenses et orchestrateur pour SQL Server (LocalDB) |
results/RESULTS_mariadb_*.md | matrices de faisabilité et de défense pour la famille MySQL |
results/RESULTS_mssql.md | matrice de faisabilité et de défense pour SQL Server |
scripts/10_victim.sql | la victime d'audit au motif wiki (trigger SECURITY DEFINER) ; réglages : pub_create, pin_path, app_owns |
scripts/20_attacks.sql | tous les vecteurs d'attaque (1A/1B/1C, GUC dormant, empoisonnement de l'attribution), une garde \if par cellule |
scripts/30_defenses.sql | garde par trigger d'événement DDL + puits append-only chaîné par hachage |
scripts/40_snapshot.sql | instantané du catalogue pour le diff d'objets RQ2 |
scripts/run_matrix.sh | orchestrateur : construit chaque cellule, mesure, écrit results/RESULTS_pg<VER>.md |
scripts/teardown.sh | supprime les conteneurs |
results/RESULTS_pg14.md, results/RESULTS_pg16.md | matrices de faisabilité et de défense remplies par version |
results/_evidence_pg*.log, results/_snap_1C_*.txt | preuves brutes de commandes/sorties |
FINDINGS.md | verdict RQ1–RQ5 en langage clair, matrice défense×attaque, positionnement de la nouveauté, résumé |
CREATE SCHEMA), pas parce que « pg_catalog gagne ». Planter dans le public déjà inscriptible fait bien de l'ombre : 1S (même signature, ordre des schémas) et 1T (la surcharge exacte bat la fonction intégrée même avec pg_catalog en premier — isole la spécificité de type) aveuglent tous deux (payload nul).row_to_json pour type composite — aveugle l'auditeur lorsque public est inscriptible et que le search_path du trigger n'est pas épinglé. Atteignable sur PostgreSQL standard ≤14 ; conditionné par une mauvaise configuration sur 15+. Épingler search_path le neutralise partout.pgaudit/log_statement) + le puits chaîné par hachage sont les défenses qui bouclent l'ensemble.Reproduit sur PostgreSQL standard dans un laboratoire local avec des données anonymisées/synthétiques. Le SQL d'attaque est cantonné au schéma de laboratoire construit ici ; il s'agit d'une étude de faisabilité/défense, pas d'un payload militarisé. Les primitives sous-jacentes sont déjà publiques (CVE-2018-1058 ; escalade search_path sur SECURITY DEFINER) — la contribution réside dans la composition et l'évaluation des défenses. Aucun système réel n'est identifié.