Wij zijn |

SQL Injectie – Wat is het en hoe werkt het?

Een van de kwetsbaarheden waar we tijdens een webapplicatie pentest onderzoek naar doen is SQL injectie. Zoals de naam al doet vermoeden proberen we onze input te injecteren in de SQL query die wordt uitgevoerd. Kun je je voorstellen wat een hacker allemaal kan aanrichten en inzien als de queries naar wens kunnen worden aangepast?

De achtergrond

Bij veel ondernemingen zijn databasesystemen niet weg te denken. Banken slaan alle rekeningnummers met bijbehorende betaaltransacties op en de grote webshops hebben databases vol met productinformatie. De meeste webapplicaties hebben een verbinding met de achterliggende database, om zo gegevens te kunnen tonen of aan te passen.

Deze databases kunnen worden bevraagd aan de hand van een zogeheten SQL query. Als gebruikersinvoer direct in zo’n query gebruikt wordt, kan er mogen code in de query worden geïnjecteerd. Dit heet een SQL injectie.

De eerste sporen naar de kwetsbaarheid SQL injectie dateren al uit 1998. Cybersecurity researcher Jeff Forristal meldde het fenomeen bij Microsoft. Microsoft antwoordde dat ze het niet als probleem zien. Sinds die tijd zijn er ontelbare gegevens buitgemaakt door het misbruiken van SQL injecties op onder andere websites.

Hoe werkt een SQL injectie?

In onderstaande afbeelding leggen we uit hoe een SQL injectie werkt. Natuurlijk zijn er verschillende soorten databases (MySQL, Oracle, MSSQL, PostgreSQL, etc) en werkt de syntax overal net iets anders, maar de basis is hetzelfde en werkt als volgt:

SQL injectie uitgelegd

In dit (onveilige) voorbeeld logt Alice in met haar gebruikersnaam en wachtwoord. Deze worden respectievelijk geplaatst bij de waarde van name en pass. Hierdoor wordt de uiteindelijke query:

SELECT * FROM `users` WHERE username = 'Alice' AND pass = 'IlikeBob!';

Indien het wachtwoord van Alice inderdaad Ilikebob! is, geeft de SQL query data terug. Als het een fout wachtwoord is zal er geen data terugkomen.

Een aanvaller probeert vervolgens in te loggen met de gebruikersnaam Admin. Als wachtwoord geeft hij op: ‘ or 1=1– –. Hierdoor ziet de SQL query er zo uit:

SELECT * FROM `users` WHERE username = 'Admin' AND pass = '' or 1=1-- -';
Dankzij de kleurtjes in de query is goed te zien wat er nu gebeurt. De single quote sluit de string en het woord OR geeft aan dat ofwel het voorgaande statement TRUE moet zijn, ofwel het statement erna. Het deel 1=1 is natuurlijk altijd TRUE, immers, 1 is gelijk aan 1. De streepjes — – geven aan dat de regel vanaf daar uitgecomment wordt, ongeacht wat er verder nog voor clausules achter staan.

Het maakt nu dus niet uit wat voor wachtwoord er wordt opgegeven want 1 is altijd gelijk aan 1. Het gehele statement is nu dus waar en geeft dezelfde data terug als bij een goed wachtwoord.

Soorten SQL injecties

Er zijn verschillende soorten SQL-injecties, elk met hun eigen kenmerken en methoden van exploitatie. Hieronder worden enkele van de meest voorkomende soorten uitgelegd.

Klassieke SQL Injectie

Een klassieke SQL-injectie, ook wel bekend als in-band SQL-injectie, is een type aanval waarbij de aanvaller de resultaten van hun kwaadaardige query direct op de webpagina kan zien. Dit kan bijvoorbeeld gebeuren wanneer een webapplicatie de resultaten van een SQL-query direct op de pagina weergeeft.

Stel dat we een webapplicatie hebben met een zoekfunctie. De applicatie neemt de zoekterm van de gebruiker, plaatst deze in een SQL-query en toont de resultaten op de pagina. De code zou er ongeveer zo uit kunnen zien:

$searchTerm = $_GET['search'];
$query = "SELECT * FROM products WHERE name LIKE '%$searchTerm%'";
$result = mysqli_query($conn, $query);
 
while($row = mysqli_fetch_assoc($result)) {
    echo "Product: " . $row['name'] . "<br>";
}

Een aanvaller kan nu een SQL-injectie uitvoeren door een zoekterm in te voeren die extra SQL-code bevat. Bijvoorbeeld:

search=%'; SELECT * FROM users; --

De resulterende query zou er dan zo uitzien:

SELECT * FROM products WHERE name LIKE '%%'; SELECT * FROM users; --%'

Deze query zou eerst proberen om producten te vinden die overeenkomen met een lege zoekterm (wat waarschijnlijk alle producten zou opleveren), en vervolgens zou het alle gegevens uit de ‘users’ tabel selecteren. Als de resultaten van de query direct op de pagina worden weergegeven, zou de aanvaller in staat zijn om alle gebruikersinformatie te zien.

Blind SQL injection

Bij een blind SQL-injectie, krijgt de aanvaller geen nuttige foutmelding of output van de query. Deze vorm van SQL-injectie vermoeilijkt het succesvol uitbuiten van de kwetsbaarheid, maar maakt het niet onmogelijk. Zo kan bijvoorbeeld het gedrag van de webserver worden geobserveerd bij het invoeren van waar/niet-waar queries (zie boolean-based SQL injectie). 

Error-based SQL injectie

Error-based SQL-injectie is een techniek waarbij een aanvaller speciaal ontworpen queries gebruikt die fouten veroorzaken in de SQL-interpreter. De foutberichten die worden teruggestuurd, bevatten vaak gevoelige informatie zoals tabelnamen, structuur van de database, of zelfs data uit de database.

Laten we een voorbeeld nemen van een webapplicatie die een gebruikers-ID uit de URL haalt en deze gebruikt in een SQL-query:

$id = $_GET['id'];
$query = "SELECT * FROM users WHERE id = $id";
$result = mysqli_query($conn, $query);

Een aanvaller kan proberen om een SQL-injectie uit te voeren door een ongeldige waarde voor ‘id’ in te voeren die een SQL-fout veroorzaakt. Bijvoorbeeld:

id=1 UNION SELECT 1,@@version -- 

De resulterende query zou er dan zo uitzien:

SELECT * FROM users WHERE id = 1 UNION SELECT 1,@@version -- 

In dit geval probeert de aanvaller de versie van de MySQL-server te achterhalen. De @@version is een systeemvariabele in MySQL die de versie van de server bevat. Als de webapplicatie de SQL-fouten direct op de pagina weergeeft, zou de aanvaller in staat zijn om de versie van de MySQL-server te zien.

Boolean-based blind SQL injectie

Bij een boolean-based blind SQL-injectie, stuurt de aanvaller specifieke queries naar de database die resulteren in een waar of niet-waar resultaat, afhankelijk van de antwoorden van de database. Dit kan de aanvaller helpen om de structuur van de database te begrijpen en uiteindelijk toegang te krijgen tot de gegevens.

SELECT * FROM users WHERE username = 'admin' AND SUBSTRING(password, 1, 1) = 'a';

In dit voorbeeld probeert de aanvaller te raden of de eerste letter van het wachtwoord ‘a’ is. Afhankelijk van de reactie van de webpagina, kan de aanvaller de rest van het wachtwoord raden door steeds een letter op te schuiven na een antwoord dat het waar is.

Elk van deze soorten SQL-injecties heeft zijn eigen kenmerken en methoden van exploitatie, maar ze hebben allemaal hetzelfde doel: het manipuleren van de database door het injecteren van kwaadaardige SQL-code. Het is daarom essentieel om uw webapplicaties te beschermen tegen deze en andere soorten SQL-injecties.

SQL injectie uitbuiten

Hierboven heb je kunnen lezen dat iedere vorm van SQL injectie een andere techniek vereist. Naast deze verschillende vormen zitten er ook nog verschillen in de techniek afhankelijk van het doel.

Authentication bypass

Wil je een inlogpagina omzeilen zonder echt geldige inloggegevens te hebben? Dan is je doel Authentication Bypass. Dit hebben we als allereerst voorgedaan, door or 1=1 -- - te injecteren, waardoor de clausule altijd waar is.

Data manipuleren

Naast het omzeilen van de inlogfunctie kan een aanvaller ook proberen om een nieuwe gebruiker toe te voegen, eventueel zelfs met beheerdersrechten.

INSERT INTO users (username, password, role) VALUES ('hacker', 'password', 'admin');

Data dumpen

Tot slot misschien wel het meest gebruikte doel. Het dumpen van (alle) data die aanwezig is in de database. Denk bijvoorbeeld aan de hele tabel users, waardoor een aanvaller alle usernames en wachtwoorden kan buitmaken.

Geautomatiseerd uitbuiten SQL injectie

Ongeacht het doel van de aanvaller is het succesvol uitbuiten van een SQL injectie vaak veel werk. Je probeert iets en controleert of het de gewenste output geeft. Uiteindelijk kom je er wel, maar het is veel trial and error. Natuurlijk bestaan er tools om geïdentificeerde SQL injecties automatisch uit te buiten. De bekendste tool hiervoor is sqlmap en staat standaard geïnstalleerd op Kali Linux. Gebruik de tool alleen als je toestemming hebt en/of gevrijwaard bent om een webapplicatie aan te vallen. In een latere blogpost zullen we verder inzoomen op het gebruik van sqlmap.

Sqlmap probeert verschillende standaard SQL injectie payloads op alle parameters. Aan de hand van het response bepaalt de tool of de website met achterliggende database al dan niet kwetsbaar zijn voor SQL injectie. Natuurlijk zijn er ontzettend veel verschillende flags en functionaliteiten, maar de basis werkt zoals onderstaand.

sqlmap -u “https://pentests.nl/login?username=admin&password=admin”

┌──(kali㉿kali)-[~]
└─$ sqlmap -u "https://pentests.nl/login?username=admin&password=admin"
         ___
        __H__
  ___ ___[.]_____ ___ ___  {1.6.4#stable}
 |_ -| . [.]     | .'| . |
 |___|_  [']_|_|_|__,|  _|
       |_|V...       |_|   https://sqlmap.org  [!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program
 
 [*] starting @ 09:07:22 /2023-07-19/
 
[09:07:23] [INFO] testing connection to the target URL
[09:07:23] [INFO] checking if the target is protected by some kind of WAF/IPS
[09:07:23] [INFO] testing if the target URL content is stable
[09:07:36] [INFO] testing if GET parameter 'username' is dynamic
[09:07:36] [WARNING] GET parameter 'username' does not appear to be dynamic
[09:07:36] [WARNING] heuristic (basic) test shows that GET parameter 'username' might not be injectable
[09:07:36] [INFO] testing for SQL injection on GET parameter 'username'
[09:07:36] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[09:07:38] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[09:07:38] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[09:07:39] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[09:07:39] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[09:07:40] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[09:07:40] [INFO] testing 'Generic inline queries'
[09:07:40] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[09:07:41] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[09:07:41] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[09:07:41] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[09:07:42] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[09:07:42] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind (IF)'
[09:07:43] [INFO] testing 'Oracle AND time-based blind' 

De tool vraagt een aantal keer om een keuze te maken. De tool herkent waar de SQL injectie te vinden is en wat voor soort injectie het is. Er zijn verschillende soorten, zoals onderstaand wordt uitgelegd. Nadat het injectiepunt gevonden is is mogelijk om de hele database uit te lezen. Bijvoorbeeld met de flag –dump-all.

SQL injectie voorkomen

Het voorkomen van SQL-injectie is een essentieel onderdeel van de beveiliging van elke webapplicatie. De enige manier om SQL injecties te voorkomen is door geen gebruikersinvoer direct en ongefilterd in SQL queries te gebruiken. Natuurlijk moeten delen van de inhoud van de gebruikersinvoer zo nu en dan toch worden opgeslagen, hoe doe je dit op een veilige manier? Hier zijn enkele van de meest effectieve methoden om SQL-injectie te voorkomen:

Gebruik van Prepared Statements

Een van de meest effectieve manieren om SQL-injectie te voorkomen is door het gebruik van prepared statements. Met prepared statements, wordt de SQL-code gescheiden van de data. Dit betekent dat een aanvaller geen kwaadaardige SQL-code kan injecteren, omdat de data niet als code wordt geïnterpreteerd. Hier is een voorbeeld van een prepared statement in PHP:

$stmt = $pdo->prepare('SELECT * FROM users WHERE email = ? AND status=?');
$stmt->execute([$email, $status]);
$user = $stmt->fetch();

Gebruik van Stored Procedures

Stored procedures zijn vergelijkbaar met prepared statements in die zin dat ze de SQL-code scheiden van de data. Echter, stored procedures worden opgeslagen in de database zelf en kunnen worden aangeroepen vanuit de applicatie.

Input Validatie

Het is belangrijk om alle gebruikersinvoer te valideren voordat deze wordt gebruikt in een SQL-query. Dit betekent dat je moet controleren of de invoer het verwachte formaat heeft, en onverwachte of potentieel kwaadaardige waarden moet afwijzen.

Least Privilege

Elke verbinding met de database moet zo min mogelijk rechten hebben. Dit betekent dat als een aanvaller erin slaagt SQL-code te injecteren, de schade die ze kunnen aanrichten beperkt is.

Door deze methoden te implementeren, kunt u uw webapplicaties effectief beschermen tegen SQL-injectie aanvallen. Het is belangrijk om te onthouden dat geen enkele methode 100% effectief is, en dat het beste verdedigingsmechanisme een combinatie van verschillende methoden is.

Hoe kunnen wij u helpen?

QR Codes: Het onverwachte wapen in Device Code Phishing

Device code phishing, net als aanvallen via Adversary-in-the-middle (AiTM), vertegenwoordigt een geavanceerde vorm van cyberdreiging die zich onderscheidt van traditionele phishing. Device code phishing exploiteert de ‘OAuth2 Device Authorization Grant flow‘ van Microsoft Azure, die gebruikers in staat stelt zich aan te melden bij apparaten met beperkte invoermogelijkheden.

read more

CORS: het belang van Cross-Origin Resource Sharing

Bij onze klanten zien we een toenemende implementatie van Cross-Origin Resource Sharing (CORS). Helaas constateren we ook een stijging in het aantal onveilig geconfigureerde CORS-implementaties. In deze blog duiken we dieper in wat CORS is, de meest voorkomende misconfiguraties en hun potentiële risico’s, en hoe je sterke CORS-regels kunt instellen om je webapplicaties te beschermen.

read more