SQL Injections

Hi,

heute bin ich wieder über etwas gestolpert, was ich nicht nachvollziehen kann:

?id=-54+union+select+concat_ws(0x3a,version(),database(),user()),2,3,4

Mir ist klar, daß diese Injection die MySQL Version den Datenbanknamen und den User ausgibt... aber warum?

Ich habe dieses Ding auf russischen Seiten gefunden, die eine Vielzahl an Internet Seiten gehackt haben und die Ergebnisse dort veröffentlichen...

Ich möchte diese Links nicht posten, da dort Passwörter etc aufgelistet worden sind und die Sicherheitslücken nach wie vor auf den meisten Seiten bestehen. Die Webmaster haben diese Lücken scheinbar noch nicht entdeckt.
 
Das sind die Daten, die man zum Raussuchen eines versionsspezifischen Exploits und zum durchführen eines möglichst klein gehaltenen Bruteforce mindestens braucht.
 
Hi,

heute bin ich wieder über etwas gestolpert, was ich nicht nachvollziehen kann:

?id=-54+union+select+concat_ws(0x3a,version(),database(),user()),2,3,4

Mir ist klar, daß diese Injection die MySQL Version den Datenbanknamen und den User ausgibt... aber warum?

Ich habe dieses Ding auf russischen Seiten gefunden, die eine Vielzahl an Internet Seiten gehackt haben und die Ergebnisse dort veröffentlichen...

Ich möchte diese Links nicht posten, da dort Passwörter etc aufgelistet worden sind und die Sicherheitslücken nach wie vor auf den meisten Seiten bestehen. Die Webmaster haben diese Lücken scheinbar noch nicht entdeckt.


Was genau möchtest du jetzt wissen? Wieso SQLi funktionieren?

?id=-54+union+select+concat_ws(0x3a,version(),database(),user()),2,3,4

Ich spalte mal die Abfrage auf

So würe sie normal ausschauen:?id=-54

Mit dem Befehl UNION kann man bei SQL abfragen verbinden+union+

danach kommt halt die Abfrage wenn dafür noch Felder für die Ausgabe Vorhanden sind.

select+concat_ws(0x3a,version(),database(),user()),2,3,4

Und so würde es im Quellcode ausschaue

Nur als Beispiel
$sql = "SELECT * FROM tabelle where id = '$_GET['id']'"

Und somit schließt er mit union noch eine Abfrage hinten an
 
Mich würden mögliche Gegenmaßnahmen interessieren, vielleicht direkt am obigen Beispiel.

Na ja...der Entwickler muss den Inhalt der per GET-Request übergebenen Variable "id" mit den Mitteln der von ihm benutzen Sprache "aufräumen".

In php z.B.

PHP:
$id = intval( $_GET['id'] );

if( $id > 0 ) { 
  $sql = "SELECT bar,foo FROM tabelle where id = " . $id;
} else {
  // ERROR_HANDLING...
}
Micha
 
  • Werte, wo Zahlen erwartet werden, explizit als int oder float (was eben erwartet wird) casten
  • Wahrheitswerte (boolean) ebenfalls explizit casten

IMHO sollte nicht versucht werden irgendwelchen ungültigen Userinput nachträglich zu validieren. Wenn da ungültige Daten reinkommen, dann sind diese zu verwerfen.
Das heißt, wird eine Zahl erwartet, und man bekomme einen String, dann sollte man nicht versuchen diesen in eine Zahl zu casten.
In PHP gibts dafür z.B. is_numeric() und Strings kann man mit Regulären Ausdrücken über z.B. preg_match() prüfen. Ein mysql_real_escape_string() sollte natürlich trotzdem nicht fehlen
 
In PHP gibts dafür z.B. is_numeric() und Strings kann man mit Regulären Ausdrücken über z.B. preg_match() prüfen.

Das ist IMO zum Teil Geschmackssache und hängt zum anderen Teil vom Einsatzgebiet ab.
Wenn es eine ehr unwichtige numerische Eingabe ist, bei der ein "0" im Zweifelsfall keinen Schaden anrichtet, dann brauche ich ja nicht unbedingt zusätzliche Rechenleistung für Fehler-Routinen beanspruchen.
Beispiel: Wenn eine Bildergallerie bei manipuliertem Start-Bild-Wert eben als Wert 'ne 0 annimmt und somit die erste Seite der Gallerie anzeigt, dann ist das ja nicht weiter tragisch.

Wenn die Zahl natürlich 'nen wichtigen Einfluss hat und nicht vernachlässigt werden darf (meinetwegen 'ne Postleitzahl bei der Adress-Eingabe), dann hast du Recht - da sollte man abfragen und gegebenen Falls 'ne Fehlermeldung bringen.
 
Eine GET-Variable ist immer vom Typ String.

wenn der String jedoch nur aus Zahlen besteht, gibt is_numeric() trotzdem true zurück und somit das von Thunderb0lt gewünschte Ergebnis.

PHP:
<?php
function getinfo($num)
{
    echo 'Typ         : ' . gettype($num) . chr(10);
    echo 'numeric     : ' . (is_numeric($num)?'ja':'nein') . chr(10);
    echo 'cast to int : ' . (int)$num . chr(10) . chr(10);
}
getinfo('123');
getinfo(123);
getinfo('12a3b');
getinfo(null);

?>

Code:
martin@lenotux:~$ php test.php 
Typ         : string
numeric     : ja
cast to int : 123

Typ         : integer
numeric     : ja
cast to int : 123

Typ         : string
numeric     : nein
cast to int : 12

Typ         : NULL
numeric     : nein
cast to int : 0

martin@lenotux:~$

ich persönlich handhabe das meist so:
Wenn ich irgendwelche Model-Klassen programmiere, wird dort (mit wenigen Ausnahmen) hart gecastet - eine Klasse soll schließlich Funktionalität darstellen und nicht Dummheit oder Böswilligkeit des Users anmeckern.
Wenn eine Fehler-Behandlung stattfinden soll, dann werden die Daten, bevor sie an das Model gegeben werden, im Controller validiert und dort wird dann für die Ausgabe einer Fehlermeldung oder dergleichen gesorgt.
 
und somit das von Thunderb0lt gewünschte Ergebnis.
Ja, ich habe nichts anderes behauptet... ;)

Ich persönlich bin kein Freund von is_numeric. Das akzeptiert mir zu viel. Ich will z.B. keine Hex Values in meinen Querys. Und sowas auch nicht.

PHP:
<?php

include("db.inc");

if(is_numeric($_GET['id'])) {
  db_query("SELECT id FROM results WHERE id = " . $_GET['id']);
}  
?>
Code:
mime@kira:~ %  GET http://localhost/x.php?id=123e4567
1367 - Illegal double '123e4567' value found during parsing -- SELECT id FROM results WHERE id = 123e4567
Das ist doch Mist...

Micha
 
Ich persönlich bin kein Freund von is_numeric. Das akzeptiert mir zu viel. Ich will z.B. keine Hex Values in meinen Querys.

Ein interessanter Punkt, den ich zugegebenermaßen bisher gar nicht auf meiner Liste hatte. Danke für den Hinweis!
 
Bei Usereingaben überprüfe ich min. und max. Länge, sowie Definitionsbereich (zulässige Zeichen) und Zeichenmuster via regular Expression. Hierzu schrieb ich folgende Funktion:
PHP:
/*
function check_user_input(): validation of user input by length check and regex-check

$value:     user input to be checked
$len_min:   allowed min. length
$len_max:   allowed max. length
$regex:     allowed regex. pattern for $value

return values:
-1 : $value too short
+1 : $value too long
+2 : $value doesn't match regex pattern
 0 : $value is O.K.

*/

function check_user_input($value, $len_min, $len_max, $regex) {
  $len=strlen($value);
  if ($len < $len_min) return -1;
  if ($len > $len_max) return  1;
  return (preg_match($regex, $value)) ? 0 : 2;
}
Gibt es Einwände? :)
 
Da musst du aber eine Menge oder sehr komplexe Regexen durchgehen, bevor du alle möglichen SQL-Injections ausschliessen kannst ohne valide Inputs zu zerstören. Nimm z.B. einen Blog-Beitrag über SQL-Injections. Den würdest du mit dieser Vorgehensweise unleserlich machen.

Da sollte man dann doch eher die Tipps von PHP: SQL Injection - Manual befolgen und auf Funktionen wie mysql_real_escape_string() zurückgreifen. Wenn der Input irgendwo wieder ausgegeben wird, kann man auch mit htmlentities() gleich eine Umwandlung von Sonderzeichen in HTML-Code vornehmen. Ausserdem natürlich eine Typenprüfung vornehmen, wenn es um reine Integer-Werte geht, wie im eingangs erwähnten Beispiel. 'id' soll da ja offenbar eh eine Zahl sein, also sollte man auch prüfen ob 'id' wirklich eine Zahl ist. Ein String wie '-54+union+select+concat_ws(0x3a,version(),database( ),user()),2,3,4' würde dann gar nicht erst an die DB weitergereicht, weil es eindeutig keine ist.
 
Da musst du aber eine Menge oder sehr komplexe Regexen durchgehen, bevor du alle möglichen SQL-Injections ausschliessen kannst ohne valide Inputs zu zerstören. Nimm z.B. einen Blog-Beitrag über SQL-Injections. Den würdest du mit dieser Vorgehensweise unleserlich machen.

Für Blog-Einträge und komplexere Unterfangen ist die Funktion nicht vorgesehen. Ich verwende sie lediglich im Kontext simpler Usereingaben in Editfeldern (z.B. Usernamen, Passwort, etc.) um zu prüfen, ob die eingegebenen Daten dem für ein Feld gültigem Zeichenvorrat unterliegen.

In einer meiner, ich nenne es mal "Chat-Anwendungen", müssen User einen eindeutigen Nicknamen eingeben, der zwischen 3 und 15 Zeichen und alphanumerisch sein muss. Das überprüfe ich unter Verwendung o.g. Funktion dann wiefolgt:

PHP:
// user's nickname
  case "nickname": switch (check_user_input($value, 3, 15, "/^[a-zA-Z0-9_-]+$/")) {
                          case -1 : echo ("- Nickname too short\n<br>"); $create_db=false; break;
                          case  1 : echo ("- Nickname too long\n<br>");  $create_db=false; break;
                          case  2 : echo ("- Nickname invalid\n<br>");   $create_db=false; break;
                   }
                   break;
Übersehe ich in o.g. Beispiel mögliche Schwachstellen, die ein Angreifer ausnutzen könnte?
 
Zurück
Oben