SQL Injection

aus HaBo WiKi, der freien Wissensdatenbank von http://www.hackerboard.de
Wechseln zu: Navigation, Suche

Bei der SQL Injection handelt es sich um eine Technik, die es einem Hacker erlaubt, über Sicherheitslücken in dynamisch auf Datenbanken zugreifenden Server-Scripts Daten in einer Datenbank zu modifizieren, erzeugen oder löschen. Außerdem ist es mittels SQL-Injection möglich, Passwortabfragen u.ä. zu umgehen, insofern diese durch eine Datenbankabfrage ablaufen. Viele, auch große, Server sind vor SQL-Injection nicht geschützt, was gefährlich ist, weil es z.B. einem Neonazi die Verbreitung von Propaganda auf Online-Auftritten von renommierten Zeitungen u.ä. ermöglichen könnte uvm.

Grundlagen

Datenbanken footprinten und verändern

Die Technik beruht auf der Schwäche vieler Skripte, keine Verifizierung von vom User gemachten Eingaben durchzuführen, sondern diese direkt in ein SQL-Statement einzubauen. Angenommen ein Script zur Darstellung einer Nachricht auf der Seite www.moeglicheseite.com sieht folgendermaßen aus:

<%
ID=request("ID")
Abfrage="SELECT ID, titel, inhalt FROM Nachrichten WHERE ID=" &ID&
Rs0.Open Abfrage,oConn
%>

Ein Link zu einer bestimmten Nachricht sieht dann folgendermaßen aus:

http://www.moeglicheseite.com/nachrichten.asp?ID=6

Natürlich kann ein User auch einfach die Zahl nach ID verändern - es wird dann eine andere Nachricht angezeigt. Was passiert aber, wenn ein Hacker hinter ID=6 noch mehr SQL einfügt, beispielsweise OR? Das Script führt dann folgende Abfrage aus (wenn der Parameter ein String ist, muss hinter den Wert ein Apostroph, gefolgt von SQL, um Anführungszeichen im SQL-Script zu schließen):

Abfrage="SELECT ID, titel, inhalt FROM Nachrichten WHERE ID=6 OR

Das liefert eine Fehlermeldung, die, wenn keine Schutzmaßnahmen getroffen wurden, im Browser angezeigt wird - sie könnte folgendermaßen aussehen:

Microsoft OLE DB Provider for ODBC Drivers error '80040e14' 
[Microsoft][ODBC SQL Server Driver][SQL Server]Line 1: Incorrect syntax near 'OR'. 
nachrichten.asp, line 3

Eine solche Fehlermeldung zeigt, dass SQL-Injection grundsätzlich möglich ist - doch was dann? Um SQL-Kommandos wie ändern, löschen usw. ausführen zu können, muss man über den Aufbau der Datenbank, also Tabellen- und Spaltennamen, Bescheid wissen. Mittels des Befehls HAVING 1=1 erhalten wir hilfreiche Fehlermeldungen:

http://www.moeglicheseite.com/nachrichten.asp?ID=6 HAViNG 1=1--

führt zu

Microsoft OLE DB Provider for ODBC Drivers error '80040e14' 
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'Nachrichten.ID' is invalid in the select list
because it is not contained in an aggregate function and there is no GROUP BY clause.
nachrichten.asp, line 3

Das System verlangt also gemäß den SQL-Regeln eine GROUP BY-Klausel vor dem HAVING-Befehl:

http://www.moeglicheseite.com/nachrichten.asp?ID=6 GROUP BY Nachrichten.ID HAViNG 1=1--''

und schon erhält man die nächste Fehlermeldung:

Microsoft OLE DB Provider for ODBC Drivers error '80040e14' 
[Microsoft][ODBC SQL Server Driver][SQL Server]Column 'Nachrichten.titel' is invalid in the select list
because it is not contained in an aggregate function and there is no GROUP BY clause.
nachrichten.asp, line 3

Nun wissen wir:

  • Es gibt eine Tabelle, die entweder Nachrichten heisst, oder diesen Alias trägt
  • Diese Tabelle hat die Spalten ID und Titel

Man fügt also dann immer alle bekannten Spalten der GROUP BY-Klausel hinzu, bis alle Spalten aller beteiligten Tabellen bekannt sind. Nun ist es Zeit, auszuprobieren, ob man eigenes SQL einfügen kann - bespielsweise durch Anwenden eines UPDATE befehls:

http://www.moeglicheseite.com/nachrichten.asp?ID=6 UPDATE nachrichten SET titel='test' WHERE ID=6--

Der Doubledash am Ende des Statements bedeutet für Microsoft-SQL das Einfügen eines Kommentars und somit das Ende des Statements. Bei anderen SQL-Dialekten ist es notwendig, vor und nach dem neuen Befehl ein Semikolion einzufügen und dann den ersten Teil des alten Befehls zu rekonstruieren. Wenn die Tabelle wirklich Nachrichten heisst, und das nicht nur ihr Alias war, so sollte man jetzt beim Aufruf der Nachricht mit ID 6 statt dem alten Titel den Titel 'test' lesen können. Genau so können dann Einträge gelöscht werden - das Einfügen von Einträgen ist schon etwas komplizierter. Denn dazu muss man wissen, welche Datentypen die einzelnen Spalten haben - was bei 30+ Spalten in einer typischen Tabelle nicht so leicht ist. Aber irgendwann hat man, durch mehrfaches anwenden des INSERT Befehls, die richtige Kombination zusammen, und kann frei Einträge einfügen. Eleganter ist es, den UNION ALL SELECT Befehl zu verwenden - dieser zwingt Werte in das Script.

SELECT ID, titel, inhalt FROM Nachrichten WHERE ID=6 UNION ALL SELECT 4,'Testtitel','Laber Rhababer' FROM Nachrichten

Zeigt also statt der eigentlichen Nachricht eine Nachricht mit dem Titel 'Testtitel' und dem Inhalt 'Laber Rhababer'. So kann man:

  • Datentypen ermitteln - so muss z.B. der zweite Wert, also titel, ein Varchar sein sonst gibt es eine Fehlermeldung
  • Das Footprinten verfeinern: Microsoft hat eine Tabelle namens sysObjects in jeder Datenbank. Dort stehen, als name die wahren Namen der Tabellen. Lieferten uns die Fehlermeldungen nur einen Alias, so kann man durch das Statement UNION ALL SELECT 1,'testtitel',name FROM sysObjects WHERE xType='U'-- anstelle des Inhaltes die Namen der vom User erstellten Tabellen auslesen.

Logins umgehen

Oftmals wird bei einem Login auf einer Webseite eine Datenbankabfrage durchgeführt, die ungefähr folgendermaßen aussieht:

"SELECT username, passwort FROM user WHERE username='" &inputboxwert& "'AND passwort='" &passwortboxwert&"'"

Wenn das einen Datensatz liefert, war der Login erfolgreich, sonst nicht. Es ist offensichtlich, dass bei einem solchen Script SQL-Injection möglich ist, aber es ist gar nicht nötig, mit viel Aufwand zu footprinten usw. Denn es geht ja nur darum, das Script dazu zu bringen, mindestens einen Datensatz zu liefern, also muss man eine Bedingung einfügen, die immer wahr ist. Oft funktioniert z.B. die Eingabe von ' OR 1=1-- in das Passworteingabefeld. Eine andere Möglichkeit ist das Einfügen von ' OR ''=' in die username-Box und das Passworteingabefeld. Dann wird der Query nämlich zu

"SELECT username, passwort FROM user WHERE username='' OR '' = ''AND passwort='' OR ''=''"

- und ''='' ist eigentlich immer wahr.

Schutz vor SQL Injection

Es ist trotz der weiten Verbreitung der Sicherheitslücke sehr leicht, seine eigene Webseite vor ihr zu schützen. Man sollte:

  • Eine Input Validation vor die Datenbankabfrage setzen, also überprüfen, ob in den vom User eingegebenen Parameter nur erlaubte Zeichen erhalten
  • Die Ausgabe von Fehlermeldungen unterbinden
  • Dem Script nur SELECT-Rechte auf die Datenbank geben
  • Nutzen von Prepared Statements