LFI & RFI

Zum Thema Sicherheit von PHP Scripts (mit besonderem Fokus auf SQL Injections) gab es vor kurzem ja schon einen Eintrag, jedoch möchte ich einige weitere Tipps/Verbesserungen vorstellen, die ziemlich einfach umzusetzen sind ohne gross an Performance einzubüssen.

LFI
Als erstes möchte ich kurz auf LFIs (local file includes/inclusions) eingehen. In der Praxis entsteht eine solche Lücke meist aus Leichtsinn, Unwissenheit oder aufgrund der Faulheit des Coders bei der Nutzung von include oder require (selbstverständlich auch bei include_once respektive require_once). Hier ein kurzes Beispiel eines verwundbaren Scripts:

<?php
 
if (isset($_GET["page"]) && !empty($_GET["page"])) {
    $page = $_GET["page"];
    include $page.".php";
}
else
    inlcude "main.php";
 
?>

Sobald der GET Parameter “page” gesetzt ist (http://…/index.php?page=user) wird die Datei includet. Da keinerlei Überprüfung des Parameters erfolgt kann ein Besucher mit bösen Absichten jegliche Datei includen die er will zum Beispiel so: “http://…/index.php?page=../../../../../../etc/passwd” – Auf diese Weise erhält er Zugang zu jeder Datei auf dem Server auf dem der HTTPDaemon läuft (auf Finessen wie CHROOT Jails etc. gehe ich hier nicht ein).
Auch eine Abfrage wie:

<?php
 
if (isset($_GET["page"]) && !empty($_GET["page"]))
    $page = $_GET["page"];
else
    $page = "main";
 
if (file_exists($page.".php"))
    include $page.".php";
else
    inlcude "main.php";
 
?>

Wird nicht helfen, da die Möglichkeit ungewollte Files zu includen noch immer besteht.
Eine einfache Möglichkeit solche Lücken zu schliessen ist sogenanntes Whitelisting. Dabei erstellt man einen Array mit den Seiten und prüft dann ob der “page” Parameter darin enthalten ist:

<?php
 
$pages = array("main", "user", "guestbook");
 
if (isset($_GET["page"]) && !empty($_GET["page"]))
    $page = $_GET["page"];
else
    $page = "main";
 
if (in_array($page, $pages) && file_exists($page.".php"))
    include $page.".php";
else
    inlcude "main.php";
 
?>

So includet man einfach die “main.php” sobald der “page” Parameter nicht im Array ist und schliesst somit die LFI Lücke.

RFI
Eine ähnliche Attacke wie LFIs nur wird hier eine Datei von einem anderen Server geladen (zum Beispiel eine PHP Shell) und dann durch das Script ausgeführt. Auch hier hilft das oben vorgestellte Whitelisting um solchen Attacken vorzubeugen.

Fürs erste war es das nun auch schon, ich werde mich jedoch in Kürze mit einem weiteren Artikel, der noch weitere PHP Security Tipps beinhalten wird, melden, besucht den Blog einfach wieder oder haut ihn gleich in euren Feedreader rein.

So long
bl0b

Danke an bl0b fuer den Artikel, eventuell folgt demnaechst mehr von ihm 😉

Ade Rainbow Tables, Hallo GPU Brute-Force – Teil 3

Hallu,

wie auf Teil 2 kurz angesprochen folgt hier nochn kleiner 3ter Teil, wie man die Testsieger richtig anwendet. Wird fuer einige wahrscheinlich extrem uninteressant sein, aber fuer “Anfaenger” eventuell hilfreich 🙂

Ich fang mit BarsWF an, ich werd die CUDA Version nehmen, da ich eine Nvidia Grafikkarte habe (ATI User muessten die AMD BROOK Beta nehmen, mehr dazu wie gesagt auf der Herstellerseite)

Runterladen, entpacken und cmd oeffnen (start, ausfuehren, cmd – oder bei vista & win7 links unten das icon anklicken, suchen cmd enter fertig). Dann haben wir unsere tolle Windows CMD.

Ich erklaers mal auf die Simpelste methode^^ ihr zieht nun einfach die BarsWF_CUDA_x64.exe in eure cmd und drueckt enter, nun sehts die Usage:

Usage:
 -?                                     Prints this help
 -r                                     Continue previous work from barswf.save BarsWF updates it every 5 minutes or on exit
 -h 1b0e9fd3086d90a159a1d6cb86f11b4c    Set hash to attack
 -c 0aA~                                Set charset. 0 - digits, a - small chars, A - capitals, ~ - special symbols
 -C "abc23#"                            Add custom characters to charset.
 
 -X "0D0A00"                            Add custom characters in hex to charset.
 
 
 -min_len 3                             Minimal password length. Default 0. MAX 15!!! :-]
 -thread_n 128                          | Might increase speed on GTX260 and later.
 -grid_n 128                            | Try 192, 256 e.t.c. for both
 -gpu_time 1500                         How long GPU kernel allowed to work, in milliseconds. 1500 - a bit faster, 50 - a bit slower, but windows is more responsive. Default 500.

Fuer uns ist nur
-h 1b0e9fd3086d90a159a1d6cb86f11b4c Set hash to attack
-c 0aA~ Set charset. 0 – digits, a – small chars, A – capitals, ~ – special symbols

wichtig.

Ich nehm wieder c79b730cf5c8856702fbd8277483c2e3 als Hash. nun druecken wir einmal pfeiltaste nach oben ( xD ) um unsere .exe samt richtigen pfad zu haben, oder wie ziehen es neu in die cmd und schreiben dazu “-h c79b730cf5c8856702fbd8277483c2e3 -c a”

-h gibt Hash an, -c a waehlt fuer uns aus, dass er nur nach “Kleinbuchstaben” suchen soll.

Dann enter, und schon startet der Bruteforce angriff. Sollte er das PW nun rausbekommen, siehts so aus wie auf meinem Screen @ Teil 1.

Dann zu EGB (Extreme GPU Bruteforcer) einfach auf der Herstellerseite runterladen und entpacken.

Hier haben wir eine groessere Auswahl an Verschluesselungstypen, doch in unserem Fall nehmen wir einfach die MD5.exe – in die MD5.txt kommen unsere MD5 Hashes rein (kann auch nur einer sein). In der MD5.ini ist unsere Konfigurationsdatei drin, die schauen wir uns kurz genauer an:

[Settings]
 
;*********************************************************************
; Minimal password length (0...15 characters), the default value is 3.
MinLength=3
 
;*********************************************************************
; Maximal password length (1...55 characters), the default value is 7.
MaxLength=8
 
;*********************************************************************
; Character set for attack, the default value is ?l?d.
;
; Standard character sets:
;	?d: 0123456789
;	?l: abcdefghijklmnopqrstuvwxyz
;	?u: ABCDEFGHIJKLMNOPQRSTUVWXYZ
;	?s: [email protected]#$%^&*()`~-_=+\|[]{};:'",.<>/?
CharacterSet=?d
 
;*********************************************************************
; Number of passwords to be processed by a thread (1...30000),
; the default value is 6000.
PasswordsInThread=6000
 
;*********************************************************************
; Number of stream processors (16...256), the default value is 96.
StreamProcessors=96
 
;*********************************************************************
; This parameter allows removing recovered hashes from the input file.
; On the "1" value, the recovered hashes will be deleted;
; on the "0" value (default) - the hashes will be preserved.
DeleteHashes=0
 
;*********************************************************************
; Last processed password.
LastPassword=
 
;*********************************************************************
; Current GPU device (1...256), the default value is 1.
CurrentDevice=1
 
;*********************************************************************
; Attack time (in minutes), default - 0 (unlimited attack time)
AttackTime=0
 
;*********************************************************************
; Custom character sets for the mask attack
CustomCharacterSet1=
CustomCharacterSet2=
CustomCharacterSet3=
CustomCharacterSet4=
CustomCharacterSet5=
CustomCharacterSet6=
CustomCharacterSet7=
CustomCharacterSet8=
CustomCharacterSet9=
CustomCharacterSetA=
CustomCharacterSetB=
CustomCharacterSetC=
CustomCharacterSetD=
CustomCharacterSetE=
CustomCharacterSetF=
 
;*********************************************************************
; Mask for the mask attack
Mask=

Hier sind fuer uns normal nur die 3 Werte wichtig:
MinLength=3
MaxLength=8
CharacterSet=?d

Ich benutz immer
MinLength=3
MaxLength=10
CharacterSet=?d?l

Wobei ihr natuerlich auch ganz andere Werte nehmen koennt.

Nun oeffnen wir unsere CMD. Entweder wechseln wir per cd ins richtige Verzeichnis und geben dann nur “MD5.exe MD5.txt” und enter ein, oder wir ziehen uns die MD5.exe (dann ein leerzeichen abstand) und MD5.txt in das CMD Fenster und druecken Enter.

Nun startet auch hier der Bruteforce Vorgang und erfolgreich gebrutete Hashes werden dann in der MD5.out (welche ihr mit nem beliebigen Text Editor oeffnen koennt) gespeichert.

Und joa, das wars im großen und ganzen, wollts nicht zu kompliziert irgendwie erlaeutern und mich nur kurz fassen (wurd aber doch laenger) ^^

Ade Rainbow Tables, Hallo GPU Brute-Force – Teil 2

Heyho,

da in meinem ersten Teil von “Ade Rainbow Tables, Hallo GPU Brute-Force” *klick* ein paar Tools vorgestellt wurden, leider einige auch fehlerhaft, kommt hier ein verbesserter kleiner Nachtrag 🙂

Zu BarsWF:

Vorteile:

+ schnellster Brute-Force in diesem Test (5sek fuer 7 stelliges PW aus Kleinbuchstaben)!
+ Kostenlos/Freeware
+ Auswahl von Charset waehlbar (”-c 0aA~ Set charset. 0 – digits, a – small chars, A – capitals, ~ – special symbols”, -c a = Kleinbuchstaben, welches ich auch verwendet hatte)
+ Nettes Design in der Console
+ “Brute-Forced” wird ueber volle CPU + GPU Power (kommt bei mir Nvidia GeForce GTX 280 & Intel Quadcore Q9550 auf ~ 950 MHash/sec)

Nachteile:

– Leider keine Saltet Hashes Brute-Force moeglich

Zu Lightning Hash Cracker:

Vorteile:
+ Kostenlos/Freeware
+ Speed ist ganz ordentlich

Nachteile:
+ Stoppt bei meinen Testlaeufen irgendwie wann es will, ohne den Hash zu entschluesseln (siehe Screenshot)
+ Usage ist auch ein bisschen kompliziert, im Vergleich zu den anderen Brute-Forcern

Zu etwas”neuem” bzw. was ich beim letzten Test nicht erwaehnt hatte, CuMD5:

Vorteile:
+ Kostenlos/Freeware
+ Man kann bei der Charsetauswahl angeben, welche Buchstaben waehrend des Brute-Force-Angriffes verwendet werden kann
+ Geschwindigkeit in Ordnung

Nachteile:
– Eventuell am Anfang etwas kompliziert die Sache mit den Charsets, da klein a nicht fuer reihen a-z zaehlen sondern fuer den buchstaben a – Habs versucht in dem Screen eventuell zu erklaeutern, wie ich das gemacht hab.
– “Herstellerseite” wirkt etwas unserioes

Und zu nvCUDA, welches ich letztes mal nicht testen konnte:

Und auch diesmal scheint es nicht richtig klappen, ich erhalte jedes mal einen “runtime error”, obwohl ich nun extra die cudart.dll runtergeladen und in den Ordner verschoben hatte  -.-

Ist mir aber nun egal.

Die Sieger stehen sowieso fest:

Platz 1 fuer normales MD5 Hash Cracking ist

BarsWF: http://3.14.by/en/md5

Zeigte “ueberragende” Leistung und war schnellster Bruteforcer im Test 🙂

Platz 1 fuer Salted Hashes ist:

Extreme GPU Bruteforcer: http://www.insidepro.com/eng/egb.shtml

Leider kostenpflichtig, jedoch lohnt es sich, wenn man vorallem viel mit Salted Hashes zu tun hat 😉

Joa, das wars soweit, eventuell folt nochn kleiner Teil 3, dieser wird aber nur kurz sein, und zeigen/beschreiben wie man die “Testsieger” richtig anwendet.

Mehr zum Thema GPU Bruteforce auch hier: *klick*

Webseite gegen XSS, SQLi’s, RFI und LFI schützen

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.

http://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

http://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:

http://url/index.php?site=main.php

Wir versuchen nun eine andere Datei zu includen.

http://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 Text-Tutorial von Lidloses_Auge 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:

http://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:

http://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:

http://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.

http://url/shop/product.php?id='[email protected]@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:

http://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.

http://url/shop/product.php?id='[email protected]@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 ^^
(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: http://free-hack.com/showthread.php?t=41091

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

How to fix Galerie 3.2 (pic) WBB Lite Addon Vulnerability

Heyho,

hab letztens wieder an meinen ersten Milw0rm Eintrag gedacht, und dann ist mir in den Kopf gekommen “Luecken finden ist die eine Sache, Luecken schließen die andere”. Daher werd ich euch einen Weg zeigen, wie man diese Luecke schließen kann.

Es geht um dieses Exploit.

Hier nochmal die SQL Injection:

http://www.site.com/galerie.php?action=show&pic=10'/**/and/**/ascii(substring((SELECT/**/password/**/from/**/bb1_users/**/WHERE/**/userid=1),1,1))>1/*

Nun sehen wir, dass die Luecke sich in der galerie.php befindet und anscheinend der parameter “pic” nicht ueberprueft wird bzw. auch Usereingaben “ausfuehrt” und dadurch laesst sich das MySQL Query beeinflussen.

Schauen wir uns mal die betroffene Zeile in der Galerie.php an, wir stoßen auf Zeile 683 & 684 und sehen:

$result = $db->query("UPDATE bb".$n."_galeriedata SET click=click+1 WHERE id = '$_GET[pic]' LIMIT 1"); 
$result_show = $db->query("SELECT g.*, u.userid, u.username FROM bb".$n."_galeriedata g LEFT JOIN bb".$n."_users u ON (g.userid=u.userid) WHERE id = '$_GET[pic]'");

Aha! Hier sieht man auch schoen, dass dort WHERE id = ‘$GET[pic]’ steht, daher haben wir bei unserer SQL Injection auch ein ‘ (hochkomma) gebraucht.

Nun wie schließen wir die Luecke? Ich werd intval() benutzen, warum? Weil intval() nur eine Zahl bzw. einen Integer zurueck gibt/erlaubt. Das heißt, wenn wir nun versuchen die Eingabe wieder zu manipulieren, dann passiert ganz einfach nichts, da er nur Zahlen “annimmt”. Und da wir in diesem Falle nur eine Zahl erwarten, passt intval ganz gut.

Wir suchen nun also in unserer galerie.php nach

$_GET[pic]

Dies sollte nur bei Zeile 683 & 684 auftauchen.

Dort ersetzen wir nun unser

$_GET[pic]

durch

".intval($_GET[pic])."

Dann sollten Zeile 683 & 684 etwa so aussehen:

$result = $db->query("UPDATE bb".$n."_galeriedata SET click=click+1 WHERE id = '".intval($_GET[pic])."' LIMIT 1"); 
$result_show = $db->query("SELECT g.*, u.userid, u.username FROM bb".$n."_galeriedata g LEFT JOIN bb".$n."_users u ON (g.userid=u.userid) WHERE id = '".intval($_GET[pic])."'");

Danach speichern wir das ganze, rufen unsere Galerie auf und probieren nochmal

http://www.site.com/galerie.php?action=show&pic=10'/**/and/**/ascii(substring((SELECT/**/password/**/from/**/bb1_users/**/WHERE/**/userid=1),1,1))>1/*

Was passiert? Richtig, kein MySQL Error, nichts. Die Luecke wurde gefixxt 🙂