blog.Mainzer Datenfabrik

Schutz vor Berechtigungsmissbrauch in SQL Server

cover image of blog article 'Schutz vor Berechtigungsmissbrauch in SQL Server'

Einführung

Dieser Artikel richtet sich an all jene Datenbankadministratoren, die ihre SQL Server Datenbanken vor Missbrauch und Datenraub durch potentielle Angreifer schützen wollen. Wir erklären Ihnen verschiedene Maßnahmen, die Sie ergreifen können um Ihre User und Administratoren vor solchen Szenarien zu bewahren und gegen einen Hijacking Angriff zu verteidigen. Je mehr Berechtigung eine einzelne Person hat, desto interessanter wird sie für potenzielle Angreifer. Die meisten solcher Angriffe erfolgen über bereits autorisierte Nutzer mit entsprechenden Berechtigungen und Zugriffen auf das System. Allerdings können auch Entwickler ohne ebendiese Berechtigungen Sicherheitslücken ausnutzen und sich Zugriff zu Ihrer Datenbank verschaffen. Wir wollen Ihnen mit diesem Artikel helfen, dass diese Angriffe nicht erfolgreich durchgeführt werden und Sie sich umfassend schützen.

Explizit untersuchen wir folgende Angriff-Szenarios:

  1. Angriff auf Benutzer mit sysadmin Berechtigungen
    Sie sind der leitende DBA oder ein sysadmin auf Serverebene und verfügen über die CONTROL SERVER Berechtigung. Der Angreifer ist ein User mit der Berechtigung, gespeicherte Prozeduren und Trigger direkt oder indirekt zu erstellen, indem sein Code auf die Zielumgebung deployt wird. Sein Ziel ist es sysadmin zu werden oder Ihre Berechtigungen auszunutzen, um sensible Daten zu stehlen oder zu manipulieren.

  2. Cross DB Angriff
    Sie sind User in der Datenbank 1. Der Angreifer, der erhöhte Berechtigungen in der Datenbank 2 besitzt, nicht aber in Datenbank 1, möchte Ihre Rolle übernehmen und kritische Daten stehlen.

  3. Angriff auf Benutzer mit db_owner Berechtigungen
    Sie sind DBA auf Datenbankebene und haben die CONTROL Berechtigung in dieser Datenbank. Der Angreifer besitzt direkte oder indirekte Berechtigungen zur Erstellung von gespeicherten Prozeduren oder anderen ausführbaren Modulen. Er strebt die vollständige Kontrolle über die Datenbank an.

Der Ausgangspunkt für unsere Untersuchung ist die Frage, wie Jobs zur Index- und Statistikpflege durch DDL-Trigger missbraucht werden können. Anschließend werfen wir einen Blick darauf, wie Sie dazu verleitet werden können, DML-Trigger oder gespeicherte Prozeduren auszuführen, die bösartigen Code beinhalten. Wir zeigen kurz, wie Sie gespeicherte Prozeduren schreiben können, die privilegierte Aktionen auf eine Art ausführen, so dass auch nicht privilegierte User diese ausführen können. Im Anschluss schauen wir uns Agent Jobs an, die eine enorme Angriffsfläche für das Hijacking von Berechtigungen darstellen - es sei denn, Sie befolgen unsere Ratschläge. Als letztes gehen wir darauf an, wie ein Entwickler Code in ein Deployment-Skript einschleusen könnte, das Sie ausführen, oder sich zunutze machen könnte, dass Sie der Benutzer einer Anwendung sind, über die er oder sie Kontrolle hat.

Seien Sie sich jederzeit bewusst, je mehr Kontrolle und Berechtigungen Sie haben, desto mehr Angriffsfläche bieten Sie kriminellen Personen.

Angriffe über DDL Trigger

Szenario 1 - Angriff auf sysadmin

Das Szenario 1 beschreibt die Situation, dass Sie die Rolle des DBA auf Serverebene inne haben. Für viele oder sogar alle Datenbanken auf dem Server existieren User mit db_owner Rechten für die lokale Datenbank, jedoch ohne Berechtigungen auf Server-Ebene. Als Größenumfang nehmen wir 50 - 100 kleine bis mittelgroße Datenbanken an.
Sie wollen Wartungsaufträge erstellen die gängige Wartungsarbeiten erledigen: Datenbanken sichern, Datenbankintegrität prüfen, Statistiken aktualisieren und Indizes reorganisieren bzw. neu aufbauen. Solche Wartungsaufträge würden normalerweise unter einem Konto mit sysadmin Berechtigungen laufen. Aber sollten Sie das wirklich tun?

Gefahrensituation: Ein lokaler Power-User hätte die Möglichkeiten mit folgendem Skript einen DDL-Trigger zu einer bestehenden Datenbank hinzuzufügen und sich dadurch selbst zum sysadmin zu machen:

CREATE OR ALTER TRIGGER EvilDdlTri ON DATABASE FOR DDL_DATABASE_LEVEL_EVENTS AS 
   IF is_srvrolemember('sysadmin') = 1 
       EXEC('USE master ALTER SERVER ROLE sysadmin ADD MEMBER [DOMAIN\EvilUser]')

Durch Backups oder Integritätsprüfungen wird der DDL Trigger nicht ausgelöst. Jedoch können UPDATE STATISTICS und ALTER INDEX REBUILD / REORGANIZE zur Auslösung des Triggers führen. Er prüft hierbei, ob der ausführende User sysadmin Berechtigungen besitzt. Trifft dies zu, führt der Trigger Anweisungen aus, die im Berechtigungsrahmen des sysadmin liegen. In unserem Beispiel weist sich der User selbst sysadmin Rechte zu und hat somit auf alle Daten uneingeschränkten Zugriff.
Wir empfehlen daher - gerade bei kritischen Daten - die Mitglieder der Administratoren-Gruppe regelmäßig auf Unstimmigkeiten zu überprüfen. Jeder Name, der normalerweise nicht in dieser Gruppe auftauchen sollte, könnte ein Indiz für einen möglichen Angreifer sein und sollte mit besonderer Aufmerksamkeit behandelt werden. Ist das Ziel es Angreifers jedoch “nur” an sensible Daten zu gelangen, kann dies auch direkt durch die Ausführung des Triggers geschehen. Es besteht keine Notwendigkeit für den Angreifer, sich selbst höhere Berechtigungen zuzuweisen, was es umso schwerer macht, einen solchen Fall aufzudecken.

Leider gibt es kaum Möglichkeiten, einen DDL-Trigger zu stoppen, sobald er auf Serverebene in Aktion war. Auch diesen aufzuspüren ist schier unmöglich, da die Updates schließlich durch die Berechtigungsübergabe von einem sa oder Dienstkonto von SQL Server ausgeführt wurden.

Unser Ziel ist es also, möglichst einen Weg zu finden, der einen Angriff durch den DDL Trigger gar nicht erst zulässt.

→ Sorgen Sie dafür, dass Sie niemals Jobs mit mehr Berechtigungen durchführen, als für die dessen Ausführung nötig sind. Diese Strategie wird auch PoLP - Principle of Least Priviledge genannt.

Um das PoLP Prinzip einzuhalten, gibt es eine recht simple Maßnahme die Sie dafür ergreifen können. Eine Wartungsprozedur zur Pflege von Indizes enthält in der Regel dynamischen SQL Code, der über Datenbanken und Tabellen auf dem Server iteriert.

SELECT @sql = 'ALTER INDEX ' + quotename(@ixname) + ' ON ' + 
              quotename(@schema) + '.' + quotename(@table) + ' REORGANIZE'
SELECT @sp_executesql = @db + '.sys.sp_executesql'
EXEC @sp_executesql @sql

SELECT @sql = 'USE ' + quotename(@db) + '
              ALTER INDEX ' + quotename(@ixname) + ' ON ' + 
              quotename(@schema) + '.' + quotename(@table) + ' REORGANIZE'
EXEC (@sql)

Ergänzen Sie den @sql-Batch um folgenden Befehl, um das Hijacking von Berechtigungen zu vermeiden.

EXECUTE AS USER = 'dbo'

Dieser Befehl imitiert den Datenbank Owner. Das heißt, Sie geben sich als Eigentümer der Datenbank aus. Immer dann, wenn Sie sich als ein Datenbankbenutzer ausgeben, wird Ihrem Sicherheitskontext auf Serverebene nicht vertraut - selbst wenn Sie in Wirklichkeit Systemadministrator sind. Um dies zu veranschaulichen, suchen Sie sich eine Datenbank zum Testen (tempdb reicht aus) und erstellen diesen DDL-Trigger:

CREATE OR ALTER TRIGGER ddltri ON database for DDL_DATABASE_LEVEL_EVENTS AS 
   SELECT is_srvrolemember('sysadmin'),* FROM sys.login_token

Wählen Sie nun eine Tabelle aus und führen folgenden Befehl aus:

ALTER INDEX ALL ON sometable REBUILD

Sie erhalten im Nachgang folgende Ausgabe:

principal_id sid        name               type           usage
---- ------------ -------    ------------------ -------------- ---------------
1    259          0x01050... DOMAIN\user        WINDOWS LOGIN  GRANT OR DENY
1    2            0x02       public             SERVER ROLE    GRANT OR DENY
1    3            0x03       sysadmin           SERVER ROLE    GRANT OR DENY
...

Anhand der ersten Spalte können Sie erkennen, dass Ihre Abfrage unter sysadmin Berechtigungen ausgeführt wird. In der Spalte “usage” rechts wird GRANT OR DENY angezeigt, was im Umkehrschluss bedeutet, dass sowohl die Vergabe als auch die Rücknahme von Berechtigungen möglich sind.

Als nächstes integrieren wir den obigen Befehl in das EXECUTE AS USER Skript:

EXECUTE AS USER = 'dbo'
ALTER INDEX ALL ON sometable REBUILD
REVERT

Möglicherweise erhalten Sie folgende Fehlermeldung:

“Msg 15517, Level 16, State 1, Line 152"

Cannot execute as the database principal because the principal "dbo" does not exist, this type of principal cannot be impersonated, or you do not have permission.”

Das könnte mitunter daran liegen, dass die SID für dbo innerhalb der Datenbank (sys.database_principals) nicht mit der Spalte sys.databases.owner_sid übereinstimmt. Ein solches Szenario passiert immer dann, wenn Sie eine Datenbank-Sicherung auf einem anderen Server wiederherstellen. Der Befehl RESTORE ist dafür verantwortlich, den Eigentümer in sys.databases zu behalten oder alternativ den ausführenden Benutzer als Besitzer zu berechtigen. RESTORE wird jedoch niemals die SID für dbo in der Datenbank lesen oder aktualisieren. Der Datenbank Owner sollte besser durch den ALTER AUTHORIZATION Befehl in den vorgesehenen Eigentümer geändert werden.

Bei erfolgreicher Ausführung der Abfrage erhalten wir folgende Ausgabe:

principal_id sid         name               type           usage
----- ------------ ----------- ------------------ -------------- ----------
0     259          0x010500... DOMAIN\user        WINDOWS LOGIN  DENY ONLY
0     2            0x02        public             SERVER ROLE    DENY ONLY
0     3            0x03        sysadmin           SERVER ROLE    DENY ONLY
...

Die 0 in der linken Spalte heißt, dass Sie nicht als sysadmin handeln. In der Spalte “usage” ganz rechts steht durchgängig DENY ONLY. Das heißt, alle diese User können nur Rechte entziehen, aber keine Berechtigungen vergeben. Mit diesem Vorgehen haben Sie sich also im aktuellen Kontext selbst um die sysadmin Rechte beraubt. Gleiches gilt für den Zugriff auf andere Datenbanken, die allerdings in der obigen Tabelle nicht aufgelistet werden. Sobald Sie EXECUTE AS USER verwenden, agieren Sie sich ausschließlich in der aktuellen Datenbank. Würde nun ein DDL Trigger versuchen, Ihre Berechtigungen auf Serverebene zu missbrauchen oder Daten aus anderen Datenbanken abzuziehen, wird er diese nicht finden können. Geben Sie sich als Datenbank Owner aus, haben Sie weiterhin alle Rechte und Zugriffe auf der Datenbank, um Wartungsarbeiten durchführen zu können. Natürlich besteht aber weiterhin die Möglichkeit, dass Benutzer mit niedrigeren Berechtigungen über den DDL-Trigger Code im Rahmen eines db_owner ausführen können.

Mit dem Befehl REVERT können Sie den Identitätswechsel dann wieder rückgängig machen und zu den Ursprungsberechtigungen zurückkommen. Und dabei brauchen Sie keine Bedenken haben, dass der REVERT von einem DDL Trigger ausgenutzt werden könnte. Dieser wirkt sich nur auf ein EXECUTE AS aus.

Nach der Anwendung von EXECUTE AS USER sehen die Snippets folgendermaßen aus:

SELECT @sql = 'EXECUTE AS USER = ''dbo''
               ALTER INDEX ' + quotename(@ixname) + ' ON ' + 
               quotename(@schema) + '.' + quotename(@table) + ' REORGANIZE
               REVERT'
SELECT @sp_executesql = @db + '.sys.sp_executesql'
EXEC @sp_executesql @sql

SELECT @sql = 'USE ' + quotename(@db) + '
              EXECUTE AS USER = ''dbo''
              ALTER INDEX ' + quotename(@ixname) + ' ON ' + 
              quotename(@schema) + '.' + quotename(@table) + ' REORGANIZE
              REVERT'
EXEC (@sql)

→ Achten Sie darauf, dass Sie zum Wechseln der Zieldatenbank im zweiten Schritt den USE Befehl vor EXECUTE AS - stellen.

Ist die Datenbank als vertrauenswürdig (TRUSTWORTHY) eingestuft worden und einem sa zugeordnet, erzielt der EXECUTE AS USER Befehl jedoch nicht den gewünschten Effekt und der DDL Trigger hat Zugriff auf die sysadmin Berechtigung. Die Kombination von TRUSTWORTHY und der Ernennung eines sa als Owner der Datenbank stellen an sich schon ein großes Sicherheitsrisiko dar, was einem Angreifer mittels DDL Trigger den Aufstieg zu einem Systemadministrator leicht machen würde. Wir empfehlen daher jeder Datenbank einen eigenen SQL Server Login als Besitzer zuzuordnen und ihm darüber hinaus keine Berechtigungen zu erteilen. Etablieren Sie das als best practice in Ihrer Umgebung.

In diesem Abschnitt haben wir uns auf Wartungsjobs mit ihren Tücken und Fallen konzentriert. Sie sind letztlich das offensichtlichste Ziel von Angreifern. Die Regelmäßigkeit der Durchführung bietet eine gute Basis, sich mittels DDL Triggern sysadmin Rechte zu erschleichen, da Angreifer somit nicht lange warten müssen.

Szenario 2 - Angriff auf db_owner

Nicht nur sysadmins sind von Angriffen durch DDL Trigger betroffen. Als db_owner kann es Sie genauso leicht treffen. Die Gefahr besteht vor allem, wenn ein Mitglied mit der Rolle db_ddladmin existiert. Bei manchen Usern empfiehlt es sich, diese als db_ddladmin zu berechtigen, damit diese gespeicherte Prozeduren, Tabellen und Trigger erstellen und bearbeiten können. Die User sollten aber nach Möglichkeit nicht dazu berechtigt werden, DDL Trigger ändern zu dürfen.

Wir empfehlen Ihnen daher, sofern Sie einen db_ddladmin in Ihrer Umgebung haben, folgenden Befehl auszuführen:

DENY ALTER ANY DATABASE DDL TRIGGER TO [Domain\User]

Die noch bessere Alternative wäre, den User nicht direkt als db_ddladmin zu berechtigen. Fügen Sie stattdessen eine Datenbankrolle oder AD Gruppe zu db_ddladmin hinzu und lassen darüber die ausgewählten Benutzer entsprechende Berechtigungen erhalten. Verweigern Sie wie oben gezeigt dieser Rolle oder AD Gruppe die Berechtigung ALTER ANY DATABASE DDL TRIGGER. Damit müssen Sie nicht jedes Mal daran denken, die Berechtigung auf Userebene zu entfernen, sobald ein neuer Entwickler diese Rolle bekommen soll.

Dies ist umso wichtiger, wenn Sie DBA-Aufgaben sowohl auf Server- als auch auf Datenbankebene übernehmen. Vergeben Sie diese Berechtigungen also nur an vertrauenswürdige und verlässliche Kolleg:innen. Je umfangreicher die vergebenen Berechtigungen sind, desto attraktiver werden sie für Angreifer.

Das Szenario Cross-db-Angriffe behandeln wir zu einem späteren Zeitpunkt im Verbund mit dem Thema “Angriff über DML Trigger”.

Instandhaltungsaufträge

Wartungs-Jobs dienen mittlerweile als beliebtes Ziel für Angreifer. Der Hintergrund liegt an der regelmäßigen, wenn nicht sogar täglichen Durchführung der Jobs. Im vorherigen Kapitel sind wir auf die selbstständige Implementierung von Wartungs-Jobs und die Verwendung von EXECUTE AS USER = 'dbo' eingegangen.

Letztlich müssen wir das Rad bei Wartungs-Jobs nicht neu erfinden und so greifen viele auf die folgenden beiden Standard-Lösungen zurück

  1. Wartungspläne in SQL Server Management Studio
  2. Wartungslösung von Ola Hallengren

Wir starten mit der Wartungslösung von Ola Hallengren. Sie verfügt über eine Prozedur IndexOptimize die den Parameter @ExecuteAsUser akzeptiert. Darüber können Sie anfordern, dass die generierten Kommandos von einem angegebenen Benutzer ausgeführt werden sollen. Hierfür können neben dbo auch andere User verwendet werden. Die Idee dahinter ist, dass Sie einen User in jeder Datenbank haben, der zur db_ddladmin Gruppe hinzugefügt wurde. Damit können sie sich auch vor dem Hijacking der db_owner Rolle schützen. Damit ein bestimmter User in jeder neuen Datenbank angelegt wird, können sie diesen zur model Datenbank hinzufügen.

Die Standardeinstellung für @ExecuteAsUser ist NULL. Dies bedeutet im Umkehrschluss, dass hier kein Identitätswechsel vorliegt und Sie somit nicht gegen Permission Hijacking geschützt sind, bis sie diesen Default-Wert überschreiben. Beachten Sie, dass der Job (wie oben bereits thematisiert) bei @ExecuteAsUser='dbo' aufgrund einer Nichtübereinstimmung zwischen dbo und dem Eintrag in sys.databases fehlschlagen kann.

Mit Wartungsplänen in SSMS gehen Sie in der Regel ein “größeres” Risiko ein, da Sie keine Möglichkeit haben, gegen den Missbrauch von Berechtigungen vorzugehen. Behalten Sie jedoch im Hinterkopf, dass das Problem hauptsächlich bei Index- & Statistikwartungen auftritt, da diese DDL Trigger auslösen. BACKUP und DBCC CHECKDB lösen keine DDL Trigger aus. Somit ist ein entsprechender Schutz mit EXECUTE AS USER nicht notwendig. (Hinweis: Es gibt nach wie vor Szenarien, die noch nicht dahingehend untersucht wurden, ob sie DDL Trigger anstoßen)

Außerdem muss es erst User in der Datenbank geben, die berechtigt wurden, einen DDL Trigger zu erstellen, ohne gleichzeitig sysadmin zu sein. In den meisten Teams übernehmen die gleichen Personen die DBA-Tätigkeiten auf Server- als auch auf Datenbankebene. So lange sie also keine weiteren Personen der db_owner oder db_ddladmins hinzufügen, haben Sie diesbezüglich nichts zu befürchten.

Gibt es jedoch User, die die Rolle db_owner inne haben, ohne Mitglied von sysadmin zu sein und die genannten Möglichkeiten mit SSMS Wartungsplänen oder dem @ExecuteAsUser Parameter von Ola Hallengren kommen für Sie nicht in Frage, sollten Sie folgende Optionen berücksichtigen:

  1. Schreiben Sie Ihren eigenen Wartungsplan, der EXECUTE AS USER dort verwendet, wo es notwendig ist
  2. Nutzen Sie die Wartungslösung von Ola Hallengren zumindest hinsichtlich Index- & Statistikpflege, indem Sie @ExecuteAsUSer für IndexOptimize verwenden.
  3. Planen Sie separate Index- & Wartungsjobs für jede Datenbank, in der es db_owner gibt, die nicht gleichzeitig sysadmin sind. Dieser Job muss einem SQL Server Login zugeordnet sein, der in der Datenbank die db_owner Berechtigung hat, jedoch keine weiteren datenbankübergreifenden Berechtigungen hat.
  4. Teilen Sie Ihren lokalen Usern in den betroffenen Datenbanken mit, dass die Index- & Statistikwartungen eigenständig geplant werden müssen.
  5. Sperren Sie die DDL Trigger auf Datenbankebene mit einem DDL Trigger auf Serverebene. Damit ermöglichen Sie sich, die Wartungspläne in SSMS zu verwenden, ohne entsprechende Jobs planen zu müssen.
  6. Wenn Sie als DBA einen Entwickler oder Consultant als db_owner zum Troubleshooting oder Performance Tuning hinzugefügt haben, können Sie diese Person vielleicht nachträglich zu db_ddladmin herabstufen und anschließend das Recht ALTER ANY DATABASE DDL TRIGGER entziehen.
  7. Vertrauen Sie darauf, dass die fragwürdigen Personen ihre Berechtigungen nicht missbrauchen.

Letztlich müssen Sie entscheiden welche Variante am Besten und sinnvollsten für Ihre Umgebung ist, da beispielsweise Nummer 7 nicht auf hochsensiblen Systemen praktiziert werden sollte, auf Entwicklungs- oder Testservern aber eher kein Problem darstellt. Datenbank DDL Trigger mittels DDL Triggern auf Serverebene zu verbieten macht eigentlich nur Sinn, wenn Sie die volle Kontrolle darüber haben, von wo Datenbankbackups wiederhergestellt werden können. Haben Entwickler selbst die Möglichkeit Datenbanken wiederherzustellen, da sie der Besitzer sind oder der Server-Rolle dbcreator angehören, können auch darüber Datenbank DDL Trigger mit schadhaftem Code auf Ihren Server gelangen. Außerdem können DDL Trigger auch sehr hilfreich sein, wie beispielsweise bei Audits.

Sollten Sie den DDL Trigger auf Serverebene umsetzen wollen, können Sie diesen mit folgendem Befehl erstellen:

CREATE OR ALTER TRIGGER server_ddltri ON ALL SERVER 
                FOR CREATE_TRIGGER, ALTER_TRIGGER AS
   DECLARE @objecttype varchar(20),
           @eventdata xml = eventdata()
   SELECT @objecttype = E.e.value('(./text())[1]', 'nvarchar(MAX)')
   FROM   @eventdata.nodes('/EVENT_INSTANCE/TargetObjectType') AS E(e)
   IF upper(@objecttype) = 'DATABASE'
   BEGIN
      ROLLBACK TRANSACTION
      ; THROW 50000, 'Database level DDL triggers not permitted on this server', 1
   END

Die Bereitstellung des DDL Triggers auf Serverebene allein reicht nicht aus. Sie müssen alle vorhandenen Datenbanken auf die DDL Trigger prüfen, da diese durch den neuen Trigger nicht gelöscht bzw. an der Ausführung gehindert werden. Verwenden Sie dafür folgenden Befehl:

CREATE TABLE #ddltridbs (db sysname NOT NULL PRIMARY KEY)
DECLARE @sql nvarchar(MAX)
SELECT @sql = 
   (SELECT 'IF EXISTS (SELECT *
                       FROM   ' + quotename(name) + '.sys.triggers
                       WHERE parent_class_desc = ''DATABASE'')
              INSERT #ddltridbs (db) VALUES(' + quotename(name, '''') + ')' +
              char(13) + char(10)
    FROM   sys.databases
    WHERE  state_desc = 'ONLINE'
      AND  database_id > 4
    FOR XML PATH(''), TYPE).value('.', 'nvarchar(MAX)')
PRINT @sql
EXEC (@sql)
SELECT * FROM #ddltridbs

Diesen Befehl sollten Sie ebenfalls verwenden, wenn Sie ein älteres Backup oder eines von außerhalb wiederherstellen möchten.

Möchten Sie diesen Weg einschlagen und dennoch das Hinzufügen von nützlichen DDL Triggern erlauben, können Sie den nachfolgenden Befehl zu Ihrem Trigger auf Serverebene hinzufügen:

IF is_srvrolemember('sysadmin') = 1 RETURN

Somit sind Administratoren nach wie vor in der Lage, die Trigger zu erstellen. Weiterhin sollten Sie den DDL Trigger immer EXECUTE AS SELF hinzufügen:

CREATE OR ALTER TRIGGER ddltri ON DATABASE WITH EXECUTE AS SELF
        FOR DDL_DATABASE_LEVEL_EVENTS AS

SELF wird hierbei verwendet, da OWNER für DDL Trigger nicht erlaubt sind. Dadurch wird sichergestellt, dass der Trigger in einem nicht vertrauenswürdigen Sicherheitskontext ausgeführt wird und keine Aktionen außerhalb der Datenbank ausgeführt werden können. Dazu gehört beispielsweise Code, der vom DDL Trigger ausgeführt wird. Sie sollten den Datenbank Usern mitteilen, dass sie zukünftig original_login() verwenden müssen, um die Umgebung ordnungsgemäß zu überwachen, da SYSTEM_USER nur den imitierten Kontext zurück liefert.

DML Trigger##

Wenn ein DDL Trigger für den Missbrauch von Berechtigungen verwendet werden kann, kann dies auch ein regulärer Trigger für INSERT, UPDATE oder DELETE.

Szenario 1 - Angriff auf sysadmin
Als erfahrener DBA wurden sie vermutlich schon das ein oder andere Mal gefragt, eine Datenbereinigung durchzuführen. Nicht etwa weil es in Ihrem Aufgabenbereich liegen würde, sondern weil dazu auch die ein oder andere kompliziertere SQL Anweisung zur Aktualisierung von Tabellen innerhalb der Datenbank nötig ist und Sie das letztendlich besser können als der Entwickler. Oder zumindest behauptet dieser es. Denn möglicherweise hat diese Person auch einen DML Trigger zu einer Tabelle hinzugefügt der prüft, ob ein User zu der sysadmin Gruppe gehört, nur um dann schadhaften Code auszuführen.

Zum Schutz vor DDL Triggern haben wir in den vorherigen Abschnitten bereits erklärt, wie der Befehl

EXECUTE AS USER = 'dbo'

verwendet werden soll. Hier geht es jedoch vordergründig um den Schutz der sysadmin Berechtigungen, die db_owner Rolle werden wir nun im weiteren Verlauf des Artikels beleuchten.

Sollte eine Aktualisierung von Datenbanken auf Ihrer To-Do-Liste stehen und entsprechende Daten von verknüpften Servern müssen abgerufen werden, empfehlen wir Ihnen diese Daten in temporäre Tabellen zu speichern, bevor Sie Ihre Berechtigungen herabstufen und die tatsächliche Aktualisierung mit EXECUTE AS USER + REVERT durchführen. Solange Sie nur Daten selektieren, gibt es keine Angriffsfläche.

Für das genannte Szenario sollten Sie in der Tabelle sys.triggers nachsehen, welche Trigger in den Tabellen vorhanden sind, die Sie aktualisieren möchten. Nicht nur um bösartigen Code zu identifizieren, sondern generell um zu erfahren, ob Trigger vorhanden sind und falls ja, welchen Zweck sie erfüllen. Trotzdem ersetzt diese Vorgehensweise nicht die Verwendung von EXECUTE AS USER.

Szenario 2 - Cross-DB Angriffe

In diesem Szenario sind Sie der Admin der Datenbank 1, die eine Vielzahl an sensiblen Daten enthält. Der Administrator der Datenbank 2 möchte nun einige Daten Ihrer Datenbank in seine Datenbank übernehmen und teilt Ihnen mit, dass er dafür eine eigene Tabelle erstellt hat, in die die Daten eingespeist werden können. Dafür wurde temporär ein User mit INSERT Berechtigungen für diese Tabelle erstellt. Die Gefahr besteht, dass es möglicherweise einen Trigger gibt, der dem Administrator der Datenbank 2 weitaus mehr Berechtigungen zuteilt, als vorgesehen sind. Er könnte als Nutzer zu Ihrer Datenbank hinzugefügt werden oder mehr Daten kopieren, als die Sie ihm eigentlich geben wollen.

Wenn Ihnen die Recherche in sys.triggers auch keine Hilfe ist, könnte es möglicherweise daran liegen, dass der Admin der Datenbank 2 Ihre VIEW DEFINITION auf verschiedene Ebenen eingeschränkt hat und Ihnen daher keine Trigger aufgelistet werden. Weiterhin kann es sein, dass Sie eventuell den schädlichen Trigger entdecken, jedoch seinen Code nicht einsehen können/dürfen.

Eine andere Möglichkeit besteht darin, dass Ihnen der Datenbank 2 Admin für den Datentransfer die Kontrolle über ein Schema überträgt. In diesem dürfen Sie selbst die Tabellen zur Ablage der Daten erstellen. Was Sie mit Ihren Berechtigungen jedoch nicht herausfinden können ist, dass DDL Trigger existieren, die schädlichen Code ausführen.

Wie können Sie in diesem Fall also vorgehen? Auch hier lautet die Antwort wieder: EXECUTE AS USER. Sie haben jedoch keine Berechtigungen zur Impersonierung von dbo auf der Datenbank 2, also welchen User können Sie stattdessen verwenden?

Lösung: Sie geben sich als sich selbst aus. Angenommen ihr Login ist DOMAIN\AdminForDB_1. Extrahieren Sie alle benötigten Daten zunächst in temporäre Tabellen und nutzen anschließend folgenden Befehl zum Datentransfer in die Datenbank B:

USE B 
go 
EXECUTE AS USER = 'DOMAIN\AdminForDB_1'

Damit setzen Sie den Sicherheitskontext außer Kraft und kein Trigger kann Ihre Berechtigungen missbrauchen. Überprüfen Sie jedoch im Vorhinein in sys.databases, ob die Datenbank 2 als TRUSTWORTHY gekennzeichnet ist. Notwendig dafür sind Ihnen vorhandene Serverberechtigungen wie z.B. VIEW ANY DATABASE. Diese Berechtigung wird standardmäßig an alle User vergeben.

Szenario 3 - Angriff auf db_owner

Angriffe auf db_owner können über DML Trigger von Mitgliedern mit der Rolle db_ddladmin oder von Usern mit der ALTER Berechtigung auf dem dbo Schema oder anderen wichtigen Tabellen/Schemata durchgeführt werden. In den meisten Fällen erstellen sie einen Trigger für eine Tabelle und verleiten die Opfer dazu, verschiedene Operationen auf dieser Tabelle auszuführen.

Mit folgendem DDL Trigger können Sie sich vor Angriffen über DML Trigger schützen:

CREATE TRIGGER no_triggers_please ON DATABASE FOR 
     CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER AS
   IF is_rolemember('db_owner') = 0
   BEGIN
      ROLLBACK TRANSACTION
      ; THROW 50000, 'Only db_owner can work with triggers in this database', 11
   END

Dieser Trigger ist vor allem hilfreich, wenn in der verwendeten Datenbank wenig bis gar keine Trigger im Regelbetrieb verwendet werden, oder sie Entwicklern das Arbeiten mit gespeicherten Prozeduren, nicht aber mit Triggern erlauben wollen. Sie sollten jedoch darauf achten, dass Mitglieder mit der Rolle db_ddladmin über keine ALTER ANY DATABASE DDL TRIGGER Rechte verfügen. Ansonsten könnten sie den DDL Trigger kurzzeitig deaktivieren, um anschließend einen DML Trigger mit bösartigem Code zu erstellen.

Grundsätzlich wollen wir von der Verwendung von Triggern nicht gänzlich abraten. Sie sind gerade hinsichtlich der Datenbankintegrität ein hilfreiches Tool für Entwickler. Sie könnten durchaus Entwickler haben, denen Sie dedizierte Rechte für das Entwickeln mit DDL (also z.B. Prozeduren, jedoch keine DDL Trigger!) und DML Triggern gestatten, aber Sie wollen nicht, dass diese Personen auch Benutzer oder Zertifikate erstellen. In diesem Fall ist db_ddladmin eine gute Wahl.

Seitennavigation

Zur Artikel Übersicht

Auf dieser Seite

SQL Server 2014 Migration SupportNEU
Im Sommer 2024 endet der Extended Support des Microsoft SQL Server 2014 SP3. Erfahren sie wie wir Sie bei Ihrer Migration unterstützen können! mehr erfahren