
Self-Defeating Audits: reproduzierbares Lab, das zeigt, wie eine PostgreSQL-Rolle mit geringen Rechten einen triggerbasierten Auditor reversibel blind macht + die Attribution vergiftet (PG14/16), mit Verteidigungsmaßnahmen. Synthetische Daten; CVE-2018-1058-Primitive werden angeführt.
Lizenz: MIT (Code) / CC BY 4.0 (Doku)
Forschung zur defensiven Sicherheit, die testet, ob eine privilegienarme, nur zur Injektion fähige Datenbankrolle einen trigger-basierten Auditor reversibel blenden und die Attribuierung auf Standard-PostgreSQL vergiften kann — und welche Verteidigungen das tatsächlich verhindern. Alles läuft in wegwerfbaren, lokalen Docker-Containern mit vollständig synthetischen Daten (RFC 5737-Dokumentations-IPs, erfundene Identitäten). Es sind keine echten Systeme, Anmeldeinformationen oder Daten beteiligt.
Siehe den Kurzbericht zum Labor in self-defeating-audits-lab.md.
Erstellt das vielfach kopierte „Audit-Trigger“-Opfer aus dem PostgreSQL-Wiki und arbeitet dann über PostgreSQL 14 und 16 hinweg eine Machbarkeitsmatrix Zelle für Zelle ab, wobei — out of band — gemessen wird, ob jeder Angriffsvektor tatsächlich verhindert, dass ein Schreibvorgang auditiert wird, ob er rückstandsfrei umkehrbar ist und welche Verteidigungen ihn ausschalten.
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
Erfordert Docker. Verwendet die -alpine-Images (funktional identische Engine).
Die Studie wird auf zwei weiteren Engine-Familien reproduziert. MySQL-Familie (MariaDB, DEFINER-Trigger-Modell) unter 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 (Ownership-Chaining-Modell) unter scripts/mssql/ — läuft auf einer lokalen LocalDB-Instanz über sqlcmd auf dem Host (kein Docker; der privilegienarme Angreifer wird mit EXECUTE AS USER-Impersonation modelliert):
cd scripts/mssql
./run_matrix_mssql.sh # -> results/RESULTS_mssql.md ; then ./teardown.sh
Reproduzierbarkeitshinweis (SQL Server). Im Gegensatz zu den Docker-Engines hat der SQL-Server-Teil eine Host-Abhängigkeit: Er läuft auf einer lokalen SQL Server LocalDB-Instanz über
sqlcmd(Pfad inrun_matrix_mssql.shfest codiert), ist also nur unter Windows verfügbar und kann nicht auf einen Image-Digest festgepinnt werden. Der privilegienarme Angreifer wird mitEXECUTE AS USER-Impersonation modelliert (LocalDB unterstützt nur die Windows-Authentifizierung). Die Befunde zum Engine-Verhalten (DISABLE ohne Rückstände; DDL-Guard übersieht DISABLE) sind unabhängig von der Sitzungsart und würden auch für einen echten Login gelten; die Befunde zu Objektrechten werden durch die Impersonation originalgetreu durchgesetzt. Es ist die am wenigsten portable der drei Engines — ehrlich angemerkt.
Wichtigstes engine-übergreifendes Ergebnis: Die search_path-Schattenklasse (1A/1B) lässt sich weder auf MySQL noch auf SQL Server übertragen (kein search_path), wohl aber Trigger-Ersetzung, Bypass durch Dormanz und Attributionsvergiftung. Die Bindung an DEFINER-Rechte bei MySQL macht das Blenden leichter erkennbar; SQL Server erlaubt — wie PostgreSQL — eine byteperfekte Wiederherstellung und fügt ein Zero-Residue-DISABLE TRIGGER-Primitiv hinzu, das seine eigene DDL-Verteidigung übersieht. Vollständiger Drei-Engine-Vergleich in FINDINGS.md.
CREATE SCHEMA ausführen), nicht weil „pg_catalog gewinnt“. Das Platzieren in das bereits beschreibbare public erzeugt sehr wohl Shadowing: 1S (gleiche Signatur, Schema-Reihenfolge) und 1T (exakte Überladung schlägt die eingebaute Funktion selbst wenn pg_catalog zuerst kommt — isoliert die Typspezifität) blenden beide den Auditor (mit genulltem Payload).row_to_json-Überladung für zusammengesetzte Typen — blendet den Auditor, wenn public beschreibbar und der search_path des Triggers nicht festgepinnt ist. Auf Standard-PostgreSQL ≤14 erreichbar; auf 15+ nur bei Fehlkonfiguration. Das Festpinnen von search_path macht es überall wirkungslos.Reproduziert auf Standard-PostgreSQL in einem lokalen Labor mit anonymisierten/synthetischen Daten. Das Angriffs-SQL ist auf das hier aufgebaute Labor-Schema beschränkt; dies ist eine Machbarkeits-/Verteidigungsstudie, kein einsatzbereiter (weaponized) Payload. Die zugrunde liegenden Primitiven sind bereits öffentlich (CVE-2018-1058; SECURITY DEFINER-search_path-Eskalation) — der Beitrag ist die Kombination und die Bewertung der Verteidigungen. Es wird kein reales System identifiziert.
| Pfad | Inhalt |
|---|
scripts/00_up.sh | Einweg-Container mit festgepinnter Version (PostgreSQL) |
scripts/mysql/ | Opfer, Angriffe, Verteidigungen und Orchestrator der MySQL-Familie (MariaDB) |
scripts/mssql/ | Opfer, Angriffe, Verteidigungen und Orchestrator für SQL Server (LocalDB) |
results/RESULTS_mariadb_*.md | Machbarkeits- und Verteidigungsmatrizen der MySQL-Familie |
results/RESULTS_mssql.md | Machbarkeits- und Verteidigungsmatrix für SQL Server |
scripts/10_victim.sql | das Audit-Opfer nach Wiki-Vorlage (SECURITY DEFINER-Trigger); Stellschrauben: pub_create, pin_path, app_owns |
scripts/20_attacks.sql | alle Angriffsvektoren (1A/1B/1C, GUC-Dormanz, Attributionsvergiftung), ein \if-Guard pro Zelle |
scripts/30_defenses.sql | DDL-Event-Trigger-Guard + hash-verkettete Append-only-Senke |
scripts/40_snapshot.sql | Katalog-Snapshot für den RQ2-Objektdiff |
scripts/run_matrix.sh | Orchestrator: baut jede Zelle auf, misst, schreibt results/RESULTS_pg<VER>.md |
scripts/teardown.sh | entfernt Container |
results/RESULTS_pg14.md, results/RESULTS_pg16.md | ausgefüllte Machbarkeits- und Verteidigungsmatrizen pro Version |
results/_evidence_pg*.log, results/_snap_1C_*.txt | rohe Befehls-/Ausgabebelege |
FINDINGS.md | allgemeinverständliches RQ1–RQ5-Fazit, Verteidigungs×Angriffs-Matrix, Positionierung der Neuheit, Abstract |
pgauditlog_statement