Soorten SQL Injections

Op deze pagina worden de volgende soorten SQL-injections uitgelegd:

Comments

Net als bij veel andere programmeertalen is het ook bij SQL mogelijk om commentaar in de query mee te geven. Dit commentaar wordt niet uitgevoerd en dient meestal slechts ter verduidelijking van de query. Echter is het mogelijk om een zogeheten line comment te gebruiken als manipulatie van een parameter. Op deze manier kan ervoor worden gezorgd dat een deel van de query wordt genegeerd. Een line comment wordt aangegeven met '--.

We nemen het volgende stukje PHP code dat gebruikt maakt van geposte waardes uit een formulier.

$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM members WHERE username = '{$username}' AND password = '{$password}'"

Door het simpelweg posten van admin'-- als gebruikersnaam kan ervoor gezorgd worden dat het tweede deel van de query genegeerd wordt. Het belangrijkste deel van een inlogsysteem wordt dus buiten spel gezet: het wachtwoord. De gebruiker kan dus inloggen als een 'admin', zonder hiervoor een wachtwoord te hoeven weten.

SELECT * FROM members WHERE username = 'admin'--' AND password = '{$password}'

Incorrectly filtered escape characters

Deze SQL-injectie bestaat uit het manipuleren van een query om zo acties met de database te kunnen uitvoeren die normaal niet toegestaan zijn. We nemen het volgende voorbeeld.

$username = $_POST['username'];
$query = "SELECT * FROM users WHERE name = '{$username}'";

Deze query kunnen we manipuleren door de waaarde a' or 't' ='t als gebruikersnaam te posten. Het volgende voegen we toe.

SELECT * FROM users WHERE name = 'a' OR 't'='t';

Het raden van een naam is in dit geval niet meer nodig, want de OR met de vergelijking 't' = 't' slaagt sowieso en op deze manier is de query dus omzeild. In dit voorbeeld hebben we de naam omzeild, maar dit kan dus ook goed werken voor het omzeilen van een wachtwoord.

Maar als we op deze manier een vrij simpel OR statement kunnen toevoegen, is het dan ook mogelijk om een hele tabel te verwijderen? In principe wel: a'; DROP TABLE users;--

Hierdoor zal de tabel voor gebruikers verwijderd worden. De query ziet er dan als volgt uit.

SELECT * FROM users WHERE name = 'a';DROP TABLE users--';

Incorrect type handling

Bij dit type SQL injectie wordt er niet gecontroleerd of de ingegeven waarde wel van het juiste type is. Als er bijvoorbeeld een numerieke waarde wordt verwacht, dan is het verstandig om te controleren of de ingegeven waarde ook daadwerkelijk numeriek is. Als dit wordt nagelaten dan kunnen kwaadwillende de query zodanig manipuleren dat ongewenste gegevens in de database worden aangepast of worden verwijderd. Genomen het volgende voorbeeld.

$id = $_GET['id'];
$query = "SELECT * FROM data WHERE id = {$id}"

We verwachten dat $id een numerieke waarde bevat, maar dit controleren we nergens. We zouden dus in principe 1; DROP TABLE users kunnen opgeven als id. Omdat er nergens een goede controle is zal de applicatie gewoon verder uitgevoerd worden, maar de tabel users is dan wel verdwenen.

SELECT * FROM DATA WHERE id=1;DROP TABLE users;

Blind injection

Een blind sql-injection is identiek aan een gewonen sql-injection echter wordt er op deze manier geprobeerd om bruikbare foutmelding te achterhalen. Er worden een aantal true/false statements meegegeven in de injectie waarmee een foutmelding gegenereerd wordt. voorbeeld:

SELECT naam FROM TEST1 WHERE naam = 'thijs' AND 1=1;

In de code hierboven wordt het SQL statement correct uitgevoerd en zal de data gewoon in de applicatie te zien zijn. Terwijl in de code hieronder geen resultaat geneerd. Er zullen een aantal foutmelding op het scherm weergegeven worden.

SELECT naam FROM TEST1 WHERE naam = 'thijs' AND 1=2;

Union Injections

Dit deel over UNION Injections wordt herzien. Wijzigingen worden verwacht in de eerste helft van 2018.

Door middel van UNION is het mogelijk om de resultaten van twee tabellen samen te voegen. Het is noodzakelijk dat in beide queries hetzelfde aantal kolommen worden geselecteerd. De reden hiervoor is dat de resultaten uit de tweede tabel onder de kolomnamen van de eerste tabel komen te staan.

SELECT id, titel, naam FROM tabel1 UNION ALL SELECT id, tekst, opmerking FROM tabel2;

De velden id, tekst en opmerking uit tabel 2 worden in het resultaat geplaatst onder de velden id, titel en naam. De typen uit beide tabellen moeten overeen zijn. Als het veld ID een integer is dan dient het eerste veld uit de tweede tabel ook van dat type te zijn.

Het risico van SQL injections is de UNION injection. Deze injection is eenvoudig te gebruiken vanuit de adresbalk. Door middel van UNION te gebruiken kunnen er gegevens opgehaald die niet voor de gebruiker bestemd zijn.

In onderstaand voorbeeld wordt aan de hand van de URL een UNION injection uitgevoerd:

?id=12349999' UNION ALL SELECT 1,2,3,4,5,6,7 FROM [tabelnaam]--

Wat gebeurd er in deze request: ?id=12349999' Er wordt een niet bestaand id geraden, zodat er uit de eerste tabel geen data wordt getoond. Dit ID wordt afgesloten zodat UNION in dezelfde query komt.
UNION ALL SELECT Er wordt een nieuw verzoek naar een tabel ingevoegd.
FROM [tabelnaam] [tabelnaam] kan naar eigen inzicht worden gegokt.
-- De rest van de eventuele query wordt uitgecommentarieerd.
De query die wordt uitgevoerd ziet er als volgt uit:

SELECT * FROM tabel UNION ALL SELECT 1,2,3,4,5,6,7 FROM [tabelnaam]

Echter is het nadelig dat de tabelnaam gegokt moet worden. Hiervoor is er de volgende mogelijkheid, waarmee het mogelijk is om tabelnamen te achterhalen

?id=12349999' UNION ALL SELECT 1,TABLE_NAME,3,TABLE_ROWS,5 FROM information_schema.tables  --

Deze query vraagt vraagt een overzicht op van alle tabellen uit de database information_schema. In deze database wordt de structuur van alle databases, tabellen en dergelijke opgeslagen. In feite is het weten van alle tabelnamen zinloos, want niet alles is direct te benaderen vanuit de applicatie. Om achter de daadwerkelijke database naam te komen kan het volgende gedaan worden:

?id=12349999' union all select 1,2,3,4,5,6,7 from tabelnaamdienietbestaat --

Als de foutweergave is ingeschakeld dan komt de volgende fout in beeld:

1146: Table 'databasenaam.tabelnaamdienietbestaat' doesn't exist

Nu de databasenaam bekend is wordt het mogelijk om daadwerkelijk tabelnamen op de vragen.

?id=12349999'
UNION ALL
    SELECT 1,TABLE_NAME,3,TABLE_ROWS,5,6,7
    FROM information_schema.tables
    WHERE TABLE_SCHEMA='databasenaam'
    LIMIT 1,1  --

De limit staat er in, omdat er altijd maar één naam gegeven zal worden. Deze kan in de url verhoogd en verlaagd worden om de juiste tabelnaam te hanteren. Als de tabelnaam bekend is, dan wordt het mogelijk om de kolommen op te vragen:

?id=12349999'
UNION ALL
    SELECT 1,COLUMN_NAME,3,PRIVILEGES,5,6,7
    FROM information_schema.columns
    WHERE TABLE_SCHEMA= 'databasenaam' AND TABLE_NAME='tabelnaam'
    LIMIT 1,1--

Als de kolommen bekend zijn dan kan hieruit de data worden getoond:

?id=12349999' union all select 1,user,3,password,5,6,7 from members LIMIT 1,1--

Op deze manier kan data worden verkregen die niet is bedoeld om door anderen gebruikt te worden. Waardoor het mogelijk wordt om bijvoorbeeld cookies over te nemen.