Einleitung
Benutzereingaben sind immer auch eine potenzielle Möglichkeit für Angreifer, um schadhaften Code in eine Webapplikation einzuschleusen. Mit diesem Code führen sie dann entweder unerwünschte Aktionen aus oder greifen auf sensible Daten zu. Ein Beispiel ist die sogenannte SQL Injection.
Bei einer SQL Injection gibt ein Angreifer gezielt manipulierte Eingaben ein, die vom empfangenden Programm verarbeitet werden, sodass sie die auszuführende SQL-Anweisung verändern. Dadurch kann der Angreifer unautorisierten Zugriff auf Daten erlangen, sensible Informationen abfragen oder sogar Daten in der Datenbank manipulieren oder löschen.
Eine SQL Injection ist natürlich nur möglich, wenn die Webapplikation mit einer Datenbank interagiert. Beispiele dafür sind:
- Benutzeranmeldung: Bei der Anmeldung prüft die Webapplikation die eingegebenen Benutzerdaten gegen in der Datenbank gespeicherte Daten.
- Benutzerregistrierung: Bei der Registrierung werden neue Benutzerdaten in die Datenbank eingefügt.
- Suchfunktion: Bei einer Suche wird die Datenbank nach Einträgen durchsucht, die mit den eingegebenen Begriffen übereinstimmen.
Auf der Webseite des LUPFERs gibt es ein Formular zur Benutzeranmeldung. Wenn nun die Benutzerdaten in einer Datenbank gespeichert sind, wie kann dann eine Benutzereingabe eine SQL-Abfrage beeinflussen? Dafür überlegen wir, wie eine SQL-Abfrage gestaltet sein könnte, mit der ein Benutzer bei der Webapplikation authentifizert werden kann:
SELECT username FROM users WHERE username = '<Username>' AND password = '<Password>';
Hier gehen wir davon aus, dass die Benutzer in einer Tabelle users gespeichert sind, die mindestens zwei Spalten besitzt.
Das sind eine Spalte für den Benutzernamen und eine für das Passwort, die hier username und password heißen sollen.
Die Benutzereingaben sind dabei von einfachen Anführungszeichen umschlossen, um sie als Zeichenketten zu kennzeichnen.
Um einen Benutzer zu authentifizieren, soll nun über die SELECT-Anweisung der username aus der Tabelle users zurückgegeben werden, für den die Bedingung der WHERE-Klausel erfüllt wird:
username = '<Username>': Prüft, ob der eingegebene Benutzername<Username>mit einem Eintrag in der Tabelle übereinstimmtpassword = '<Password>': Prüft zusätzlich, ob das eingegebene Passwort<Password>mit dem entsprechenden Benutzer übereinstimmt
Die Abfrage gibt also den Benutzernamen zurück, wenn die Kombination aus Benutzername und Passwort korrekt ist. Ist dies nicht der Fall, wird kein Ergebnis zurückgegeben, was bedeutet, dass der Benutzer nicht authentifiziert werden konnte.
Gelingt es mit den Benutzereingaben diese SQL-Anweisung so zu manipulieren, dass die Webanwendung eine nicht vorgesehene Reaktion zeigt, ist die SQL Injection erfolgreich. Ob das hier möglich ist, wirst du nun untersuchen.
Testen auf Sicherheitsmaßnahmen
Zunächst geht es darum, zu prüfen, ob ein Eingabefeld anfällig für SQL Injection ist. Dafür testest du, ob deine Eingabe direkt in einer SQL-Abfrage eingefügt wird, ohne dass sie validiert oder bereinigt wird. Das erkennst du, indem du einzelne Zeichen eingibst, die in SQL eine besondere Bedeutung haben:
- Ein einfaches Anführungszeichen
'kennzeichnet Zeichenketten. - Ein Semikolon
;steht am Ende einer Abfrage.
Wenn die Webanwendung diese Zeichen nicht filtert oder so maskiert, dass sie ihre spezielle Bedeutung verlieren, führen sie zu einer fehlerhaften Datenbankabfrage. Unter Umständen verhält sich die Webanwendung dann anders als bei der Eingabe von Zeichen ohne spezielle Bedeutung.
Das Eingabefeld Username ist offensichtlich anfällig für SQL Injection.
Exploitation
Wie kannst du nun aber durch deine Eingabe die SQL-Anweisung so manipulieren, dass sie gültig ist und eine unerwartete Aktion ausführt? Hier ist noch einmal die SQL-Abfrage:
SELECT username FROM users WHERE username = '<Username>' AND password = '<Password>';
Mit dieser Anweisung gelingt der Login, wenn die Bedingung nach der WHERE-Klausel wahr ist.
Wenn es dir durch deine Eingabe gelingt, dass die Bedingung wahr wird, auch wenn du keine Zugangsdaten kennst, dann wirst du trotzdem eingeloggt.
Dafür brauchst du noch weitere Zeichen mit besonderer Bedeutung in SQL oder SQL-Schlüsselwörter.
Dies sind:
- Kommentartrennzeichen wie
--oder# AND,OR
Mit dem Payload user';-- lautet die resultierende SQL-Abfrage:
SELECT username FROM users WHERE username = 'user';--AND password = '<Password>'
Wenn du also den Benutzernamen user eingibst, den du dank der Brute-Force-Attacke kennst, dann wirst du auch ohne Passwort eingeloggt, indem du
- Mit dem einfachen Anführungszeichen
'die Eingabe der Zeichenkette abschließt - Mit dem Semikolon
;die manipulierte SQL-Abfrage beendest und mit den zwei Bindestrichen--den Rest der ursprünglichen Abfrage auskommentierst
Damit wird also die Bedingung nach der WHERE-Klausel wahr, sobald die Tabelle users einen Eintrag für den Benutzernamen user enthält.
Aber du kannst dich sogar einloggen, wenn du nicht einmal einen Benutzernamen kennst.
Dies gelingt mit dem Payload ' OR 1=1;--:
SELECT username FROM users WHERE username = '' OR 1=1;--AND password = '<Password>'
Die SQL-Abfrage ist also wie folgt manipuliert:
- Sie wählt den Eintrag aus der Tabelle
usersaus, bei dem der Wert fürusernameleer ist. Dies dürfte für keinen Eintrag zutreffen. - Das
ORfügt derWHERE-Klausel eine weitere Bedingung1=1hinzu, die immer erfüllt ist. Daher ist der gesamte Ausdruck immer wahr, sodass alle Tabelleneinträge zurückgegeben werden. - Mit den beiden Bindestrichen wird der Rest der ursprünglichen Abfrage auskommentiert, sodass die manipulierte Abfrage korrekt ist.
Damit wirst du als admin angemeldet, weil das wahrscheinlich der erste Eintrag in der Tabelle users ist.
Wie bei der Brute-Force-Attacke hast du nun Zugriff auf weitere Ressourcen, für die du erneut die Reconnaissance durchführen kannst.
Bevor du die Webapplikation weiter untersuchst, solltest du dir bewusst machen, welche Schwachstellen hier eine SQL-Injection ermöglichen:
- Die Eingaben werden nicht validiert.
Dadurch behält das einfache Anführungszeichen
'seine Bedeutung in einer SQL-Abfrage. - Es werden keine sogenannten Prepared Statements eingesetzt.
Dies sind vorgefertigte SQL-Abfragen, die zur Vermeidung von SQL-Injection und zur Verbesserung der Leistung verwendet werden und die Parameterplatzhalter enthalten.
Für den Login mit Prepared Statement wären dann folgende SQL-Abfragen erforderlich:
Die Benutzereingaben werden damit strikt als Daten, nicht aber als Code behandelt.PREPARE login_user(text, text) AS SELECT * FROM users WHERE username = $1 AND password = $2; EXECUTE login_user('<Username>', '<Password>');
Weitere Techniken der SQL Injection
Systemabhängige Payloads
Es gibt verschiedene Datenbanksysteme, die SQL alle etwas unterschiedlich implementieren. Daher sind auch die Payloads für eine erfolgreiche SQL Injection systemabhängig. Wenn du nicht weißt, welches Datenbanksystem eingesetzt wird, musst du Payloads für die verschiedenen Systeme ausprobieren. Im Internet gibt es dafür sogenannte Cheat Sheets.
Enumeration und Exploitation lassen sich auch toolgestützt durchführen.
Eine Möglichkeit ist sqlmap.
Arten der SQL Injection
Was du hier gemacht hast, bezeichnet man als Boolean Based SQL Injection.
Bei dieser Technik wird eine Anfrage so manipuliert, dass sie entweder wahr oder falsch zurückgibt.
Mögliche Auswirkungen sind:
- Wie bei der Webapplikation des LUPFERs kann unter Umständen die Authentifizierung umgangen werden.
- Es können Informationen schrittweise extrahiert werden, indem Bedingungen in der
WHERE-Klausel hinzugefügt werden.
Daneben gibt es noch die Error Based SQL Injection. Wenn eine Webapplikation anfällig für diese Art der SQL Injection ist, gibt sie Fehlermeldungen der Datenbank aus, die Informationen über die Datenbankstruktur oder -inhalte enthalten:
- Die Fehlermeldungen können Hinweise auf die Struktur der SQL-Abfragen geben. Dies können zum Beispiel die Namen von Tabellen oder Spalten sein, die in der Abfrage verwendet werden.
- Die Fehlermeldungen können Informationen über die Version der verwendeten Datenbanksoftware enthalten.
Damit du dir vorstellen kannst, wie so eine Fehlermeldung aussieht, ist hier ein Beispiel:
Um zu testen, ob die Webapplikation anfällig für SQL Injection ist, hast du den Payload ' als Username und ein beliebiges Passwort eingegeben.
Die Datenbank gibt darauf folgenden Fehler zurück:
ERROR: syntax error at or near "a"
LINE 1: select * from users where username = ''' AND password ='a';
^
Würde diese Fehlermeldung vom Webserver an den Client weitergegeben werden, wüsstest du, dass die Benutzer in der Tabelle users gespeichert sind.
Bei der Union Based SQL Injection wird die UNION-Anweisung verwendet, um Daten aus mehreren Tabellen in einer einzigen Abfrage zu kombinieren.
Die ursprüngliche SQL-Abfrage wird also so modifiziert, dass sie zusätzlich zu den regulären Ergebnissen auch Daten aus anderen Tabellen zurückgibt.
Damit werden Daten aus einer Tabelle extrahiert, die nicht für den Benutzer sichtbar sein sollten.
Der Gebrauch des Schlüsselworts UNION ist im Abschnitt Datenbank beschrieben.
